mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 02:39:44 +02:00
GT-2961 Escape forward slash delimiter in CategoryPath
This commit is contained in:
parent
1cc8de3e67
commit
ea88558241
5 changed files with 488 additions and 150 deletions
|
@ -57,13 +57,13 @@ class CategoryDB extends DatabaseObject implements Category {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
|
|
||||||
subcategoryMap = new LazyLoadingCachingMap<String, CategoryDB>(mgr.lock, CategoryDB.class) {
|
subcategoryMap = new LazyLoadingCachingMap<>(mgr.lock, CategoryDB.class) {
|
||||||
@Override
|
@Override
|
||||||
public Map<String, CategoryDB> loadMap() {
|
public Map<String, CategoryDB> loadMap() {
|
||||||
return buildSubcategoryMap();
|
return buildSubcategoryMap();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
dataTypeMap = new LazyLoadingCachingMap<String, DataType>(mgr.lock, DataType.class) {
|
dataTypeMap = new LazyLoadingCachingMap<>(mgr.lock, DataType.class) {
|
||||||
@Override
|
@Override
|
||||||
public Map<String, DataType> loadMap() {
|
public Map<String, DataType> loadMap() {
|
||||||
return createDataTypeMap();
|
return createDataTypeMap();
|
||||||
|
@ -273,9 +273,6 @@ class CategoryDB extends DatabaseObject implements Category {
|
||||||
if (categoryName == null || categoryName.length() == 0) {
|
if (categoryName == null || categoryName.length() == 0) {
|
||||||
throw new InvalidNameException("Name cannot be null or zero length");
|
throw new InvalidNameException("Name cannot be null or zero length");
|
||||||
}
|
}
|
||||||
if (categoryName.indexOf(DELIMITER_CHAR) >= 0) {
|
|
||||||
throw new InvalidNameException("Bad name: " + categoryName);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -23,12 +23,6 @@ import ghidra.util.task.TaskMonitor;
|
||||||
* Each data type resides in a given a category.
|
* Each data type resides in a given a category.
|
||||||
*/
|
*/
|
||||||
public interface Category extends Comparable<Category> {
|
public interface Category extends Comparable<Category> {
|
||||||
public static final char DELIMITER_CHAR = '/'; // delimeter between categories
|
|
||||||
|
|
||||||
public static final String NAME_DELIMITER = "/"; // delimiter between names
|
|
||||||
|
|
||||||
public static final String DELIMITER_STRING = "" + DELIMITER_CHAR; // delimeter between categories
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the name of this category.
|
* Get the name of this category.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.program.model.data;
|
package ghidra.program.model.data;
|
||||||
|
|
||||||
import java.util.StringTokenizer;
|
import java.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A category path is the full path to a particular data type
|
* A category path is the full path to a particular data type
|
||||||
|
@ -24,97 +24,211 @@ public class CategoryPath implements Comparable<CategoryPath> {
|
||||||
|
|
||||||
public static final char DELIMITER_CHAR = '/';
|
public static final char DELIMITER_CHAR = '/';
|
||||||
public static final String DELIMITER_STRING = "" + DELIMITER_CHAR;
|
public static final String DELIMITER_STRING = "" + DELIMITER_CHAR;
|
||||||
|
public static final String ESCAPED_DELIMITER_STRING = "\\" + DELIMITER_STRING;
|
||||||
|
|
||||||
public static final CategoryPath ROOT = new CategoryPath(null);
|
public static final CategoryPath ROOT = new CategoryPath();
|
||||||
|
|
||||||
private static final String ILLEGAL_STRING = DELIMITER_STRING + DELIMITER_STRING;
|
private static final String ILLEGAL_STRING = DELIMITER_STRING + DELIMITER_STRING;
|
||||||
|
private static final int DIFF = ESCAPED_DELIMITER_STRING.length() - DELIMITER_STRING.length();
|
||||||
|
|
||||||
private final String parentPath;
|
// parentPath can only be null for ROOT
|
||||||
|
private final CategoryPath parentPath;
|
||||||
private final String name;
|
private final String name;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a category path given a string.
|
* Converts a non-escaped String into an escaped string suitable for being passed in as a
|
||||||
|
* component of a single category path string to the constructor that takes a single
|
||||||
|
* escaped category path string. The user is responsible for constructing the single
|
||||||
|
* category path string from the escaped components.
|
||||||
|
* @param nonEscapedString String that might need escaping for characters used for delimiting
|
||||||
|
* @return escaped String
|
||||||
|
* @see #unescapeString(String)
|
||||||
|
*/
|
||||||
|
public static String escapeString(String nonEscapedString) {
|
||||||
|
return nonEscapedString.replace(DELIMITER_STRING, ESCAPED_DELIMITER_STRING);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts an escaped String suitable for being passed in as a component of a single category
|
||||||
|
* path string into an non-escaped string.
|
||||||
|
* @param escapedString String that might need unescaping for characters used for delimiting
|
||||||
|
* @return non-escaped String
|
||||||
|
* @see #escapeString(String)
|
||||||
|
*/
|
||||||
|
public static String unescapeString(String escapedString) {
|
||||||
|
return escapedString.replace(ESCAPED_DELIMITER_STRING, DELIMITER_STRING);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for internal creation of ROOT.
|
||||||
|
*/
|
||||||
|
private CategoryPath() {
|
||||||
|
// parentPath can only be null for ROOT
|
||||||
|
parentPath = null;
|
||||||
|
name = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a CategoryPath from a parent and a hierarchical array of strings where each
|
||||||
|
* string is the name of a category in the category path.
|
||||||
*
|
*
|
||||||
* @param path category path string.
|
* @param parentPathIn the parent CategoryPath. Choose {@code ROOT} if needed.
|
||||||
|
* @param categoryPath the array of names of categories.
|
||||||
|
* @throws IllegalArgumentException if the given array is null or empty.
|
||||||
|
*/
|
||||||
|
public CategoryPath(CategoryPath parentPathIn, String... categoryPath) {
|
||||||
|
this(parentPathIn, Arrays.asList(categoryPath));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a CategoryPath from a parent and a hierarchical list of strings where each
|
||||||
|
* string is the name of a category in the category path.
|
||||||
|
*
|
||||||
|
* @param parentPathIn the parent CategoryPath. Choose {@code ROOT} if needed.
|
||||||
|
* @param categoryList the hierarchical array of categories to place after parentPath.
|
||||||
|
* @throws IllegalArgumentException if the given list is null or empty.
|
||||||
|
*/
|
||||||
|
public CategoryPath(CategoryPath parentPathIn, List<String> categoryList) {
|
||||||
|
Objects.requireNonNull(parentPathIn);
|
||||||
|
if (categoryList == null || categoryList.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Category list must contain at least one string name!");
|
||||||
|
}
|
||||||
|
name = categoryList.get(categoryList.size() - 1);
|
||||||
|
if (categoryList.size() == 1) {
|
||||||
|
parentPath = parentPathIn;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
parentPath =
|
||||||
|
new CategoryPath(parentPathIn, categoryList.subList(0, categoryList.size() - 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a category path given a forward-slash-delimited string (e.g., {@code "/aa/bb"}).
|
||||||
|
* If an individual component has one or more '/' characters in it, then it can be
|
||||||
|
* <I><B>escaped</B></I> using the {@link #escapeString(String)} utility method. The
|
||||||
|
* {@link #unescapeString(String)} method can be used to unescape an individual component.
|
||||||
|
* <P>
|
||||||
|
* <B>Refrain</B> from using this constructor in production code, and instead use one of the
|
||||||
|
* other constructors that does not require escaping. Situations where using this constructor
|
||||||
|
* is OK is in simple cases where a literal is passed in, such as in testing methods or in
|
||||||
|
* scripts.
|
||||||
|
* @param path category path string, delimited with '/' characters where individual components
|
||||||
|
* may have '/' characters escaped.
|
||||||
*/
|
*/
|
||||||
public CategoryPath(String path) {
|
public CategoryPath(String path) {
|
||||||
if (path == null || path.length() == 0 || path.equals(DELIMITER_STRING)) {
|
if (path == null || path.length() == 0 || path.equals(DELIMITER_STRING)) {
|
||||||
this.parentPath = this.name = "";
|
// parentPath can only be null for ROOT
|
||||||
|
parentPath = null;
|
||||||
|
name = "";
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
else if (path.charAt(0) != DELIMITER_CHAR) {
|
else if (path.charAt(0) != DELIMITER_CHAR) {
|
||||||
throw new IllegalArgumentException("Paths must start with " + DELIMITER_STRING);
|
throw new IllegalArgumentException("Paths must start with " + DELIMITER_STRING);
|
||||||
}
|
}
|
||||||
else if (path.charAt(path.length() - 1) == DELIMITER_CHAR) {
|
else if (path.charAt(path.length() - 1) == DELIMITER_CHAR &&
|
||||||
|
path.lastIndexOf(ESCAPED_DELIMITER_STRING) != path.length() -
|
||||||
|
ESCAPED_DELIMITER_STRING.length()) {
|
||||||
throw new IllegalArgumentException("Paths must not end with " + DELIMITER_STRING);
|
throw new IllegalArgumentException("Paths must not end with " + DELIMITER_STRING);
|
||||||
}
|
}
|
||||||
else if (path.indexOf(ILLEGAL_STRING) >= 0) {
|
else if (path.indexOf(ILLEGAL_STRING) >= 0) {
|
||||||
throw new IllegalArgumentException("Paths must have non-empty elements");
|
throw new IllegalArgumentException("Paths must have non-empty elements");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
int index = path.lastIndexOf(DELIMITER_CHAR);
|
int escapedIndex = path.length();
|
||||||
this.parentPath = path.substring(0, index);
|
int delimiterIndex = path.length();
|
||||||
this.name = path.substring(index + 1);
|
while (delimiterIndex > 0) {
|
||||||
|
escapedIndex = path.lastIndexOf(ESCAPED_DELIMITER_STRING, escapedIndex - 1);
|
||||||
|
delimiterIndex = path.lastIndexOf(DELIMITER_CHAR, delimiterIndex - 1);
|
||||||
|
if (delimiterIndex != escapedIndex + DIFF) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.parentPath = new CategoryPath(path.substring(0, delimiterIndex));
|
||||||
|
this.name = unescapeString(path.substring(delimiterIndex + 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a category path given a parent category and name.
|
|
||||||
*
|
|
||||||
* @param parent parent category this path will reside in.
|
|
||||||
* @param name name of the category within the parent category.
|
|
||||||
*/
|
|
||||||
public CategoryPath(CategoryPath parent, String name) {
|
|
||||||
if (name == null || name.length() == 0 || name.indexOf(DELIMITER_CHAR) >= 0) {
|
|
||||||
throw new IllegalArgumentException("Bad name: " + name);
|
|
||||||
}
|
|
||||||
this.parentPath = parent.isRoot() ? "" : parent.getPath();
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if this category path corresponds to the root category
|
* Determine if this category path corresponds to the root category
|
||||||
* @return true if this is a root category path
|
* @return true if this is a root category path
|
||||||
*/
|
*/
|
||||||
public boolean isRoot() {
|
public boolean isRoot() {
|
||||||
return parentPath.length() == 0 && name.length() == 0;
|
// parentPath can only be null for ROOT
|
||||||
|
return parentPath == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the name of this category path
|
* Return the parent category path.
|
||||||
|
* @return the parent
|
||||||
|
*/
|
||||||
|
public CategoryPath getParent() {
|
||||||
|
return parentPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the terminating name of this category path.
|
||||||
|
* @return the name
|
||||||
*/
|
*/
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the full path to the category including the category name as a string.
|
* Return the {@link String} representation of this category path including the category name,
|
||||||
|
* where components are delimited with a forward slash. Any component that contains a forward
|
||||||
|
* slash will be have the forward slash characters escaped.
|
||||||
|
* @return the full category path
|
||||||
*/
|
*/
|
||||||
public String getPath() {
|
public String getPath() {
|
||||||
return parentPath + DELIMITER_CHAR + name;
|
if (isRoot()) {
|
||||||
}
|
return DELIMITER_STRING;
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the parent category path.
|
|
||||||
*/
|
|
||||||
public CategoryPath getParent() {
|
|
||||||
if (parentPath.length() == 0 && name.length() == 0) {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
return new CategoryPath(parentPath);
|
if (parentPath.isRoot()) {
|
||||||
|
return DELIMITER_CHAR + escapeString(name);
|
||||||
|
}
|
||||||
|
return parentPath.getPath() + DELIMITER_CHAR + escapeString(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
if (obj instanceof CategoryPath) {
|
if (this == obj) {
|
||||||
CategoryPath cp = (CategoryPath) obj;
|
return true;
|
||||||
return cp.parentPath.equals(parentPath) && cp.name.equals(name);
|
|
||||||
}
|
}
|
||||||
return false;
|
if (obj == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
CategoryPath other = (CategoryPath) obj;
|
||||||
|
if (name == null) {
|
||||||
|
if (other.name != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!name.equals(other.name)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (parentPath == null) {
|
||||||
|
if (other.parentPath != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!parentPath.equals(other.parentPath)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return parentPath.hashCode() + name.hashCode();
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result + ((name == null) ? 0 : name.hashCode());
|
||||||
|
result = prime * result + ((parentPath == null) ? 0 : parentPath.hashCode());
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -124,8 +238,9 @@ public class CategoryPath implements Comparable<CategoryPath> {
|
||||||
*/
|
*/
|
||||||
public boolean isAncestorOrSelf(CategoryPath categoryPath) {
|
public boolean isAncestorOrSelf(CategoryPath categoryPath) {
|
||||||
|
|
||||||
// Result categoryPath This
|
// Result categoryPath This
|
||||||
// ------ --------------------- ------------------------
|
// ------ --------------------- ------------------------
|
||||||
|
// True / /
|
||||||
// True / /apple
|
// True / /apple
|
||||||
// False /apple /
|
// False /apple /
|
||||||
// True /apple /apple/sub
|
// True /apple /apple/sub
|
||||||
|
@ -137,39 +252,46 @@ public class CategoryPath implements Comparable<CategoryPath> {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isRoot()) {
|
CategoryPath path = this;
|
||||||
return false;
|
while (!path.isRoot()) {
|
||||||
|
if (categoryPath.equals(path)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
path = path.getParent();
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
String otherCategory = categoryPath.getPath();
|
|
||||||
String myCategory = getPath();
|
|
||||||
if (!myCategory.startsWith(otherCategory)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (myCategory.length() == otherCategory.length()) {
|
|
||||||
// categoryPath is the same as this
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return myCategory.charAt(otherCategory.length()) == DELIMITER_CHAR;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns array of names in category path.
|
||||||
|
* @return array of names
|
||||||
|
*/
|
||||||
public String[] getPathElements() {
|
public String[] getPathElements() {
|
||||||
StringTokenizer tokenizer = new StringTokenizer(getPath(), DELIMITER_STRING);
|
return asArray();
|
||||||
String[] tokens = new String[tokenizer.countTokens()];
|
|
||||||
for (int i = 0; i < tokens.length; i++) {
|
|
||||||
tokens[i] = tokenizer.nextToken();
|
|
||||||
}
|
|
||||||
return tokens;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see java.lang.Comparable#compareTo(java.lang.Object)
|
* @see java.lang.Comparable#compareTo(java.lang.Object)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public int compareTo(CategoryPath otherPath) {
|
public int compareTo(CategoryPath other) {
|
||||||
return getPath().compareTo(otherPath.getPath());
|
CategoryPath otherParentPath = other.getParent();
|
||||||
|
int result = 0;
|
||||||
|
if (parentPath == null) {
|
||||||
|
if (otherParentPath != null) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (otherParentPath == null) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
result = parentPath.compareTo(otherParentPath);
|
||||||
|
}
|
||||||
|
if (result == 0) {
|
||||||
|
result = name.compareTo(other.getName());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
|
@ -180,4 +302,51 @@ public class CategoryPath implements Comparable<CategoryPath> {
|
||||||
return getPath();
|
return getPath();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a hierarchical list of names of the categories in the category path, starting with
|
||||||
|
* the name just below the {@code ROOT} category.
|
||||||
|
*
|
||||||
|
* @return a hierarchical list of names of the category in the category path.
|
||||||
|
*/
|
||||||
|
public List<String> asList() {
|
||||||
|
List<String> list = new ArrayList<>();
|
||||||
|
addToList(list);
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a hierarchical array of names of the categories in the category path, starting with
|
||||||
|
* the name just below the {@code ROOT} category.
|
||||||
|
*
|
||||||
|
* @return a hierarchical array of names of the categories in the category path.
|
||||||
|
*/
|
||||||
|
public String[] asArray() {
|
||||||
|
List<String> list = new ArrayList<>();
|
||||||
|
addToList(list);
|
||||||
|
return list.toArray(new String[list.size()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addToList(List<String> list) {
|
||||||
|
if (!parentPath.isRoot()) {
|
||||||
|
parentPath.addToList(list);
|
||||||
|
}
|
||||||
|
list.add(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This constructor is purposefully private and asserting. We do not want anyone to implement
|
||||||
|
* this constructor, as it would confuse the notion of the constructor that takes a single
|
||||||
|
* {@link String} that has escaped delimiters vs. what this constructor would have to require,
|
||||||
|
* which is non-escaped {@link String Strings}. There would be no way for the two Constructors
|
||||||
|
* to be distinguished from each other when passing a single argument.
|
||||||
|
*
|
||||||
|
* @param categoryPath varargs hierarchical list of names of categories.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unused") // for categoryPath varargs
|
||||||
|
private CategoryPath(String... categoryPath) {
|
||||||
|
assert false;
|
||||||
|
parentPath = null;
|
||||||
|
name = "";
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,19 +39,20 @@ public class DataTypePath {
|
||||||
* @param dataTypeName the name of the datatype.
|
* @param dataTypeName the name of the datatype.
|
||||||
* @throws IllegalArgumentException if a null category path or dataTypeName is given.
|
* @throws IllegalArgumentException if a null category path or dataTypeName is given.
|
||||||
*/
|
*/
|
||||||
public DataTypePath(CategoryPath categoryPath, String datatypeName) {
|
public DataTypePath(CategoryPath categoryPath, String dataTypeName) {
|
||||||
if (datatypeName == null || categoryPath == null) {
|
if (dataTypeName == null || categoryPath == null) {
|
||||||
throw new IllegalArgumentException("null not allowed for categoryPath or datatypeName");
|
throw new IllegalArgumentException("null not allowed for categoryPath or datatypeName");
|
||||||
}
|
}
|
||||||
this.categoryPath = categoryPath;
|
this.categoryPath = categoryPath;
|
||||||
this.dataTypeName = datatypeName;
|
this.dataTypeName = dataTypeName;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the categoryPath for the datatype represented by this datatype path.
|
* Returns the categoryPath for the datatype represented by this datatype path.
|
||||||
* (ie. the CategoryPath that contains the DataType that this DataTypePath points to).
|
* (ie. the CategoryPath that contains the DataType that this DataTypePath points to).
|
||||||
*
|
*
|
||||||
* @return the parent {@link CategoryPath} of the {@link DataType} that this DataTypePath points to.
|
* @return the parent {@link CategoryPath} of the {@link DataType} that this DataTypePath
|
||||||
|
* points to.
|
||||||
*/
|
*/
|
||||||
public CategoryPath getCategoryPath() {
|
public CategoryPath getCategoryPath() {
|
||||||
return categoryPath;
|
return categoryPath;
|
||||||
|
@ -70,6 +71,7 @@ public class DataTypePath {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the name of the datatype.
|
* Returns the name of the datatype.
|
||||||
|
* @return the name
|
||||||
*/
|
*/
|
||||||
public String getDataTypeName() {
|
public String getDataTypeName() {
|
||||||
return dataTypeName;
|
return dataTypeName;
|
||||||
|
@ -79,6 +81,7 @@ public class DataTypePath {
|
||||||
* Returns the full path of this datatype. NOTE: if the datatype name contains any
|
* Returns the full path of this datatype. NOTE: if the datatype name contains any
|
||||||
* "/" characters, then the resulting path string may be ambiguous as to where the
|
* "/" characters, then the resulting path string may be ambiguous as to where the
|
||||||
* category path ends and the datatype name begins.
|
* category path ends and the datatype name begins.
|
||||||
|
* @return the full path
|
||||||
*/
|
*/
|
||||||
public String getPath() {
|
public String getPath() {
|
||||||
StringBuffer buf = new StringBuffer(categoryPath.getPath());
|
StringBuffer buf = new StringBuffer(categoryPath.getPath());
|
||||||
|
|
|
@ -21,18 +21,17 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.program.model.data;
|
package ghidra.program.model.data;
|
||||||
|
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import org.junit.Assert;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import generic.test.AbstractGenericTest;
|
import generic.test.AbstractGenericTest;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* {@link CategoryPath} tests.
|
||||||
*
|
|
||||||
* To change the template for this generated type comment go to
|
|
||||||
* Window>Preferences>Java>Code Generation>Code and Comments
|
|
||||||
*/
|
*/
|
||||||
public class CategoryPathTest extends AbstractGenericTest {
|
public class CategoryPathTest extends AbstractGenericTest {
|
||||||
|
|
||||||
|
@ -41,109 +40,285 @@ public class CategoryPathTest extends AbstractGenericTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConstructor() {
|
public void testEscapeStringEmpty() {
|
||||||
|
String orig = "";
|
||||||
|
String escaped = CategoryPath.escapeString(orig);
|
||||||
|
String unescaped = CategoryPath.unescapeString(escaped);
|
||||||
|
assertEquals(orig, unescaped);
|
||||||
|
assertEquals("", escaped);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEscapeString1() {
|
||||||
|
String orig = "/";
|
||||||
|
String escaped = CategoryPath.escapeString(orig);
|
||||||
|
String unescaped = CategoryPath.unescapeString(escaped);
|
||||||
|
assertEquals(orig, unescaped);
|
||||||
|
assertEquals("\\/", escaped);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEscapeString2() {
|
||||||
|
String orig = "//";
|
||||||
|
String escaped = CategoryPath.escapeString(orig);
|
||||||
|
String unescaped = CategoryPath.unescapeString(escaped);
|
||||||
|
assertEquals(orig, unescaped);
|
||||||
|
assertEquals("\\/\\/", escaped);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConstructorRoot1() {
|
||||||
|
CategoryPath c = CategoryPath.ROOT;
|
||||||
|
assertEquals("/", c.getPath());
|
||||||
|
assertEquals("", c.getName());
|
||||||
|
assertTrue(c.isRoot());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConstructorRoot2() {
|
||||||
CategoryPath c = new CategoryPath(null);
|
CategoryPath c = new CategoryPath(null);
|
||||||
Assert.assertEquals("/", c.getPath());
|
assertEquals("/", c.getPath());
|
||||||
Assert.assertEquals("", c.getName());
|
assertEquals("", c.getName());
|
||||||
|
assertTrue(c.isRoot());
|
||||||
|
}
|
||||||
|
|
||||||
c = new CategoryPath("");
|
@Test
|
||||||
Assert.assertEquals("/", c.getPath());
|
public void testConstructorRoot3() {
|
||||||
Assert.assertEquals("", c.getName());
|
CategoryPath c = new CategoryPath("");
|
||||||
|
assertEquals("/", c.getPath());
|
||||||
|
assertEquals("", c.getName());
|
||||||
|
assertTrue(c.isRoot());
|
||||||
|
}
|
||||||
|
|
||||||
c = new CategoryPath("/");
|
@Test
|
||||||
Assert.assertEquals("/", c.getPath());
|
public void testConstructorRoot4() {
|
||||||
Assert.assertEquals("", c.getName());
|
CategoryPath c = new CategoryPath("/");
|
||||||
|
assertEquals("/", c.getPath());
|
||||||
|
assertEquals("", c.getName());
|
||||||
|
assertTrue(c.isRoot());
|
||||||
|
}
|
||||||
|
|
||||||
c = new CategoryPath("/apple");
|
@Test
|
||||||
Assert.assertEquals("/apple", c.getPath());
|
public void testConstructorBasicString1() {
|
||||||
Assert.assertEquals("apple", c.getName());
|
CategoryPath c = new CategoryPath("/apple");
|
||||||
|
assertEquals("/apple", c.getPath());
|
||||||
|
assertEquals("apple", c.getName());
|
||||||
|
}
|
||||||
|
|
||||||
c = new CategoryPath("/apple/pear");
|
@Test
|
||||||
Assert.assertEquals("/apple/pear", c.getPath());
|
public void testConstructorBasicString2() {
|
||||||
Assert.assertEquals("pear", c.getName());
|
CategoryPath c = new CategoryPath("/apple/pear");
|
||||||
|
assertEquals("/apple/pear", c.getPath());
|
||||||
|
assertEquals("pear", c.getName());
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
@Test
|
||||||
c = new CategoryPath("//");
|
public void testConstructorParentVarargsSingle() {
|
||||||
Assert.fail();
|
CategoryPath c = new CategoryPath("/apple/pear");
|
||||||
}
|
c = new CategoryPath(c, "mango");
|
||||||
catch (IllegalArgumentException e) {
|
assertEquals("/apple/pear/mango", c.getPath());
|
||||||
}
|
assertEquals("mango", c.getName());
|
||||||
try {
|
}
|
||||||
c = new CategoryPath("apple");
|
|
||||||
Assert.fail();
|
@Test
|
||||||
}
|
public void testConstructorParentAndList() {
|
||||||
catch (IllegalArgumentException e) {
|
CategoryPath parent = new CategoryPath("/universe/earth");
|
||||||
}
|
List<String> list = new ArrayList<>();
|
||||||
try {
|
list.add("boy");
|
||||||
c = new CategoryPath("/apple/");
|
list.add("bad");
|
||||||
Assert.fail();
|
CategoryPath c = new CategoryPath(parent, list);
|
||||||
}
|
assertEquals("/universe/earth/boy/bad", c.getPath());
|
||||||
catch (IllegalArgumentException e) {
|
assertEquals("bad", c.getName());
|
||||||
}
|
}
|
||||||
try {
|
|
||||||
c = new CategoryPath("/apple//bob");
|
@Test
|
||||||
Assert.fail();
|
public void testConstructorParentAndVarargsArray() {
|
||||||
}
|
CategoryPath parent = new CategoryPath("/apple/peaches");
|
||||||
catch (IllegalArgumentException e) {
|
CategoryPath c = new CategoryPath(parent, new String[] { "pumpkin", "pie" });
|
||||||
}
|
assertEquals("pie", c.getName());
|
||||||
|
c = c.getParent();
|
||||||
|
assertEquals("pumpkin", c.getName());
|
||||||
|
c = c.getParent();
|
||||||
|
assertEquals("peaches", c.getName());
|
||||||
|
c = c.getParent();
|
||||||
|
assertEquals("apple", c.getName());
|
||||||
|
c = c.getParent();
|
||||||
|
assertEquals("", c.getName());
|
||||||
|
assertTrue(c.isRoot());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConstructorParentAndVarargs() {
|
||||||
|
CategoryPath parent = new CategoryPath("/apple/peaches");
|
||||||
|
CategoryPath c = new CategoryPath(parent, "pumpkin", "pie");
|
||||||
|
assertEquals("pie", c.getName());
|
||||||
|
c = c.getParent();
|
||||||
|
assertEquals("pumpkin", c.getName());
|
||||||
|
c = c.getParent();
|
||||||
|
assertEquals("peaches", c.getName());
|
||||||
|
c = c.getParent();
|
||||||
|
assertEquals("apple", c.getName());
|
||||||
|
c = c.getParent();
|
||||||
|
assertEquals("", c.getName());
|
||||||
|
assertTrue(c.isRoot());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
@Test(expected = IllegalArgumentException.class)
|
||||||
public void testBadCtorParam_empty_path_element() {
|
public void testConstructorBadCtorParam_empty_path_element() {
|
||||||
new CategoryPath("//");
|
new CategoryPath("//");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
@Test(expected = IllegalArgumentException.class)
|
||||||
public void testBadCtorParam_empty_path_element_2() {
|
public void testConstructorBadCtorParam_empty_path_element_2() {
|
||||||
new CategoryPath("/apple//bob");
|
new CategoryPath("/apple//bob");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
@Test(expected = IllegalArgumentException.class)
|
||||||
public void testBadCtorParam_missing_leading_slash() {
|
public void testConstructorBadCtorParam_missing_leading_slash() {
|
||||||
new CategoryPath("apple");
|
new CategoryPath("apple");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
@Test(expected = IllegalArgumentException.class)
|
||||||
public void testBadCtorParam_bad_trailing_slash() {
|
public void testConstructorBadCtorParam_bad_trailing_slash() {
|
||||||
new CategoryPath("/apple/");
|
new CategoryPath("/apple/");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testOtherConstructor() {
|
|
||||||
CategoryPath a = new CategoryPath("/aaa");
|
|
||||||
CategoryPath b = new CategoryPath(a, "bbb");
|
|
||||||
Assert.assertEquals("/aaa/bbb", b.getPath());
|
|
||||||
Assert.assertEquals("bbb", b.getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetParent() {
|
public void testGetParent() {
|
||||||
CategoryPath c = new CategoryPath(null);
|
CategoryPath c = CategoryPath.ROOT;
|
||||||
assertNull(c.getParent());
|
assertNull(c.getParent());
|
||||||
|
|
||||||
c = new CategoryPath("/aaa/bbb/ccc");
|
c = new CategoryPath("/aaa/bbb/ccc");
|
||||||
c = c.getParent();
|
c = c.getParent();
|
||||||
Assert.assertEquals("/aaa/bbb", c.getPath());
|
assertEquals("/aaa/bbb", c.getPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testIsAncestor() {
|
public void testIsAncestorRootRoot() {
|
||||||
|
assertTrue(CategoryPath.ROOT.isAncestorOrSelf(CategoryPath.ROOT));
|
||||||
Assert.assertTrue(CategoryPath.ROOT.isAncestorOrSelf(CategoryPath.ROOT));
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIsAncestorRootApple() {
|
||||||
CategoryPath apple = new CategoryPath("/apple");
|
CategoryPath apple = new CategoryPath("/apple");
|
||||||
Assert.assertTrue(apple.isAncestorOrSelf(CategoryPath.ROOT));
|
assertTrue(apple.isAncestorOrSelf(CategoryPath.ROOT));
|
||||||
Assert.assertFalse(CategoryPath.ROOT.isAncestorOrSelf(apple));
|
assertFalse(CategoryPath.ROOT.isAncestorOrSelf(apple));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIsAncestorAppleSubApple() {
|
||||||
|
CategoryPath apple = new CategoryPath("/apple");
|
||||||
CategoryPath applesub = new CategoryPath("/apple/sub");
|
CategoryPath applesub = new CategoryPath("/apple/sub");
|
||||||
Assert.assertTrue(applesub.isAncestorOrSelf(apple));
|
assertTrue(applesub.isAncestorOrSelf(apple));
|
||||||
Assert.assertTrue(applesub.isAncestorOrSelf(applesub));
|
assertTrue(applesub.isAncestorOrSelf(applesub));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIsAncestorAppleSubNotApple() {
|
||||||
|
CategoryPath applesub = new CategoryPath("/apple/sub");
|
||||||
CategoryPath notapple = new CategoryPath("/notapple");
|
CategoryPath notapple = new CategoryPath("/notapple");
|
||||||
Assert.assertFalse(applesub.isAncestorOrSelf(notapple));
|
assertFalse(applesub.isAncestorOrSelf(notapple));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIsAncestorAppleSubApp() {
|
||||||
|
CategoryPath applesub = new CategoryPath("/apple/sub");
|
||||||
CategoryPath app = new CategoryPath("/app");
|
CategoryPath app = new CategoryPath("/app");
|
||||||
Assert.assertFalse(applesub.isAncestorOrSelf(app));
|
assertFalse(applesub.isAncestorOrSelf(app));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testToArray() {
|
||||||
|
CategoryPath path = new CategoryPath("/aaa/bbb/bob");
|
||||||
|
String[] names = path.asArray();
|
||||||
|
assertEquals("aaa", names[0]);
|
||||||
|
assertEquals("bbb", names[1]);
|
||||||
|
assertEquals("bob", names[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testToList() {
|
||||||
|
CategoryPath path = new CategoryPath("/aaa/bbb/bob");
|
||||||
|
List<String> names = path.asList();
|
||||||
|
assertEquals("aaa", names.get(0));
|
||||||
|
assertEquals("bbb", names.get(1));
|
||||||
|
assertEquals("bob", names.get(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConstructorDelimeterEscape1() {
|
||||||
|
CategoryPath path = new CategoryPath("/aaa/bbb/\\/bob");
|
||||||
|
List<String> names = path.asList();
|
||||||
|
assertEquals("aaa", names.get(0));
|
||||||
|
assertEquals("bbb", names.get(1));
|
||||||
|
assertEquals("/bob", names.get(2));
|
||||||
|
assertEquals("/aaa/bbb/\\/bob", path.getPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConstructorDelimeterEscape2() {
|
||||||
|
// Should not complain about terminating slash
|
||||||
|
CategoryPath path = new CategoryPath("/aaa/bbb/bob\\/");
|
||||||
|
List<String> names = path.asList();
|
||||||
|
assertEquals("aaa", names.get(0));
|
||||||
|
assertEquals("bbb", names.get(1));
|
||||||
|
assertEquals("bob/", names.get(2));
|
||||||
|
assertEquals("/aaa/bbb/bob\\/", path.getPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConstructorDelimeterEscape3() {
|
||||||
|
CategoryPath path = new CategoryPath("/\\/aaa/bbb/bob");
|
||||||
|
List<String> names = path.asList();
|
||||||
|
assertEquals("/aaa", names.get(0));
|
||||||
|
assertEquals("bbb", names.get(1));
|
||||||
|
assertEquals("bob", names.get(2));
|
||||||
|
assertEquals("/\\/aaa/bbb/bob", path.getPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConstructorDelimeterEscape4() {
|
||||||
|
CategoryPath path = new CategoryPath("/\\/aaa/bbb/bob");
|
||||||
|
List<String> names = path.asList();
|
||||||
|
assertEquals("/aaa", names.get(0));
|
||||||
|
assertEquals("bbb", names.get(1));
|
||||||
|
assertEquals("bob", names.get(2));
|
||||||
|
assertEquals("/\\/aaa/bbb/bob", path.getPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConstructorDelimeterEscape5() {
|
||||||
|
CategoryPath path = new CategoryPath("/\\/\\/aaa/bbb/bob");
|
||||||
|
List<String> names = path.asList();
|
||||||
|
assertEquals("//aaa", names.get(0));
|
||||||
|
assertEquals("bbb", names.get(1));
|
||||||
|
assertEquals("bob", names.get(2));
|
||||||
|
assertEquals("/\\/\\/aaa/bbb/bob", path.getPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public void testDelimeterEscapeAtRoot() {
|
||||||
|
CategoryPath path = new CategoryPath("\\//aaa/bbb/bob");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConstructorParentVarargsNestedDelimiter1() {
|
||||||
|
CategoryPath c = new CategoryPath("/apple/pear");
|
||||||
|
// nested delimiter sequence should be ignored on constructor and getName(), but output on
|
||||||
|
// getPath().
|
||||||
|
c = new CategoryPath(c, "man/go");
|
||||||
|
assertEquals("/apple/pear/man\\/go", c.getPath());
|
||||||
|
assertEquals("man/go", c.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConstructorParentVarargsNestedEscape1() {
|
||||||
|
CategoryPath c = new CategoryPath("/apple/pear");
|
||||||
|
// nested escape sequence should be ignored on constructor and getName(), but output on
|
||||||
|
// getPath().
|
||||||
|
c = new CategoryPath(c, "man\\/go");
|
||||||
|
assertEquals("/apple/pear/man\\\\/go", c.getPath());
|
||||||
|
assertEquals("man\\/go", c.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue