GP-134: Mainline changes cherry-picked from Debugger branch

This commit is contained in:
ghidravore 2020-09-16 13:20:45 -04:00
parent 192aba987a
commit 724df5a44c
60 changed files with 3855 additions and 1770 deletions

View file

@ -21,10 +21,10 @@ import db.Record;
import ghidra.util.Lock;
/**
* Base class for an cached object in the database. Database objects have keys. They are marked
* as invalid when a database cache is cleared and can be revived on a refresh as long
* as they haven't been deleted. Instantiating an object will cause it to be added
* immediately to the associated cache.
* Base class for an cached object in the database. Database objects have keys. They are marked as
* invalid when a database cache is cleared and can be revived on a refresh as long as they haven't
* been deleted. Instantiating an object will cause it to be added immediately to the associated
* cache.
*/
abstract public class DatabaseObject {
@ -36,6 +36,7 @@ abstract public class DatabaseObject {
/**
* Constructs a new DatabaseObject and adds it to the specified cache.
*
* @param cache to be used for this object or null if object will not be cached
* @param key database key to uniquely identify this object
*/
@ -64,10 +65,11 @@ abstract public class DatabaseObject {
}
/**
* Returns true if this object has been deleted. Note: once an object has been deleted,
* it will never be "refreshed". For example, if an object is ever deleted and is
* resurrected via an "undo", you will have get a fresh instance of the object.
* @return true if this object has been deleted.
* Returns true if this object has been deleted. Note: once an object has been deleted, it will
* never be "refreshed". For example, if an object is ever deleted and is resurrected via an
* "undo", you will have get a fresh instance of the object.
*
* @return true if this object has been deleted.
*/
public boolean isDeleted() {
return deleted;
@ -75,8 +77,8 @@ abstract public class DatabaseObject {
/**
*
* Invalidate this object. This does not necessarily mean that this object can
* never be used again. If the object can refresh itself, it may still be useable.
* Invalidate this object. This does not necessarily mean that this object can never be used
* again. If the object can refresh itself, it may still be useable.
*/
public void setInvalid() {
invalidateCount = getCurrentValidationCount() - 1;
@ -99,8 +101,9 @@ abstract public class DatabaseObject {
}
/**
* Returns true if object is currently invalid. Calling checkIsValid may
* successfully refresh object making it valid.
* Returns true if object is currently invalid. Calling checkIsValid may successfully refresh
* object making it valid.
*
* @see #checkIsValid()
*/
public boolean isInvalid() {
@ -108,8 +111,8 @@ abstract public class DatabaseObject {
}
/**
* Checks if this object has been deleted, in which case any use of the object is
* not allowed.
* Checks if this object has been deleted, in which case any use of the object is not allowed.
*
* @throws ConcurrentModificationException if the object has been deleted from the database.
*/
public void checkDeleted() {
@ -119,8 +122,9 @@ abstract public class DatabaseObject {
}
/**
* Check whether this object is still valid. If the object is invalid, the object will
* attempt to refresh itself. If the refresh fails, the object will be marked as deleted.
* Check whether this object is still valid. If the object is invalid, the object will attempt
* to refresh itself. If the refresh fails, the object will be marked as deleted.
*
* @return true if the object is valid.
*/
public boolean checkIsValid() {
@ -128,10 +132,11 @@ abstract public class DatabaseObject {
}
/**
* Check whether this object is still valid. If the object is invalid, the object will
* attempt to refresh itself using the specified record. If the refresh fails, the
* object will be marked as deleted and removed from cache. If this object is already
* marked as deleted, the record can not be used to refresh the object.
* Check whether this object is still valid. If the object is invalid, the object will attempt
* to refresh itself using the specified record. If the refresh fails, the object will be marked
* as deleted and removed from cache. If this object is already marked as deleted, the record
* can not be used to refresh the object.
*
* @param record optional record which may be used to refresh invalid object
* @return true if the object is valid.
*/
@ -153,8 +158,9 @@ abstract public class DatabaseObject {
}
/**
* This method provides a cheap (lock free) way to test if an object is valid. If
* this object is invalid, then the lock will be used to refresh as needed.
* This method provides a cheap (lock free) way to test if an object is valid. If this object is
* invalid, then the lock will be used to refresh as needed.
*
* @param lock the lock that will be used if the object needs to be refreshed.
* @return true if object is valid, else false
*/
@ -173,24 +179,25 @@ abstract public class DatabaseObject {
/**
* Tells the object to refresh its state from the database.
* @return true if the object was able to refresh itself. Return false if the object
* was deleted. Objects that extend this class must implement a refresh method. If
* an object can never refresh itself, then it should always return false.
*
* @return true if the object was able to refresh itself. Return false if the object was
* deleted. Objects that extend this class must implement a refresh method. If an object
* can never refresh itself, then it should always return false.
*/
protected abstract boolean refresh();
/**
* Tells the object to refresh its state from the database using the specified
* record if not null. NOTE: The default implementation ignores the record
* and invokes refresh(). Implementations of this method must take care if
* multiple database tables are used since the record supplied could correspond
* to another object. In some cases it may be best not to override this method
* or ignore the record provided.
* Tells the object to refresh its state from the database using the specified record if not
* null. NOTE: The default implementation ignores the record and invokes refresh().
* Implementations of this method must take care if multiple database tables are used since the
* record supplied could correspond to another object. In some cases it may be best not to
* override this method or ignore the record provided.
*
* @param record valid record associated with object's key (optional, may be null to force
* record lookup or other refresh technique)
* @return true if the object was able to refresh itself. Return false if record is null
* and object was deleted. Objects that extend this class must implement a refresh method.
* If an object can never refresh itself, then it should always return false.
* record lookup or other refresh technique)
* @return true if the object was able to refresh itself. Return false if record is null and
* object was deleted. Objects that extend this class must implement a refresh method.
* If an object can never refresh itself, then it should always return false.
*/
protected boolean refresh(Record record) {
return refresh();

View file

@ -141,11 +141,13 @@ public class DataTypeUtilities {
/**
* Check to see if the second data type is the same as the first data type or is part of it.
* <br>Note: pointers to the second data type are references and therefore are not considered
* to be part of the first and won't cause true to be returned. If you pass a pointer to this
* method for the first or second parameter, it will return false.
* @param firstDataType the data type whose components or base type should be checked to see
* if the second data type is part of it.
* <br>
* Note: pointers to the second data type are references and therefore are not considered to be
* part of the first and won't cause true to be returned. If you pass a pointer to this method
* for the first or second parameter, it will return false.
*
* @param firstDataType the data type whose components or base type should be checked to see if
* the second data type is part of it.
* @param secondDataType the data type to be checked for in the first data type.
* @return true if the second data type is the first data type or is part of it.
*/
@ -184,7 +186,8 @@ public class DataTypeUtilities {
/**
* Returns true if the two dataTypes have the same sourceArchive and the same UniversalID
* @param dataType1 first data type
*
* @param dataType1 first data type
* @param dataType2 second data type
* @return true if types correspond to the same type from a source archive
*/
@ -207,13 +210,14 @@ public class DataTypeUtilities {
}
/**
* Returns true if the two dataTypes have the same sourceArchive and the same UniversalID OR
* are equivalent
* @param dataType1 first data type (if invoked by DB object or manager, this argument
* must correspond to the DataTypeDB).
* Returns true if the two dataTypes have the same sourceArchive and the same UniversalID OR are
* equivalent
*
* @param dataType1 first data type (if invoked by DB object or manager, this argument must
* correspond to the DataTypeDB).
* @param dataType2 second data type
* @return true if types correspond to the same type from a source archive
* or they are equivelent, otherwise false
* @return true if types correspond to the same type from a source archive or they are
* equivelent, otherwise false
*/
public static boolean isSameOrEquivalentDataType(DataType dataType1, DataType dataType2) {
// if they contain datatypes that have same ids, then they represent the same dataType
@ -226,6 +230,7 @@ public class DataTypeUtilities {
/**
* Get the name of a data type with all conflict naming patterns removed.
*
* @param dataType data type
* @param includeCategoryPath if true the category path will be included with its
* @return name with without conflict patterns
@ -238,6 +243,7 @@ public class DataTypeUtilities {
/**
* Compares two data type name strings to determine if they are equivalent names, ignoring
* conflict patterns present.
*
* @param name1 the first name
* @param name2 the second name
* @return true if the names are equivalent when conflict suffixes are ignored.
@ -249,9 +255,8 @@ public class DataTypeUtilities {
}
/**
* Get the base data type for the specified data type stripping
* away pointers and arrays only. A null will be returned for a
* default pointer.
* Get the base data type for the specified data type stripping away pointers and arrays only. A
* null will be returned for a default pointer.
*
* @param dt the data type whose base data type is to be determined.
* @return the base data type.
@ -333,8 +338,9 @@ public class DataTypeUtilities {
}
/**
* Create a data type category path derived from the specified namespace and rooted from
* the specified baseCategory
* Create a data type category path derived from the specified namespace and rooted from the
* specified baseCategory
*
* @param baseCategory category path from which to root the namespace-base path
* @param namespace the namespace
* @return namespace derived category path
@ -343,7 +349,7 @@ public class DataTypeUtilities {
Namespace namespace) {
Namespace ns = namespace;
String path = "";
while (!(ns instanceof GlobalNamespace) && !(ns instanceof Library)) {
while (!ns.isGlobal() && !(ns instanceof Library)) {
if (path.length() != 0) {
path = "/" + path;
}
@ -361,11 +367,11 @@ public class DataTypeUtilities {
}
/**
* Attempt to find the data type whose dtName and specified namespace match a
* stored data type within the specified dataTypeManager. The best match
* will be returned. The namespace will be used in checking data type parent categories,
* however if no type corresponds to the namespace another type whose name
* matches may be returned.
* Attempt to find the data type whose dtName and specified namespace match a stored data type
* within the specified dataTypeManager. The best match will be returned. The namespace will be
* used in checking data type parent categories, however if no type corresponds to the namespace
* another type whose name matches may be returned.
*
* @param dataTypeManager data type manager
* @param namespace namespace associated with dtName (null indicates no namespace constraint)
* @param dtName name of data type
@ -379,15 +385,15 @@ public class DataTypeUtilities {
}
/**
* Attempt to find the data type whose dtNameWithNamespace match a
* stored data type within the specified dataTypeManager. The best match
* will be returned. The namespace will be used in checking data type parent categories,
* however if no type corresponds to the namespace another type whose name
* matches may be returned.
* NOTE: name parsing assumes :: delimiter and can be thrown off if name include template
* information which could contain namespaces.
* Attempt to find the data type whose dtNameWithNamespace match a stored data type within the
* specified dataTypeManager. The best match will be returned. The namespace will be used in
* checking data type parent categories, however if no type corresponds to the namespace another
* type whose name matches may be returned. NOTE: name parsing assumes :: delimiter and can be
* thrown off if name include template information which could contain namespaces.
*
* @param dataTypeManager data type manager
* @param dtNameWithNamespace name of data type qualified with namespace (e.g., ns1::ns2::dtname)
* @param dtNameWithNamespace name of data type qualified with namespace (e.g.,
* ns1::ns2::dtname)
* @param classConstraint optional data type interface constraint (e.g., Structure), or null
* @return best matching data type
*/
@ -403,6 +409,7 @@ public class DataTypeUtilities {
/**
* Return the appropriate datatype for a given C primitive datatype name.
*
* @param dataTypeName the datatype name (e.g. "unsigned int", "long long")
* @return the appropriate datatype for a given C primitive datatype name.
*/
@ -452,8 +459,8 @@ public class DataTypeUtilities {
}
/**
* <code>NamespaceMatcher</code> is used to check data type categoryPath
* for match against preferred namespace.
* <code>NamespaceMatcher</code> is used to check data type categoryPath for match against
* preferred namespace.
*/
private static interface NamespaceMatcher {
boolean isNamespaceCategoryMatch(DataType dataType);

View file

@ -685,10 +685,9 @@ class StructureDB extends CompositeDB implements Structure {
}
/**
* Create copy of structure for target dtm (source archive information is
* discarded). WARNING! copying unaligned structures which contain bitfields can
* produce invalid results when switching endianess due to the differences in
* packing order.
* Create copy of structure for target dtm (source archive information is discarded). WARNING!
* copying unaligned structures which contain bitfields can produce invalid results when
* switching endianess due to the differences in packing order.
*
* @param dtm target data type manager
* @return cloned structure
@ -703,16 +702,15 @@ class StructureDB extends CompositeDB implements Structure {
}
/**
* Create cloned structure for target dtm preserving source archive information.
* WARNING! cloning unaligned structures which contain bitfields can produce
* invalid results when switching endianess due to the differences in packing
* order.
* Create cloned structure for target dtm preserving source archive information. WARNING!
* cloning unaligned structures which contain bitfields can produce invalid results when
* switching endianess due to the differences in packing order.
*
* @param dtm target data type manager
* @return cloned structure
*/
@Override
public DataType clone(DataTypeManager dtm) {
public Structure clone(DataTypeManager dtm) {
StructureDataType struct =
new StructureDataType(getCategoryPath(), getName(), getLength(), getUniversalID(),
getSourceArchive(), getLastChangeTime(), getLastChangeTimeInSourceArchive(), dtm);
@ -793,13 +791,12 @@ class StructureDB extends CompositeDB implements Structure {
}
/**
* Backup from specified ordinal to the first component which contains the
* specified offset. For normal components the specified ordinal will be
* returned, however for bit-fields the ordinal of the first bit-field
* containing the specified offset will be returned.
* Backup from specified ordinal to the first component which contains the specified offset. For
* normal components the specified ordinal will be returned, however for bit-fields the ordinal
* of the first bit-field containing the specified offset will be returned.
*
* @param ordinal component ordinal
* @param offset offset within structure
* @param offset offset within structure
* @return index of first defined component containing specific offset.
*/
private int backupToFirstComponentContainingOffset(int index, int offset) {
@ -819,13 +816,12 @@ class StructureDB extends CompositeDB implements Structure {
}
/**
* Advance from specified ordinal to the last component which contains the
* specified offset. For normal components the specified ordinal will be
* returned, however for bit-fields the ordinal of the last bit-field containing
* the specified offset will be returned.
* Advance from specified ordinal to the last component which contains the specified offset. For
* normal components the specified ordinal will be returned, however for bit-fields the ordinal
* of the last bit-field containing the specified offset will be returned.
*
* @param ordinal component ordinal
* @param offset offset within structure
* @param offset offset within structure
* @return index of last defined component containing specific offset.
*/
private int advanceToLastComponentContainingOffset(int index, int offset) {
@ -1137,16 +1133,13 @@ class StructureDB extends CompositeDB implements Structure {
}
/**
* Replaces the internal components of this structure with components of the
* given structure.
* Replaces the internal components of this structure with components of the given structure.
*
* @param dataType the structure to get the component information from.
* @throws IllegalArgumentException if any of the component data types are not
* allowed to replace a component in this
* composite data type. For example, suppose
* dt1 contains dt2. Therefore it is not valid
* to replace a dt2 component with dt1 since
* this would cause a cyclic dependency.
* @throws IllegalArgumentException if any of the component data types are not allowed to
* replace a component in this composite data type. For example, suppose dt1
* contains dt2. Therefore it is not valid to replace a dt2 component with dt1 since
* this would cause a cyclic dependency.
* @see ghidra.program.database.data.DataTypeDB#replaceWith(ghidra.program.model.data.DataType)
*/
@Override
@ -1179,8 +1172,7 @@ class StructureDB extends CompositeDB implements Structure {
*
* @param struct
* @param notify
* @return true if fully completed else false if pointer component post resolve
* required
* @return true if fully completed else false if pointer component post resolve required
* @throws DataTypeDependencyException
* @throws IOException
*/
@ -1489,9 +1481,8 @@ class StructureDB extends CompositeDB implements Structure {
/**
*
* @param definedComponentIndex the index of the defined component that is
* consuming the bytes.
* @param numBytes the number of undefined bytes to consume
* @param definedComponentIndex the index of the defined component that is consuming the bytes.
* @param numBytes the number of undefined bytes to consume
* @return the number of bytes actually consumed
*/
private int consumeBytesAfter(int definedComponentIndex, int numBytes) {
@ -1554,14 +1545,14 @@ class StructureDB extends CompositeDB implements Structure {
}
/**
* Replace the indicated component with a new component containing the specified
* data type. Flex-array component not handled.
* Replace the indicated component with a new component containing the specified data type.
* Flex-array component not handled.
*
* @param origDtc the original data type component in this structure.
* @param origDtc the original data type component in this structure.
* @param resolvedDataType the data type of the new component
* @param length the length of the new component
* @param name the field name of the new component
* @param comment the comment for the new component
* @param length the length of the new component
* @param name the field name of the new component
* @param comment the comment for the new component
* @return the new component or null if the new component couldn't fit.
*/
private DataTypeComponent replaceComponent(DataTypeComponent origDtc, DataType resolvedDataType,
@ -1638,9 +1629,8 @@ class StructureDB extends CompositeDB implements Structure {
}
/**
* Gets the number of Undefined bytes beginning at the indicated component
* ordinal. Undefined bytes that have a field name or comment specified are also
* included.
* Gets the number of Undefined bytes beginning at the indicated component ordinal. Undefined
* bytes that have a field name or comment specified are also included.
*
* @param ordinal the component ordinal to begin checking at.
* @return the number of contiguous undefined bytes
@ -1787,7 +1777,7 @@ class StructureDB extends CompositeDB implements Structure {
comp.setLength(len, true);
shiftOffsets(nextIndex, -bytesNeeded, 0);
}
else if (comp.getOrdinal() == getLastDefinedComponentIndex()) {
else if (comp.getOrdinal() == getLastDefinedComponentIndex()) {
// we are the last defined component, grow structure
doGrowStructure(bytesNeeded - bytesAvailable);
comp.setLength(len, true);
@ -1846,9 +1836,8 @@ class StructureDB extends CompositeDB implements Structure {
}
/**
* <code>ComponentComparator</code> provides ability to compare two
* DataTypeComponent objects based upon their ordinal. Intended to be used to
* sort components based upon ordinal.
* <code>ComponentComparator</code> provides ability to compare two DataTypeComponent objects
* based upon their ordinal. Intended to be used to sort components based upon ordinal.
*/
private static class ComponentComparator implements Comparator<DataTypeComponent> {
@Override
@ -1858,17 +1847,15 @@ class StructureDB extends CompositeDB implements Structure {
}
/**
* Adjust the alignment, packing and padding of components within this structure
* based upon the current alignment and packing attributes for this structure.
* This method should be called to basically fix up the layout of the internal
* components of the structure after other code has changed the attributes of
* the structure. <BR>
* When switching between internally aligned and unaligned this method corrects
* the component ordinal numbering also.
* Adjust the alignment, packing and padding of components within this structure based upon the
* current alignment and packing attributes for this structure. This method should be called to
* basically fix up the layout of the internal components of the structure after other code has
* changed the attributes of the structure. <BR>
* When switching between internally aligned and unaligned this method corrects the component
* ordinal numbering also.
*
* @param notify if true this method will do data type change notification when
* it changes the layout of the components or when it changes the
* overall size of the structure.
* @param notify if true this method will do data type change notification when it changes the
* layout of the components or when it changes the overall size of the structure.
* @return true if the structure was changed by this method.
*/
private boolean adjustComponents(boolean notify) {

View file

@ -13,73 +13,77 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.assembler.sleigh.util;
package ghidra.program.database.util;
import ghidra.program.model.listing.Program;
/**
* A convenience context for transaction IDs on a Ghidra program database
*
* This is meant to be used idiomatically, as in a try-with-resources block:
* <p>
* This is meant to be used as an idiom in a try-with-resources block:
*
* <pre>
* {@code
* try (GhidraDBTransaction t = new GhidraDBTransaction(program, "Demo")) {
* try (ProgramTransaction t = ProgramTransaction.open(program, "Demo")) {
* program.getMemory().....
* t.commit();
* }
* }
* </pre>
*
* This idiom is very useful if there is complex logic in your transaction, it's very easy to
* forget to close the transaction, especially if an error occurs, leaving the database in an open
* transaction indefinitely. Try try-with-resources block will ensure that the transaction is
* closed in all circumstances. Note, however, that in order for the transaction to be committed,
* you must call {@link #commit()}.
* <p>
* This idiom is very useful if there is complex logic in your transaction, it's very easy to forget
* to close the transaction, especially if an error occurs, leaving the database in an open
* transaction indefinitely. The try-with-resources block will ensure that the transaction is closed
* in all circumstances. Note, however, that in order for the transaction to be committed, you must
* call {@link #commit()}.
*
* <p>
* Any exceptions within the block will cause {@code t.commit()} to be skipped, thus aborting the
* transaction.
*/
public class GhidraDBTransaction implements AutoCloseable {
public class ProgramTransaction implements AutoCloseable {
protected Program program;
protected int tid;
protected boolean open;
protected boolean commit = false;
/**
* Start a transaction on the given program with the given description
*
* @param program the program to modify
* @param description a description of the transaction
*/
public GhidraDBTransaction(Program program, String description) {
public static ProgramTransaction open(Program program, String description) {
int tid = program.startTransaction(description);
return new ProgramTransaction(program, tid);
}
private ProgramTransaction(Program program, int tid) {
this.program = program;
this.tid = program.startTransaction(description);
this.open = true;
this.tid = tid;
}
/**
* Finish the transaction
*
* <p>
* If this is called before {@link #commit()}, then the transaction is aborted. This is called
* automatically at the close of a try-with-resources block.
*/
@Override
public void close() {
if (open) {
program.endTransaction(tid, false);
open = false;
}
program.endTransaction(tid, commit);
}
/**
* Finish the transaction, and commit
*
* This MUST be called in order to commit the transaction. The transaction is immediately
* closed, and any further modifications to the database will likely result in an error.
* <p>
* This MUST be called in order to commit the transaction. The transaction is not committed
* until the close of the try-with-resources block.
*/
public void commit() {
if (open) {
program.endTransaction(tid, true);
open = false;
}
commit = true;
}
}

View file

@ -27,7 +27,48 @@ import ghidra.program.model.mem.WrappedMemBuffer;
*/
public abstract class AbstractComplexDataType extends BuiltIn {
private final static long serialVersionUID = 1;
protected static AbstractComplexDataType getDefaultComplexDataType(int size) {
if (size == 8) {
return Complex8DataType.dataType;
}
if (size == 16) {
return Complex16DataType.dataType;
}
if (size == 32) {
return Complex32DataType.dataType;
}
return null;
}
public static DataType getComplexDataType(int size, DataTypeManager dtm) {
if (size < 1) {
return DefaultDataType.dataType;
}
if (size % 2 != 0) {
return Undefined.getUndefinedDataType(size);
}
int floatSize = size / 2;
if (dtm != null) {
DataOrganization dataOrganization = dtm.getDataOrganization();
if (dataOrganization != null) {
if (floatSize == dataOrganization.getFloatSize()) {
return FloatComplexDataType.dataType.clone(dtm);
}
if (floatSize == dataOrganization.getDoubleSize()) {
return DoubleComplexDataType.dataType.clone(dtm);
}
if (floatSize == dataOrganization.getLongDoubleSize()) {
return LongDoubleComplexDataType.dataType.clone(dtm);
}
}
}
DataType dt = getDefaultComplexDataType(size);
if (dt == null) {
return Undefined.getUndefinedDataType(size);
}
return dt;
}
private final AbstractFloatDataType floatType;
public AbstractComplexDataType(String name, AbstractFloatDataType floats, DataTypeManager dtm) {

View file

@ -15,13 +15,12 @@
*/
package ghidra.program.model.data;
public enum ArchiveType {
//@formatter:off
BUILT_IN,
FILE,
PROJECT,
PROGRAM,
BUILT_IN,
FILE,
PROJECT,
PROGRAM,
TEST;
//@formatter:on

View file

@ -20,22 +20,25 @@ import java.util.Comparator;
/**
* The structure interface.
* <p>
* NOTE: Structures containing only a flexible array will report a length of 1
* which will result in improper code unit sizing since we are unable to support a
* defined data of length 0.
* NOTE: Structures containing only a flexible array will report a length of 1 which will result in
* improper code unit sizing since we are unable to support a defined data of length 0.
* <p>
* NOTE: The use of zero-length bitfields within unaligned structures is discouraged since
* they have no real affect and are easily misplaced. Their use should be reserved for
* aligned/packed structures.
* NOTE: The use of zero-length bitfields within unaligned structures is discouraged since they have
* no real affect and are easily misplaced. Their use should be reserved for aligned/packed
* structures.
*/
public interface Structure extends Composite {
@Override
Structure clone(DataTypeManager dtm);
/**
* Returns the component of this structure with the indicated ordinal.
* If the specified ordinal equals {@link #getNumComponents()} the defined
* flexible array component will be returned, otherwise an out of bounds
* exception will be thrown. Use of {@link #getFlexibleArrayComponent()} is preferred
* for obtaining this special trailing component.
* Returns the component of this structure with the indicated ordinal. If the specified ordinal
* equals {@link #getNumComponents()} the defined flexible array component will be returned,
* otherwise an out of bounds exception will be thrown. Use of
* {@link #getFlexibleArrayComponent()} is preferred for obtaining this special trailing
* component.
*
* @param ordinal the component's ordinal (zero based).
* @return the data type component.
* @throws ArrayIndexOutOfBoundsException if the ordinal is out of bounds
@ -44,259 +47,263 @@ public interface Structure extends Composite {
public abstract DataTypeComponent getComponent(int ordinal);
/**
* Gets the immediate child component that contains the byte
* at the given offset. If the specified offset corresponds to
* a bit-field,the first bit-field component containing the offset
* will be returned.
* Gets the immediate child component that contains the byte at the given offset. If the
* specified offset corresponds to a bit-field,the first bit-field component containing the
* offset will be returned.
*
* @param offset the byte offset into this data type
* @return the immediate child component.
*/
public abstract DataTypeComponent getComponentAt(int offset);
/**
* Returns the primitive Data Type that is at this offset. This is useful
* for prototypes that have components that are made up of other components
* If the specified offset corresponds to
* a bit-field,the BitFieldDataType of the first bit-field component containing
* the offset will be returned.
* Returns the primitive Data Type that is at this offset. This is useful for prototypes that
* have components that are made up of other components If the specified offset corresponds to a
* bit-field,the BitFieldDataType of the first bit-field component containing the offset will be
* returned.
*
* @param offset the byte offset into this data type.
* @return the primitive data type at the offset.
*/
public abstract DataTypeComponent getDataTypeAt(int offset);
/**
* Inserts a new bitfield at the specified ordinal position in this structure.
* Within aligned structures the specified byteWidth and bitOffset will be
* ignored since packing will occur at the specified ordinal position.
* The resulting component length and bitfield details will reflect the use
* of minimal storage sizing.
* Inserts a new bitfield at the specified ordinal position in this structure. Within aligned
* structures the specified byteWidth and bitOffset will be ignored since packing will occur at
* the specified ordinal position. The resulting component length and bitfield details will
* reflect the use of minimal storage sizing.
* <p>
* For unaligned structures, a component shift will only occur if the bitfield placement
* conflicts with another component. If no conflict occurs, the bitfield will be placed
* at the specified location consuming any DEFAULT components as needed. When a conflict
* does occur a shift will be performed at the ordinal position based upon the specified
* byteWidth. When located onto existing bitfields they will be packed together
* provided they do not conflict, otherwise the conflict rule above applies.
* For unaligned structures, a component shift will only occur if the bitfield placement
* conflicts with another component. If no conflict occurs, the bitfield will be placed at the
* specified location consuming any DEFAULT components as needed. When a conflict does occur a
* shift will be performed at the ordinal position based upon the specified byteWidth. When
* located onto existing bitfields they will be packed together provided they do not conflict,
* otherwise the conflict rule above applies.
* <p>
* Supported aligned packing starts with bit-0 (lsb) of the first byte for little-endian, and
* with bit-7 (msb) of the first byte for big-endian. This is the default behavior for most
* compilers. Insertion behavior may not work as expected if packing rules differ from this.
* Supported aligned packing starts with bit-0 (lsb) of the first byte for little-endian, and
* with bit-7 (msb) of the first byte for big-endian. This is the default behavior for most
* compilers. Insertion behavior may not work as expected if packing rules differ from this.
*
* @param ordinal the ordinal where the new datatype is to be inserted.
* @param byteWidth the storage allocation unit width which contains the bitfield. Must be large
* enough to contain the "effective bit size" and corresponding bitOffset. The actual
* component size used will be recomputed during insertion.
* @param bitOffset corresponds to the bitfield left-shift amount with the storage
* unit when viewed as big-endian. The final offset may be reduced based upon
* the minimal storage size determined during insertion.
* @param byteWidth the storage allocation unit width which contains the bitfield. Must be large
* enough to contain the "effective bit size" and corresponding bitOffset. The actual
* component size used will be recomputed during insertion.
* @param bitOffset corresponds to the bitfield left-shift amount with the storage unit when
* viewed as big-endian. The final offset may be reduced based upon the minimal
* storage size determined during insertion.
* @param baseDataType the bitfield base datatype (certain restrictions apply).
* @param bitSize the declared bitfield size in bits. The effective bit size may be
* adjusted based upon the specified baseDataType.
* @param bitSize the declared bitfield size in bits. The effective bit size may be adjusted
* based upon the specified baseDataType.
* @param componentName the field name to associate with this component.
* @param comment the comment to associate with this component.
* @return the bitfield component created whose associated data type will
* be BitFieldDataType.
* @throws InvalidDataTypeException if the specified baseDataType is
* not a valid base type for bitfields.
* @throws ArrayIndexOutOfBoundsException if ordinal is less than 0 or greater than the
* current number of components.
* @return the bitfield component created whose associated data type will be BitFieldDataType.
* @throws InvalidDataTypeException if the specified baseDataType is not a valid base type for
* bitfields.
* @throws ArrayIndexOutOfBoundsException if ordinal is less than 0 or greater than the current
* number of components.
*/
public DataTypeComponent insertBitField(int ordinal, int byteWidth, int bitOffset,
DataType baseDataType, int bitSize, String componentName, String comment)
throws InvalidDataTypeException, ArrayIndexOutOfBoundsException;
/**
* Inserts a new bitfield at the specified location in this composite.
* This method is intended to be used with unaligned structures where
* the bitfield will be precisely placed. Within an aligned structure the specified
* byteOffset, byteWidth and bitOffset will be used to identify the appropriate ordinal
* but may not be preserved. The component length will be computed
* based upon the specified parameters and will be reduced from byteWidth to
* its minimal size for the new component.
* Inserts a new bitfield at the specified location in this composite. This method is intended
* to be used with unaligned structures where the bitfield will be precisely placed. Within an
* aligned structure the specified byteOffset, byteWidth and bitOffset will be used to identify
* the appropriate ordinal but may not be preserved. The component length will be computed based
* upon the specified parameters and will be reduced from byteWidth to its minimal size for the
* new component.
* <p>
* For unaligned mode, a component shift will only occur if the bitfield placement
* conflicts with another component. If no conflict occurs, the bitfield will be placed
* at the specified location consuming any DEFAULT components as needed. When a conflict
* does occur a shift will be performed at the point of conflict based upon the specified
* byteWidth. When located onto existing bitfields they will be packed together
* provided they do not conflict, otherwise the conflict rule above applies.
* For unaligned mode, a component shift will only occur if the bitfield placement conflicts
* with another component. If no conflict occurs, the bitfield will be placed at the specified
* location consuming any DEFAULT components as needed. When a conflict does occur a shift will
* be performed at the point of conflict based upon the specified byteWidth. When located onto
* existing bitfields they will be packed together provided they do not conflict, otherwise the
* conflict rule above applies.
* <p>
* Supported packing for little-endian fills lsb first, whereas big-endian fills msb first.
* Insertion behavior may not work as expected if packing rules differ from this.
* <p>
* Zero length bitfields may be inserted although they have no real affect for
* unaligned structures. Only the resulting byte offset within the structure
* is of significance in determining its ordinal placement.
* <p>
* @param byteOffset the first byte offset within this structure which corresponds to the
* first byte of the specified storage unit identified by its byteWidth.
* @param byteWidth the storage unit width which contains the bitfield. Must be large
* enough to contain the specified bitSize and corresponding bitOffset. The actual
* component size used will be recomputed during insertion.
* @param bitOffset corresponds to the bitfield left-shift amount with the storage
* unit when viewed as big-endian. The final offset may be reduced based upon
* the minimal storage size determined during insertion.
*
* Zero length bitfields may be inserted although they have no real affect for unaligned
* structures. Only the resulting byte offset within the structure is of significance in
* determining its ordinal placement.
* <p>
*
* @param byteOffset the first byte offset within this structure which corresponds to the first
* byte of the specified storage unit identified by its byteWidth.
* @param byteWidth the storage unit width which contains the bitfield. Must be large enough to
* contain the specified bitSize and corresponding bitOffset. The actual component
* size used will be recomputed during insertion.
* @param bitOffset corresponds to the bitfield left-shift amount with the storage unit when
* viewed as big-endian. The final offset may be reduced based upon the minimal
* storage size determined during insertion.
* @param baseDataType the bitfield base datatype (certain restrictions apply).
* @param componentName the field name to associate with this component.
* @param bitSize the bitfield size in bits. A bitSize of 0 may be specified
* although its name will be ignored.
* @param bitSize the bitfield size in bits. A bitSize of 0 may be specified although its name
* will be ignored.
* @param comment the comment to associate with this component.
* @return the componentDataType created whose associated data type will
* be BitFieldDataType.
* @throws InvalidDataTypeException if the specified data type is
* not a valid base type for bitfields.
* @return the componentDataType created whose associated data type will be BitFieldDataType.
* @throws InvalidDataTypeException if the specified data type is not a valid base type for
* bitfields.
*/
public DataTypeComponent insertBitFieldAt(int byteOffset, int byteWidth, int bitOffset,
DataType baseDataType, int bitSize, String componentName, String comment)
throws InvalidDataTypeException;
/**
* Inserts a new datatype at the specified offset into this structure.
* Inserting a component will causing any conflicting component
* to shift down to the extent necessary to avoid a conflict.
* @param offset the byte offset into the structure where the new datatype is to be inserted.
* Inserts a new datatype at the specified offset into this structure. Inserting a component
* will causing any conflicting component to shift down to the extent necessary to avoid a
* conflict.
*
* @param offset the byte offset into the structure where the new datatype is to be inserted.
* @param dataType the datatype to insert.
* @param length the length to associate with the dataType.
* For fixed length types a length &lt;= 0 will use the length of the resolved dataType.
* @param length the length to associate with the dataType. For fixed length types a length
* &lt;= 0 will use the length of the resolved dataType.
* @return the componentDataType created.
* @throws IllegalArgumentException if the specified data type is not
* allowed to be inserted into this composite data type or an invalid length
* is specified.
* For example, suppose dt1 contains dt2. Therefore it is not valid
* to insert dt1 to dt2 since this would cause a cyclic dependency.
* @throws IllegalArgumentException if the specified data type is not allowed to be inserted
* into this composite data type or an invalid length is specified. For example,
* suppose dt1 contains dt2. Therefore it is not valid to insert dt1 to dt2 since
* this would cause a cyclic dependency.
*/
public DataTypeComponent insertAtOffset(int offset, DataType dataType, int length)
throws IllegalArgumentException;
/**
* Inserts a new datatype at the specified offset into this structure.
* Inserting a component will causing any conflicting component
* to shift down to the extent necessary to avoid a conflict.
* @param offset the byte offset into the structure where the new datatype is to be inserted.
* Inserts a new datatype at the specified offset into this structure. Inserting a component
* will causing any conflicting component to shift down to the extent necessary to avoid a
* conflict.
*
* @param offset the byte offset into the structure where the new datatype is to be inserted.
* @param dataType the datatype to insert.
* @param length the length to associate with the dataType.
* For fixed length types a length &lt;= 0 will use the length of the resolved dataType.
* @param length the length to associate with the dataType. For fixed length types a length
* &lt;= 0 will use the length of the resolved dataType.
* @param name the field name to associate with this component.
* @param comment the comment to associate with this component.
* @return the componentDataType created.
* @throws IllegalArgumentException if the specified data type is not
* allowed to be inserted into this composite data type or an invalid length is specified.
* For example, suppose dt1 contains dt2. Therefore it is not valid
* to insert dt1 to dt2 since this would cause a cyclic dependency.
* @throws IllegalArgumentException if the specified data type is not allowed to be inserted
* into this composite data type or an invalid length is specified. For example,
* suppose dt1 contains dt2. Therefore it is not valid to insert dt1 to dt2 since
* this would cause a cyclic dependency.
*/
public DataTypeComponent insertAtOffset(int offset, DataType dataType, int length, String name,
String comment) throws IllegalArgumentException;
/**
* Deletes the component containing the specified offset in this structure. If the offset
* corresponds to a bit-field, all bit-fields whose base type group contains the offset will
* be removed.
* @param offset the byte offset into the structure where the datatype is to be deleted.
* Deletes the component containing the specified offset in this structure. If the offset
* corresponds to a bit-field, all bit-fields whose base type group contains the offset will be
* removed.
*
* @param offset the byte offset into the structure where the datatype is to be deleted.
*/
public void deleteAtOffset(int offset);
/**
* Remove all components from this structure (including flex-array),
* effectively setting the length to zero.
* Remove all components from this structure (including flex-array), effectively setting the
* length to zero.
*/
public void deleteAll();
/**
* Clears the defined component at the given component index. Clearing a
* component causes a defined component to be replaced with a number of
* undefined dataTypes to offset the removal of the defined dataType.
* Clears the defined component at the given component index. Clearing a component causes a
* defined component to be replaced with a number of undefined dataTypes to offset the removal
* of the defined dataType.
*
* @param index the index of the component to clear.
* @throws ArrayIndexOutOfBoundsException if component ordinal is out of bounds
*/
public void clearComponent(int index) throws ArrayIndexOutOfBoundsException;
/**
* Replaces the component at the given component index with a new component
* of the indicated data type.
* @param index the index where the datatype is to be replaced.
* Replaces the component at the given component index with a new component of the indicated
* data type.
*
* @param index the index where the datatype is to be replaced.
* @param dataType the datatype to insert.
* @param length the length of the dataType to insert.
* For fixed length types a length &lt;= 0 will use the length of the resolved dataType.
* @param length the length of the dataType to insert. For fixed length types a length &lt;= 0
* will use the length of the resolved dataType.
* @return the new componentDataType at the index.
* @throws IllegalArgumentException if the specified data type is not
* allowed to replace a component in this composite data type or an invalid
* length is specified.
* For example, suppose dt1 contains dt2. Therefore it is not valid
* to replace a dt2 component with dt1 since this would cause a cyclic
* dependency. In addition, any attempt to replace an existing bit-field
* component or specify a {@link BitFieldDataType} will produce this error.
* @throws IllegalArgumentException if the specified data type is not allowed to replace a
* component in this composite data type or an invalid length is specified. For
* example, suppose dt1 contains dt2. Therefore it is not valid to replace a dt2
* component with dt1 since this would cause a cyclic dependency. In addition, any
* attempt to replace an existing bit-field component or specify a
* {@link BitFieldDataType} will produce this error.
* @throws ArrayIndexOutOfBoundsException if component index is out of bounds
*/
public DataTypeComponent replace(int index, DataType dataType, int length)
throws ArrayIndexOutOfBoundsException, IllegalArgumentException;
/**
* Replaces the component at the given component index with a new component
* of the indicated data type.
* @param index the index where the datatype is to be replaced.
* Replaces the component at the given component index with a new component of the indicated
* data type.
*
* @param index the index where the datatype is to be replaced.
* @param dataType the datatype to insert.
* @param length the length to associate with the dataType.
* For fixed length types a length &lt;= 0 will use the length of the resolved dataType.
* @param length the length to associate with the dataType. For fixed length types a length
* &lt;= 0 will use the length of the resolved dataType.
* @param name the field name to associate with this component.
* @param comment the comment to associate with this component.
* @return the new componentDataType at the index.
* @throws IllegalArgumentException if the specified data type is not
* allowed to replace a component in this composite data type or an invalid
* length is specified.
* For example, suppose dt1 contains dt2. Therefore it is not valid
* to replace a dt2 component with dt1 since this would cause a cyclic
* dependency. In addition, any attempt to replace an existing bit-field
* component or specify a {@link BitFieldDataType} will produce this error.
* @throws IllegalArgumentException if the specified data type is not allowed to replace a
* component in this composite data type or an invalid length is specified. For
* example, suppose dt1 contains dt2. Therefore it is not valid to replace a dt2
* component with dt1 since this would cause a cyclic dependency. In addition, any
* attempt to replace an existing bit-field component or specify a
* {@link BitFieldDataType} will produce this error.
* @throws ArrayIndexOutOfBoundsException if component index is out of bounds
*/
public DataTypeComponent replace(int index, DataType dataType, int length, String name,
String comment) throws ArrayIndexOutOfBoundsException, IllegalArgumentException;
/**
* Replaces the component at the specified byte offset with a new component
* of the indicated data type. If the offset corresponds to a bit-field, all bit-fields
* at that offset will be removed and replaced by the specified component. Keep in mind
* bit-field or any component removal must clear sufficient space for an unaligned
* structure to complete the replacement.
* @param offset the byte offset into the structure where the datatype is
* to be replaced.
* Replaces the component at the specified byte offset with a new component of the indicated
* data type. If the offset corresponds to a bit-field, all bit-fields at that offset will be
* removed and replaced by the specified component. Keep in mind bit-field or any component
* removal must clear sufficient space for an unaligned structure to complete the replacement.
*
* @param offset the byte offset into the structure where the datatype is to be replaced.
* @param dataType the datatype to insert.
* @param length the length to associate with the dataType.
* For fixed length types a length &lt;= 0 will use the length of the resolved dataType.
* @param length the length to associate with the dataType. For fixed length types a length
* &lt;= 0 will use the length of the resolved dataType.
* @param name the field name to associate with this component.
* @param comment the comment to associate with this component.
* @return the new componentDataType at the index.
* @throws IllegalArgumentException if the specified data type is not
* allowed to replace a component in this composite data type or an invalid
* length is specified.
* For example, suppose dt1 contains dt2. Therefore it is not valid
* to replace a dt2 component with dt1 since this would cause a cyclic
* dependency. In addition, any attempt to replace an existing bit-field
* component or specify a {@link BitFieldDataType} will produce this error.
* @throws IllegalArgumentException if the specified data type is not allowed to replace a
* component in this composite data type or an invalid length is specified. For
* example, suppose dt1 contains dt2. Therefore it is not valid to replace a dt2
* component with dt1 since this would cause a cyclic dependency. In addition, any
* attempt to replace an existing bit-field component or specify a
* {@link BitFieldDataType} will produce this error.
*/
public DataTypeComponent replaceAtOffset(int offset, DataType dataType, int length, String name,
String comment) throws IllegalArgumentException;
/**
* Determine if a trailing flexible array component has been defined.
*
* @return true if trailing flexible array component has been defined.
*/
public boolean hasFlexibleArrayComponent();
/**
* Get the optional trailing flexible array component associated with this structure.
* @return optional trailing flexible array component associated with this structure or null
* if not present.
*
* @return optional trailing flexible array component associated with this structure or null if
* not present.
*/
public DataTypeComponent getFlexibleArrayComponent();
/**
* Set the optional trailing flexible array component associated with this structure.
* @param flexType the flexible array dataType (example: for 'char[0]' the type 'char' should be specified)
*
* @param flexType the flexible array dataType (example: for 'char[0]' the type 'char' should be
* specified)
* @param name component field name or null for default name
* @param comment component comment
* @return updated flexible array component
* @throws IllegalArgumentException if specified flexType is not permitted (e.g.,
* self referencing or unsupported type)
* @throws IllegalArgumentException if specified flexType is not permitted (e.g., self
* referencing or unsupported type)
*/
public DataTypeComponent setFlexibleArrayComponent(DataType flexType, String name,
String comment) throws IllegalArgumentException;
@ -307,38 +314,40 @@ public interface Structure extends Composite {
public void clearFlexibleArrayComponent();
/**
* Increases the size of the structure by the given amount by adding undefined datatypes
* at the end of the structure.
* Increases the size of the structure by the given amount by adding undefined datatypes at the
* end of the structure.
*
* @param amount the amount by which to grow the structure.
* @throws IllegalArgumentException if amount &lt; 1
*/
public void growStructure(int amount);
/**
* Sets the current packing value (usually a power of 2). A value of NOT_PACKING should be passed
* if this isn't a packed data type. Otherwise this value indicates a maximum alignment
* Sets the current packing value (usually a power of 2). A value of NOT_PACKING should be
* passed if this isn't a packed data type. Otherwise this value indicates a maximum alignment
* for any component within this data type. Calling this method will cause the data type to
* become an internally aligned data type.
* (Same as {@link Composite#setPackingValue(int)})
* @param maxAlignment the new packing value or 0 for NOT_PACKING.
* A negative value will be treated the same as 0.
* become an internally aligned data type. (Same as {@link Composite#setPackingValue(int)})
*
* @param maxAlignment the new packing value or 0 for NOT_PACKING. A negative value will be
* treated the same as 0.
*/
public void pack(int maxAlignment);
/**
* <code>BitOffsetComparator</code> provides ability to compare an normalized bit offset
* (see {@link #getNormalizedBitfieldOffset(int, int, int, int, boolean)}) with a
* {@link DataTypeComponent} object. The offset will be considered equal (0) if the component
* contains the offset. A normalized component bit numbering is used to establish the footprint
* of each component with an ordinal-based ordering (assumes specific LE/BE allocation rules).
* <code>BitOffsetComparator</code> provides ability to compare an normalized bit offset (see
* {@link #getNormalizedBitfieldOffset(int, int, int, int, boolean)}) with a
* {@link DataTypeComponent} object. The offset will be considered equal (0) if the component
* contains the offset. A normalized component bit numbering is used to establish the footprint
* of each component with an ordinal-based ordering (assumes specific LE/BE allocation rules).
* Bit offsets for this comparator number the first allocated bit of the structure as 0 and the
* last allocated bit of the structure as (8 * structLength) - 1. For big-endian bitfields
* the msb of the bitfield will be assigned the lower bit-number (assumes msb-allocated-first),
* while little-endian will perform similar numbering assuming byte-swap and bit-reversal of the
* storage unit (assumes lsb-allocated-first). Both cases result in a normalized view where
* last allocated bit of the structure as (8 * structLength) - 1. For big-endian bitfields the
* msb of the bitfield will be assigned the lower bit-number (assumes msb-allocated-first),
* while little-endian will perform similar numbering assuming byte-swap and bit-reversal of the
* storage unit (assumes lsb-allocated-first). Both cases result in a normalized view where
* normalized bit-0 is allocated first.
*
* <pre>{@literal
* <pre>
* {@literal
* Example:
*
* Big-Endian (normalized view):
@ -351,7 +360,8 @@ public interface Structure extends Composite {
* | . . . . . . 6 7 | 8 . . . . . . . |
* |------------>| bit-offset (6, lsb position within storage unit)
* |<--->| bit-size (3)
* }</pre>
* }
* </pre>
*/
public static class BitOffsetComparator implements Comparator<Object> {
@ -394,14 +404,14 @@ public interface Structure extends Composite {
* Compute the normalized bit offset of a bitfield relative to the start of a structure.
*
* NOTE: This implementation currently relies only on endianess to dictate bit allocation
* ordering. If future support is added for alternate bitfield packing, this implementation will
* require modification.
* ordering. If future support is added for alternate bitfield packing, this implementation
* will require modification.
*
* @param byteOffset byte offset within structure of storage unit
* @param storageSize storage unit size (i.e., component length)
* @param effectiveBitSize size of bitfield in bits
* @param bitOffset left shift amount for bitfield based upon a big-endian view of the
* storage unit
* storage unit
* @param bigEndian true if big-endian packing applies
* @return normalized bit-offset
*/
@ -430,9 +440,9 @@ public interface Structure extends Composite {
}
/**
* <code>OffsetComparator</code> provides ability to compare an Integer offset
* with a DataTypeComponent object. The offset will be consider equal (0) if
* the component contains the offset.
* <code>OffsetComparator</code> provides ability to compare an Integer offset with a
* DataTypeComponent object. The offset will be consider equal (0) if the component contains the
* offset.
*/
public static class OffsetComparator implements Comparator<Object> {
@ -455,9 +465,9 @@ public interface Structure extends Composite {
}
/**
* <code>OrdinalComparator</code> provides ability to compare an Integer ordinal
* with a DataTypeComponent object. The offset will be consider equal (0) if
* the component corresponds to the specified ordinal.
* <code>OrdinalComparator</code> provides ability to compare an Integer ordinal with a
* DataTypeComponent object. The offset will be consider equal (0) if the component corresponds
* to the specified ordinal.
*/
public static class OrdinalComparator implements Comparator<Object> {

View file

@ -40,54 +40,52 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
private int alignment = -1;
/**
* Construct a new structure with the given name and length.
* The root category will be used.
* Construct a new structure with the given name and length. The root category will be used.
*
* @param name the name of the new structure
* @param length the initial size of the structure in bytes. If 0 is specified
* the structure will report its length as 1 and {@link #isNotYetDefined()}
* will return true.
* @param length the initial size of the structure in bytes. If 0 is specified the structure
* will report its length as 1 and {@link #isNotYetDefined()} will return true.
*/
public StructureDataType(String name, int length) {
this(CategoryPath.ROOT, name, length);
}
/**
* Construct a new structure with the given name, length and datatype manager
* which conveys data organization. The root category will be used.
* Construct a new structure with the given name, length and datatype manager which conveys data
* organization. The root category will be used.
*
* @param name the name of the new structure
* @param length the initial size of the structure in bytes. If 0 is specified
* the structure will report its length as 1 and {@link #isNotYetDefined()}
* will return true.
* @param dtm the data type manager associated with this data type. This can be null.
* Also, the data type manager may not yet contain this actual data type.
* @param length the initial size of the structure in bytes. If 0 is specified the structure
* will report its length as 1 and {@link #isNotYetDefined()} will return true.
* @param dtm the data type manager associated with this data type. This can be null. Also, the
* data type manager may not yet contain this actual data type.
*/
public StructureDataType(String name, int length, DataTypeManager dtm) {
this(CategoryPath.ROOT, name, length, dtm);
}
/**
* Construct a new structure with the given name and length within the
* specified categry path.
* Construct a new structure with the given name and length within the specified categry path.
*
* @param path the category path indicating where this data type is located.
* @param name the name of the new structure
* @param length the initial size of the structure in bytes. If 0 is specified
* the structure will report its length as 1 and {@link #isNotYetDefined()}
* will return true.
* @param length the initial size of the structure in bytes. If 0 is specified the structure
* will report its length as 1 and {@link #isNotYetDefined()} will return true.
*/
public StructureDataType(CategoryPath path, String name, int length) {
this(path, name, length, null);
}
/**
* Construct a new structure with the given name, length and datatype manager
* within the specified categry path.
* Construct a new structure with the given name, length and datatype manager within the
* specified categry path.
*
* @param path the category path indicating where this data type is located.
* @param name the name of the new structure
* @param length the initial size of the structure in bytes. If 0 is specified
* the structure will report its length as 1 and {@link #isNotYetDefined()}
* will return true.
* @param dtm the data type manager associated with this data type. This can be null.
* Also, the data type manager may not yet contain this actual data type.
* @param length the initial size of the structure in bytes. If 0 is specified the structure
* will report its length as 1 and {@link #isNotYetDefined()} will return true.
* @param dtm the data type manager associated with this data type. This can be null. Also, the
* data type manager may not yet contain this actual data type.
*/
public StructureDataType(CategoryPath path, String name, int length, DataTypeManager dtm) {
super(path, name, dtm);
@ -102,18 +100,18 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
/**
* Construct a new structure with the given name and length
*
* @param path the category path indicating where this data type is located.
* @param name the name of the new structure
* @param length the initial size of the structure in bytes. If 0 is specified
* the structure will report its length as 1 and {@link #isNotYetDefined()}
* will return true.
* @param length the initial size of the structure in bytes. If 0 is specified the structure
* will report its length as 1 and {@link #isNotYetDefined()} will return true.
* @param universalID the id for the data type
* @param sourceArchive the source archive for this data type
* @param lastChangeTime the last time this data type was changed
* @param lastChangeTimeInSourceArchive the last time this data type was changed in
* its source archive.
* @param dtm the data type manager associated with this data type. This can be null.
* Also, the data type manager may not yet contain this actual data type.
* @param lastChangeTimeInSourceArchive the last time this data type was changed in its source
* archive.
* @param dtm the data type manager associated with this data type. This can be null. Also, the
* data type manager may not yet contain this actual data type.
*/
public StructureDataType(CategoryPath path, String name, int length, UniversalID universalID,
SourceArchive sourceArchive, long lastChangeTime, long lastChangeTimeInSourceArchive,
@ -399,21 +397,21 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
/**
* Add a new component to the end of this structure.
* <p>
* NOTE: This method differs from inserting to the end the structure for the unaligned
* case in that this method will always grow the structure by the positive length
* specified while the insert may limit its growth by the length of a smaller fixed-length
* dataType.
* NOTE: This method differs from inserting to the end the structure for the unaligned case in
* that this method will always grow the structure by the positive length specified while the
* insert may limit its growth by the length of a smaller fixed-length dataType.
*
* @param dataType component data type
* @param length maximum component length or -1 to use length of fixed-length dataType
* after applying structures data organization as determined by data type manager.
* If dataType is Dynamic, a positive length must be specified.
* @param isFlexibleArray if true length is ignored and the trailing flexible array will be
* set based upon the specified fixed-length dataType;
* @param length maximum component length or -1 to use length of fixed-length dataType after
* applying structures data organization as determined by data type manager. If
* dataType is Dynamic, a positive length must be specified.
* @param isFlexibleArray if true length is ignored and the trailing flexible array will be set
* based upon the specified fixed-length dataType;
* @param componentName component name
* @param comment componetn comment
* @return newly added component
* @throws IllegalArgumentException if the specified data type is not
* allowed to be added to this composite data type or an invalid length is specified.
* @throws IllegalArgumentException if the specified data type is not allowed to be added to
* this composite data type or an invalid length is specified.
*/
private DataTypeComponent doAdd(DataType dataType, int length, boolean isFlexibleArray,
String componentName, String comment) throws IllegalArgumentException {
@ -687,10 +685,10 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
}
/**
* Backup from specified ordinal to the first component which contains
* the specified offset. For normal components the specified
* ordinal will be returned, however for bit-fields the ordinal of the first
* bit-field containing the specified offset will be returned.
* Backup from specified ordinal to the first component which contains the specified offset. For
* normal components the specified ordinal will be returned, however for bit-fields the ordinal
* of the first bit-field containing the specified offset will be returned.
*
* @param ordinal component ordinal
* @param offset offset within structure
* @return index of first defined component containing specific offset.
@ -712,10 +710,10 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
}
/**
* Advance from specified ordinal to the last component which contains
* the specified offset. For normal components the specified
* ordinal will be returned, however for bit-fields the ordinal of the last
* bit-field containing the specified offset will be returned.
* Advance from specified ordinal to the last component which contains the specified offset. For
* normal components the specified ordinal will be returned, however for bit-fields the ordinal
* of the last bit-field containing the specified offset will be returned.
*
* @param ordinal component ordinal
* @param offset offset within structure
* @return index of last defined component containing specific offset.
@ -892,9 +890,10 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
}
/**
* Create copy of structure for target dtm (source archive information is discarded).
* WARNING! copying unaligned structures which contain bitfields can produce
* invalid results when switching endianess due to the differences in packing order.
* Create copy of structure for target dtm (source archive information is discarded). WARNING!
* copying unaligned structures which contain bitfields can produce invalid results when
* switching endianess due to the differences in packing order.
*
* @param dtm target data type manager
* @return cloned structure
*/
@ -907,14 +906,15 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
}
/**
* Create cloned structure for target dtm preserving source archive information.
* WARNING! cloning unaligned structures which contain bitfields can produce
* invalid results when switching endianess due to the differences in packing order.
* Create cloned structure for target dtm preserving source archive information. WARNING!
* cloning unaligned structures which contain bitfields can produce invalid results when
* switching endianess due to the differences in packing order.
*
* @param dtm target data type manager
* @return cloned structure
*/
@Override
public DataType clone(DataTypeManager dtm) {
public StructureDataType clone(DataTypeManager dtm) {
if (dataMgr == dtm) {
return this;
}
@ -945,14 +945,13 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
}
/**
* Replaces the internal components of this structure with components of the
* given structure.
* Replaces the internal components of this structure with components of the given structure.
*
* @param dataType the structure to get the component information from.
* @throws IllegalArgumentException if any of the component data types
* are not allowed to replace a component in this composite data type.
* For example, suppose dt1 contains dt2. Therefore it is not valid
* to replace a dt2 component with dt1 since this would cause a cyclic
* dependency.
* @throws IllegalArgumentException if any of the component data types are not allowed to
* replace a component in this composite data type. For example, suppose dt1
* contains dt2. Therefore it is not valid to replace a dt2 component with dt1 since
* this would cause a cyclic dependency.
*/
@Override
public void replaceWith(DataType dataType) {
@ -1248,20 +1247,20 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
}
/**
* Replace the indicated component with a new component containing the
* specified data type.
* Replace the indicated component with a new component containing the specified data type.
*
* @param origDtc the original data type component in this structure.
* @param dataType the data type of the new component
* @param length the length of the new component
* @param componentName the field name of the new component
* @param comment the comment for the new component
* @return the new component or null if the new component couldn't fit.
* @throws IllegalArgumentException if the specified data type is not
* allowed to replace a component in this composite data type.
* For example, suppose dt1 contains dt2. Therefore it is not valid
* to replace a dt2 component with dt1 since this would cause a cyclic
* dependency. In addition, any attempt to replace an existing bit-field
* component or specify a {@link BitFieldDatatype} will produce this error.
* @throws IllegalArgumentException if the specified data type is not allowed to replace a
* component in this composite data type. For example, suppose dt1 contains dt2.
* Therefore it is not valid to replace a dt2 component with dt1 since this would
* cause a cyclic dependency. In addition, any attempt to replace an existing
* bit-field component or specify a {@link BitFieldDatatype} will produce this
* error.
*/
private DataTypeComponent replaceComponent(DataTypeComponentImpl origDtc, DataType dataType,
int length, String componentName, String comment) {
@ -1321,9 +1320,9 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
}
/**
* Gets the number of Undefined bytes beginning at the indicated component
* index. Undefined bytes that have a field name or comment specified are
* also included.
* Gets the number of Undefined bytes beginning at the indicated component index. Undefined
* bytes that have a field name or comment specified are also included.
*
* @param index the component index to begin checking at.
* @return the number of contiguous undefined bytes
*/
@ -1384,12 +1383,13 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
}
/**
* Adjust the alignment, packing and padding of components within this structure based upon the
* current alignment and packing attributes for this structure. This method should be
* called to fix up the layout of the internal components of the structure
* after other code has changed the attributes of the structure.
* <BR>When switching between internally aligned and unaligned this method corrects the
* component ordinal numbering also.
* Adjust the alignment, packing and padding of components within this structure based upon the
* current alignment and packing attributes for this structure. This method should be called to
* fix up the layout of the internal components of the structure after other code has changed
* the attributes of the structure. <BR>
* When switching between internally aligned and unaligned this method corrects the component
* ordinal numbering also.
*
* @return true if the structure was changed by this method.
*/
protected boolean adjustComponents() {

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -20,9 +19,9 @@ import java.util.Iterator;
public class ReferenceIteratorAdapter implements ReferenceIterator {
private final Iterator<Reference> iterator;
private final Iterator<? extends Reference> iterator;
public ReferenceIteratorAdapter(Iterator<Reference> iterator) {
public ReferenceIteratorAdapter(Iterator<? extends Reference> iterator) {
this.iterator = iterator;
}

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -20,9 +19,9 @@ import java.util.Iterator;
public class SymbolIteratorAdapter implements SymbolIterator {
private final Iterator<Symbol> iterator;
private final Iterator<? extends Symbol> iterator;
public SymbolIteratorAdapter(Iterator<Symbol> iterator) {
public SymbolIteratorAdapter(Iterator<? extends Symbol> iterator) {
this.iterator = iterator;
}

View file

@ -15,24 +15,27 @@
*/
package ghidra.program.util;
import java.lang.reflect.InvocationTargetException;
import java.util.Objects;
import ghidra.framework.options.SaveState;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.*;
import ghidra.util.Msg;
/**
* <CODE>ProgramLocation</CODE> provides information about a location in a
* program in the most generic way.
* <CODE>ProgramLocation</CODE> provides information about a location in a program in the most
* generic way.
*
* ProgramLocations refer to a specific location in a program and can be specified down
* to an address, a field at that address, and within that field, a row, col, and character
* offset. The field is not recorded directly, but by the subclass of the ProgramLocation.
* The "cursor position" within a field is specified by three variables: row, col, and character
* offset. The row is literally the row (line #) the cursor is on within the field, the
* column represents the display item on that row (For example, in the bytes field
* the column will represent which "byte" the cursor is on. Most fields only have one
* column item per row.) And finally, the character offset
* is the character position within the display item specified by the row and column. Simple fields
* <p>
* ProgramLocations refer to a specific location in a program and can be specified down to an
* address, a field at that address, and within that field, a row, col, and character offset. The
* field is not recorded directly, but by the subclass of the ProgramLocation. The "cursor position"
* within a field is specified by three variables: row, col, and character offset. The row is
* literally the row (line #) the cursor is on within the field, the column represents the display
* item on that row (For example, in the bytes field the column will represent which "byte" the
* cursor is on. Most fields only have one column item per row.) And finally, the character offset
* is the character position within the display item specified by the row and column. Simple fields
* like the address field and Mnemonic field will always have a row and column of 0.
*/
public class ProgramLocation implements Comparable<ProgramLocation> {
@ -48,19 +51,19 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
/**
* Construct a new ProgramLocation.
* <br>Note: A NullPointerException will be logged if addr is null.
*
* @param program the program of the location
* @param addr address of the location; cannot be null; This could be a
* code unit minimum address where the byteAddr is within the code unit.
* @param addr address of the location; cannot be null; This could be a code unit minimum
* address where the byteAddr is within the code unit.
* @param byteAddr address of the location; cannot be null
* @param componentPath array of indexes for each nested data component;
* the data index is the data component's index within its parent; may be null
* @param refAddr the "referred to" address if the location is
* over a reference; may be null
* @param componentPath array of indexes for each nested data component; the data index is the
* data component's index within its parent; may be null
* @param refAddr the "referred to" address if the location is over a reference; may be null
* @param row the row within the field.
* @param col - the display item index on the given row. (Note most fields only have one display item per row)
* @param charOffset - the character offset within the display item.
* @param col the display item index on the given row. (Note most fields only have one display
* item per row)
* @param charOffset the character offset within the display item.
* @throws NullPointerException if {@code addr} or {@code program} is null
*/
public ProgramLocation(Program program, Address addr, Address byteAddr, int[] componentPath,
Address refAddr, int row, int col, int charOffset) {
@ -89,20 +92,21 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
}
/**
* Construct a new ProgramLocation for the given address. The address will be adjusted
* to the beginning of the code unit containing that address(if it exists). The original
* address can be retrieved using the "getByteAddress()" method.
* <br>Note: A NullPointerException will be logged if addr is null.
* @param program the program associated with this program location (also
* used to obtain a code-unit-aligned address)
* Construct a new ProgramLocation for the given address. The address will be adjusted to the
* beginning of the {@link CodeUnit code unit} containing that address (if it exists). The
* original address can be retrieved using the {@link #getByteAddress()}" method.
*
* @param program the program associated with this program location (also used to obtain a
* code-unit-aligned address)
* @param addr address of the location; cannot be null
* @param componentPath array of indexes for each nested data component;
* the index is the data component's index within its parent; may be null
* @param refAddr the "referred to" address if the location is
* over a reference; may be null
* @param componentPath array of indexes for each nested data component; the index is the data
* component's index within its parent; may be null
* @param refAddr the "referred to" address if the location is over a reference; may be null
* @param row the row within the field.
* @param col - the display item index on the given row. (Note most fields only have one display item per row)
* @param charOffset - the character offset within the display item.
* @param col the display item index on the given row. (Note most fields only have one display
* item per row)
* @param charOffset the character offset within the display item.
* @throws NullPointerException if {@code addr} or {@code program} is null
*/
public ProgramLocation(Program program, Address addr, int[] componentPath, Address refAddr,
int row, int col, int charOffset) {
@ -111,41 +115,47 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
}
/**
* Construct a new ProgramLocation for the given address. The address will be adjusted
* to the beginning of the code unit containing that address(if it exists). The original
* address can be retrieved using the "getByteAddress()" method.
* @param program the program associated with this program location (also
* used to obtain a code-unit-aligned address)
* Construct a new ProgramLocation for the given address. The address will be adjusted to the
* beginning of the {@link CodeUnit code unit} containing that address (if it exists). The
* original address can be retrieved using the {@link #getByteAddress()} method.
*
* @param program the program associated with this program location (also used to obtain a
* code-unit-aligned address)
* @param addr address for the location
* @throws NullPointerException if {@code addr} or {@code program} is null
*/
public ProgramLocation(Program program, Address addr) {
this(program, getCodeUnitAddress(program, addr), addr, null, null, 0, 0, 0);
}
/**
* Construct a new ProgramLocation for the given address. The address will be adjusted
* to the beginning of the code unit containing that address(if it exists). The original
* address can be retrieved using the "getByteAddress()" method.
* @param program the program associated with this program location (also
* used to obtain a code-unit-aligned address)
* Construct a new ProgramLocation for the given address. The address will be adjusted to the
* beginning of the {@link CodeUnit code unit} containing that address (if it exists). The
* original address can be retrieved using the {@link #getByteAddress()} method.
*
* @param program the program associated with this program location (also used to obtain a
* code-unit-aligned address)
* @param addr address for the location
* @param row the row within the field.
* @param col - the display item index on the given row. (Note most fields only have one display item per row)
* @param charOffset - the character offset within the display item.
* @param col the display item index on the given row. (Note most fields only have one display
* item per row)
* @param charOffset the character offset within the display item.
* @throws NullPointerException if {@code addr} or {@code program} is null
*/
public ProgramLocation(Program program, Address addr, int row, int col, int charOffset) {
this(program, getCodeUnitAddress(program, addr), addr, null, null, row, col, charOffset);
}
/**
* Construct a new ProgramLocation for the given address. The address will be adjusted
* to the beginning of the code unit containing that address(if it exists). The original
* address can be retrieved using the "getByteAddress()" method.
* @param program the program associated with this program location (also
* used to obtain a code-unit-aligned address)
* Construct a new ProgramLocation for the given address. The address will be adjusted to the
* beginning of the {@link CodeUnit code unit} containing that address (if it exists). The
* original address can be retrieved using the {@link #getByteAddress()} method.
*
* @param program the program associated with this program location (also used to obtain a
* code-unit-aligned address)
* @param addr address for the location
* @param refAddr the "referred to" address if the location is over a
* reference
* @param refAddr the "referred to" address if the location is over a reference
* @throws NullPointerException if {@code addr} or {@code program} is null
*/
public ProgramLocation(Program program, Address addr, Address refAddr) {
this(program, getCodeUnitAddress(program, addr), addr, null, refAddr, 0, 0, 0);
@ -158,15 +168,15 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
}
/**
* Returns the componentPath for the codeUnit. Null will be returned if the
* object is an Instruction or a top-level Data object.
* Returns the componentPath for the {@link CodeUnit code unit}. Null will be returned if the
* object is an {@link Instruction} or a top-level {@link Data} object.
*/
public int[] getComponentPath() {
return componentPath;
}
/**
* Returns program associated with location or null if not specified.
* Returns the program associated with this location.
*/
public Program getProgram() {
return program;
@ -174,9 +184,11 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
/**
* Returns the address associated with this location.
* <br>Note: this may not be the same as the byte address. For example, in
* a code unit location this may be the minimum address of the code unit
* that contains the byte address.
*
* <p>
* Note: this may not be the same as the byte address. For example, in a {@link CodeUnit code
* unit} location this may be the minimum address of the code unit that contains the byte
* address.
*/
public Address getAddress() {
return addr;
@ -190,8 +202,7 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
}
/**
* Returns the "referred to" address if the location is over an
* address in some field.
* Returns the "referred to" address if the location is over an address in some field.
*/
public Address getRefAddress() {
return refAddr;
@ -199,6 +210,7 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
/**
* Save this program location to the given save state object.
*
* @param obj the save state object for saving the location
*/
public void saveState(SaveState obj) {
@ -218,8 +230,8 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
}
/**
* Restore this program location using the given program
* and save state object.
* Restore this program location using the given program and save state object.
*
* @param program1 program to restore from
* @param obj the save state to restore from
*/
@ -240,6 +252,13 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
}
/**
* Get the program location for the given program and save state object.
*
* @param program the program for the location
* @param saveState the state to restore
* @return the restored program location
*/
public static ProgramLocation getLocation(Program program, SaveState saveState) {
String className = saveState.getString("_CLASSNAME", null);
if (className == null) {
@ -248,7 +267,7 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
try {
Class<?> locClass = Class.forName(className);
ProgramLocation loc = (ProgramLocation) locClass.newInstance();
ProgramLocation loc = (ProgramLocation) locClass.getConstructor().newInstance();
loc.restoreState(program, saveState);
if (loc.getAddress() != null) {
return loc;
@ -260,23 +279,20 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
catch (ClassNotFoundException e) {
// not sure why we are ignoring this--if you know, then please let everyone else know
}
catch (InstantiationException e) {
Msg.showError(ProgramLocation.class, null, "Programming Error",
"Class " + className + " must have default constructor!", e);
}
catch (IllegalAccessException e) {
catch (InstantiationException | IllegalAccessException | NoSuchMethodException e) {
Msg.showError(ProgramLocation.class, null, "Programming Error",
"Class " + className + " must have public default constructor!", e);
}
catch (InvocationTargetException e) {
Msg.showError(ProgramLocation.class, null, "Programming Error",
"Class " + className + " default constructor threw an exception!", e);
}
return null;
}
@Override
public int hashCode() {
if (addr == null) {
return 0;
}
return addr.hashCode();
return Objects.hash(program, addr);
}
@Override
@ -323,6 +339,9 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
return false;
}
ProgramLocation other = (ProgramLocation) obj;
if (program != other.program) {
return false;
}
if (compareAddr(addr, other.addr) != 0) {
return false;
}
@ -343,7 +362,7 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
if (other == this) {
return 0;
}
int result = ProgramLocationComparator.instance.compare(this, other);
int result = ProgramLocationComparator.INSTANCE.compare(this, other);
if (result == 0) {
result = row - other.row;
if (result == 0) {
@ -419,6 +438,7 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
/**
* Returns true if this location represents a valid location in the given program
*
* @param testProgram the program to test if this location is valid.
* @return true if this location represents a valid location in the given program
*/
@ -428,6 +448,7 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
/**
* Returns the row within the program location.
*
* @return the row within the program location.
*/
public int getRow() {
@ -436,14 +457,15 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
/**
* Returns the character offset in the display item at the (row,col)
* @return the character offset in the display item at the (row,col)
*
* @return the character offset in the display item at the (row,col)
*/
public int getCharOffset() {
return charOffset;
}
/**
* Returns the column index of the display piece represented by this location. For most
* Returns the column index of the display piece represented by this location. For most
* locations, there is only one display item per row, in which case this value will be 0.
*/
public int getColumn() {

View file

@ -15,20 +15,33 @@
*/
package ghidra.program.util;
import java.util.HashMap;
import java.util.Map;
import java.util.*;
public class ProgramLocationComparator {
import ghidra.program.model.listing.Program;
/**
* A comparator for the common fields of {@link ProgramLocation}
*
* <p>
* This comparator only compares the program, address, and class of the program location. To compare
* at greater granularity, invoke the {@link ProgramLocation#compareTo(ProgramLocation)} method, or
* use the natural ordering. Each particular type of location uses this comparator, and then
* compares the more detailed fields, if necessary. If this comparator indicates equality, then the
* two locations are definitely of the same class.
*/
public class ProgramLocationComparator implements Comparator<ProgramLocation> {
/** The singleton instance */
public static final ProgramLocationComparator INSTANCE = new ProgramLocationComparator();
private static final Class<?>[] PROGRAM_LOCATION_CLASSES = {
DividerLocation.class, ProgramLocation.class, PlateFieldLocation.class, FunctionLocation.class,
FunctionRepeatableCommentFieldLocation.class, FunctionSignatureFieldLocation.class,
FunctionSignatureSourceFieldLocation.class, FunctionCallFixupFieldLocation.class,
FunctionReturnTypeFieldLocation.class, FunctionCallingConventionFieldLocation.class,
FunctionNameFieldLocation.class, FunctionStartParametersFieldLocation.class,
FunctionParameterFieldLocation.class, FunctionParameterNameFieldLocation.class,
FunctionEndParametersFieldLocation.class, VariableLocation.class,
VariableTypeFieldLocation.class, VariableNameFieldLocation.class,
DividerLocation.class, ProgramLocation.class, PlateFieldLocation.class,
FunctionLocation.class, FunctionRepeatableCommentFieldLocation.class,
FunctionSignatureFieldLocation.class, FunctionSignatureSourceFieldLocation.class,
FunctionCallFixupFieldLocation.class, FunctionReturnTypeFieldLocation.class,
FunctionCallingConventionFieldLocation.class, FunctionNameFieldLocation.class,
FunctionStartParametersFieldLocation.class, FunctionParameterFieldLocation.class,
FunctionParameterNameFieldLocation.class, FunctionEndParametersFieldLocation.class,
VariableLocation.class, VariableTypeFieldLocation.class, VariableNameFieldLocation.class,
VariableLocFieldLocation.class, VariableXRefFieldLocation.class,
VariableCommentFieldLocation.class,
@ -42,39 +55,58 @@ public class ProgramLocationComparator {
PostCommentFieldLocation.class,
SpaceFieldLocation.class, SpacerFieldLocation.class, SubDataFieldLocation.class,
RegisterFieldLocation.class, };
public static final ProgramLocationComparator instance = new ProgramLocationComparator();
RegisterFieldLocation.class,
};
private Map<Class<?>, Integer> priorityMap;
private ProgramLocationComparator() {
priorityMap = new HashMap<Class<?>, Integer>();
priorityMap = new HashMap<>();
for (int ordinal = 0; ordinal < PROGRAM_LOCATION_CLASSES.length; ordinal++) {
priorityMap.put(PROGRAM_LOCATION_CLASSES[ordinal], ordinal);
}
}
@Override
public int compare(ProgramLocation loc1, ProgramLocation loc2) {
int result = loc1.getAddress().compareTo(loc2.getAddress());
if (result == 0) {
Class<?> class1 = loc1.getClass();
Class<?> class2 = loc2.getClass();
if (class1 == class2) {
return 0;
}
Integer ordinal1 = priorityMap.get(class1);
Integer ordinal2 = priorityMap.get(class2);
if (ordinal1 == null && ordinal2 == null) {
return class1.getName().compareTo(class2.getName());
}
if (ordinal1 == null) {
return 1;
}
if (ordinal2 == null) {
return -1;
}
result = ordinal1.intValue() - ordinal2.intValue();
int result;
// Try to make a sensible comparison of programs before just using identity hashes
Program program1 = loc1.getProgram();
Program program2 = loc2.getProgram();
result = program1.getName().compareTo(program2.getName());
if (result != 0) {
return result;
}
return result;
result = Integer.compare(program1.hashCode(), program2.hashCode());
if (result != 0) {
return result;
}
result = loc1.getAddress().compareTo(loc2.getAddress());
if (result != 0) {
return result;
}
Class<?> class1 = loc1.getClass();
Class<?> class2 = loc2.getClass();
if (class1 == class2) {
return 0;
}
Integer ordinal1 = priorityMap.get(class1);
Integer ordinal2 = priorityMap.get(class2);
if (ordinal1 == null && ordinal2 == null) {
return class1.getName().compareTo(class2.getName());
}
if (ordinal1 == null) {
return 1;
}
if (ordinal2 == null) {
return -1;
}
result = Integer.compare(ordinal1.intValue(), ordinal2.intValue());
if (result != 0) {
return result;
}
return 0;
}
}