/*
* JBoss, Home of Professional Open Source.
*
* See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing.
*
* See the AUTHORS.txt file distributed with this work for a full listing of individual contributors.
*/
package org.teiid.designer.core.validation.rules;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.teiid.core.designer.ModelerCoreRuntimeException;
import org.teiid.core.designer.util.CoreArgCheck;
import org.teiid.core.designer.util.CoreStringUtil;
import org.teiid.designer.core.ModelerCore;
/**
* StringNameValidator
*
* @since 8.0
*/
public class StringNameValidator {
public static final char UNDERSCORE_CHARACTER = '_';
public static final char[] DEFAULT_VALID_NON_LETTER_OR_DIGIT_CHARS = {UNDERSCORE_CHARACTER};
public static final char DEFAULT_REPLACEMENT_CHARACTER = UNDERSCORE_CHARACTER;
public static final int MAXIMUM_LENGTH = Integer.MAX_VALUE;
public static final int DEFAULT_MAXIMUM_LENGTH = 255;
public static final int DEFAULT_MINIMUM_LENGTH = 1;
public static final boolean DEFAULT_CASE_SENSITIVE_NAME_COMPARISON = false;
private static final Integer INTEGER_ONE = new Integer(1);
private final int maximumLength;
private final int minimumLength;
private final boolean caseSensitive;
private final char replacementCharacter;
private final char[] validNonLetterOrDigitChars;
private final ExistingNames existingNames;
/**
* Construct an instance of StringNameValidator.
*/
public StringNameValidator( final int minLength,
final int maxLength,
final boolean caseSensitive,
final char replacementCharacter,
final char[] validNonLetterOrDigitChars ) {
super();
this.minimumLength = minLength < 0 ? DEFAULT_MINIMUM_LENGTH : minLength;
this.maximumLength = maxLength < 0 ? MAXIMUM_LENGTH : maxLength;
this.caseSensitive = caseSensitive;
this.replacementCharacter = replacementCharacter;
if (this.minimumLength > this.maximumLength) {
final String msg = ModelerCore.Util.getString("StringNameValidator.The_minimum_length_may_not_exceed_the_maximum_length"); //$NON-NLS-1$
throw new IllegalArgumentException(msg);
}
if (validNonLetterOrDigitChars == null) {
this.validNonLetterOrDigitChars = DEFAULT_VALID_NON_LETTER_OR_DIGIT_CHARS;
} else {
this.validNonLetterOrDigitChars = validNonLetterOrDigitChars;
Arrays.sort(this.validNonLetterOrDigitChars);
}
this.existingNames = new ExistingNames(this.caseSensitive);
}
/**
* @param name the name to add to the existing name collection (should not be <code>null</code>)
*/
public boolean addExistingName(String name) {
return this.existingNames.add(name);
}
/**
* Clears the existing name collection.
*/
public void clearExistingNames() {
this.existingNames.clear();
}
/**
* Construct an instance of StringNameValidator.
*/
public StringNameValidator( final boolean caseSensitive ) {
this(DEFAULT_MINIMUM_LENGTH, DEFAULT_MAXIMUM_LENGTH, caseSensitive, DEFAULT_REPLACEMENT_CHARACTER, null);
}
/**
* Construct an instance of StringNameValidator.
*/
public StringNameValidator( final char[] validNonLetterOrDigitChars ) {
this(DEFAULT_MINIMUM_LENGTH, DEFAULT_MAXIMUM_LENGTH, DEFAULT_CASE_SENSITIVE_NAME_COMPARISON,
DEFAULT_REPLACEMENT_CHARACTER, validNonLetterOrDigitChars);
}
/**
* Construct an instance of StringNameValidator.
*/
public StringNameValidator( final int maxLength,
final char[] validNonLetterOrDigitChars ) {
this(DEFAULT_MINIMUM_LENGTH, maxLength, DEFAULT_CASE_SENSITIVE_NAME_COMPARISON, DEFAULT_REPLACEMENT_CHARACTER,
validNonLetterOrDigitChars);
}
/**
* Construct an instance of StringNameValidator.
*/
public StringNameValidator( final int minLength,
final int maxLength,
final char[] validNonLetterOrDigitChars ) {
this(minLength, maxLength, DEFAULT_CASE_SENSITIVE_NAME_COMPARISON, DEFAULT_REPLACEMENT_CHARACTER, validNonLetterOrDigitChars);
}
/**
* Construct an instance of StringNameValidator.
*/
public StringNameValidator( final int minLength,
final int maxLength,
final boolean caseSensitive,
final char replacementCharacter ) {
this(minLength, maxLength, caseSensitive, replacementCharacter, null);
}
/**
* Construct an instance of StringNameValidator.
*/
public StringNameValidator( final int minLength,
final int maxLength,
final boolean caseSensitive ) {
this(minLength, maxLength, caseSensitive, DEFAULT_REPLACEMENT_CHARACTER, null);
}
/**
* Construct an instance of StringNameValidator.
*/
public StringNameValidator( final int minLength,
final int maxLength ) {
this(minLength, maxLength, DEFAULT_CASE_SENSITIVE_NAME_COMPARISON, DEFAULT_REPLACEMENT_CHARACTER, null);
}
/**
* Construct an instance of StringNameValidator.
*/
public StringNameValidator( final int maxLength ) {
this(DEFAULT_MINIMUM_LENGTH, maxLength, DEFAULT_CASE_SENSITIVE_NAME_COMPARISON, DEFAULT_REPLACEMENT_CHARACTER, null);
}
/**
* Construct an instance of StringNameValidator.
*/
public StringNameValidator() {
this(DEFAULT_MINIMUM_LENGTH, DEFAULT_MAXIMUM_LENGTH, DEFAULT_CASE_SENSITIVE_NAME_COMPARISON,
DEFAULT_REPLACEMENT_CHARACTER, null);
}
/**
* @return
*/
public boolean isCaseSensitive() {
return caseSensitive;
}
/**
* @return
*/
public int getMaximumLength() {
return maximumLength;
}
/**
* @return
*/
public int getMinimumLength() {
return minimumLength;
}
/**
* @return
*/
public char getReplacementCharacter() {
return replacementCharacter;
}
/**
* Check whether the name length is between {@link #getMinimumLength()} and {@link #getMaximumLength()} (inclusive).
*
* @param name the name to check; may not be null
* @return a message stating what is wrong with the name, or null if the name is considered valid
*/
public String checkNameLength( final String name ) {
CoreArgCheck.isNotNull(name);
final int strLength = name.length();
if (strLength < getMinimumLength()) {
final Object[] params = new Object[] {new Integer(getMinimumLength())};
final String msg = ModelerCore.Util.getString("StringNameValidator.MinLengthFailure", params); //$NON-NLS-1$
return msg;
// check the entity name length agaist a desired value
} else if (strLength > getMaximumLength()) {
final Object[] params = new Object[] {new Integer(strLength), new Integer(getMaximumLength())};
final String msg = ModelerCore.Util.getString("StringNameValidator.The_name_length_({0})_is_longer_than_allowed_({1})", params); //$NON-NLS-1$
return msg;
}
// Valid, so return no error message
return null;
}
/**
* Check whether the characters in the name are considered valid. The first character must be an alphabetic character;
* remaining characters must be either alphabetic characters, digits or the underscore ('_') character. Any characters that
* are in the {@link #getInvalidCharacters() invalid set} will also fail validation
*
* @param name the name to be checked; may not be null
* @return a message stating what is wrong with the name, or null if the name is considered valid
*/
public String checkNameCharacters( final String name ) {
CoreArgCheck.isNotNull(name);
// Go through the string and ensure that each character is valid ...
int length = name.length();
if (length == 0) {
return null;
}
char c = name.charAt(0);
String msg = null;
boolean isDoubleQuoted = false;
if( !CoreStringUtil.isDoubleQuoted(name) ) {
msg = isValidInitialChar(c);
} else if( length > 1 ) {
isDoubleQuoted = true;
msg = isValidInitialChar(name.charAt(1));
}
if (msg != null) {
return msg;
}
for (int index = 1; index < length; index++) {
c = name.charAt(index);
if( isDoubleQuoted ) {
if( index < length-1) {
msg = isValidCharInDoubleQuotes(c, index);
}
} else {
msg = isValidChar(c, index);
}
if (msg != null) {
return msg;
}
}
// Valid, so return no error message
return null;
}
protected String isValidChar(char c, int index) {
if ( !Character.isLetter(c)) {
if (!Character.isDigit(c) && !isValidNonLetterOrDigit(c) ) {
final Object[] params = new Object[] {new Character(c), new Integer(index+1), getValidNonLetterOrDigitMessageSuffix()};
return ModelerCore.Util.getString("StringNameValidator.The_character___{0}___(at_position_{1})_is_not_allowed;_only_alphabetic,_digit_or_underscore", params); //$NON-NLS-1$
}
}
return null;
}
protected String isValidCharInDoubleQuotes(char c, int index) {
if ( !Character.isLetter(c)) {
if (c != CoreStringUtil.Constants.DOT_CHAR &&
!Character.isDigit(c) &&
!isValidNonLetterOrDigit(c) ) {
final Object[] params = new Object[] {new Character(c), new Integer(index+1), getValidNonLetterOrDigitMessageSuffix()};
return ModelerCore.Util.getString("StringNameValidator.The_character___{0}___(at_position_{1})_is_not_allowed;_only_alphabetic,_digit_or_underscore", params); //$NON-NLS-1$
}
}
return null;
}
protected String isValidInitialChar(char c) {
if (!Character.isLetter(c)) {
final Object[] params = new Object[] {new Character(c)};
final String msg = ModelerCore.Util.getString("StringNameValidator.The_first_character_of_the_name_({0})_must_be_an_alphabetic_character", params); //$NON-NLS-1$
return msg;
}
return null;
}
/**
* Allows additional non-letter or non-digit characters to be valid. Subclasses should override this method to add
* additional valid characters.
* @param c
* @return true if valid character
*/
public boolean isValidNonLetterOrDigit(char c) {
return Arrays.binarySearch(validNonLetterOrDigitChars, c) >= 0;
}
public String getValidNonLetterOrDigitMessageSuffix() {
return ModelerCore.Util.getString("StringNameValidator.or_other_valid_characters"); //$NON-NLS-1$
}
/**
* This method returns whether the specified name consists of valid characters, and does <i>not include whether it is valid in
* its namespace</i>. The following are the rules that are used by this method: <li>The name may not be null</li> <li>The name
* may not be zero-length</li> <li>The name may not have a length greater than 255 characters</li> <li>The first character
* must be an alphabetic character; remaining characters must be either alphabetic characters, digits or the underscore ('_')
* character</li>
*
* @param newName the name being considered
* @return true if the name is a valid name (excluding context-sensitive naming rules), or false otherwise.
*/
public boolean isValidName( final String name ) {
final String reasonInvalid = checkValidName(name);
if (reasonInvalid != null) {
return false;
}
return true;
}
/**
* This method returns whether the specified name consists of valid characters, and does <i>not include whether it is valid in
* its namespace</i>. The following are the rules that are used by this method:
* <ul>
* <li>The name may not be null</li>
* <li>The name may must have a length that is equal to or greater than {@link #getMinimumLength()}</li>
* <li>The name may must have a length that is equal to or less than {@link #getMaximumLength()}</li>
* <li>The first character must be an alphabetic character; remaining characters must be either alphabetic characters, digits
* or the underscore ('_') character</li>
* <li>The name may not contain {@link #getInvalidCharacters() invalid characters}</li>
* </ul>
*
* @param name the name being considered
* @return a message which is a validation error, null if the name is valid.
*/
public String checkValidName( final String name ) {
// The name may not be null
if (name == null) {
final String msg = ModelerCore.Util.getString("StringNameValidator.The_name_may_not_be_null"); //$NON-NLS-1$
return msg;
}
// Check the length of the name ...
// the length is being seperately checked by a differrent method
// which is invoked by String length rule, need not be checked twice.
final String lengthMsg = checkNameLength(name);
if (lengthMsg != null) {
return lengthMsg;
}
// Check the characters in the name ...
final String contentMsg = checkNameCharacters(name);
if (contentMsg != null) {
return contentMsg;
}
// If it passed all of the tests ...
return null;
}
/**
* Checks if the given name is a unique EObject within its container.
*
* @param name The name that should be unique within its container
* @param eObject The EObject to validate
* @param nameFeatureID the ID of the feature that represents the name feature
* @return a message stating what is wrong with the name, or null if the name is considered valid
*/
public String checkUniqueness( final String name,
final EObject eObject,
final List siblings,
final int nameFeatureID ) {
CoreArgCheck.isNotNull(name);
CoreArgCheck.isNotNull(eObject);
CoreArgCheck.isNotNull(siblings);
// get the metamodel URI for the eObject
final String eObjUri = eObject.eClass().getEPackage().getNsURI();
// check all the contents of the container
final Iterator iter = siblings.iterator();
int matchCntr = 0;
while (iter.hasNext()) {
final EObject sibling = (EObject)iter.next();
// Process this sibling EXCEPT if the same object
if (sibling != eObject) {
final EClass siblingClass = sibling.eClass();
final String siblingURI = siblingClass.getEPackage().getNsURI();
// if sibling and th eObject belong to the same metamodel
if (eObjUri.equals(siblingURI)) {
final EStructuralFeature eFeature = siblingClass.getEStructuralFeature(nameFeatureID);
// If the specified feature exists for this EObject
if (eFeature != null) {
final String siblingName = (String)sibling.eGet(eFeature);
// Increment the counter whenever a child has the specified name
if (siblingName != null) {
if ((isCaseSensitive() && siblingName.equals(name))
|| (!isCaseSensitive() && siblingName.equalsIgnoreCase(name))) {
matchCntr++;
}
}
}
}
}
}
// if there is at least one match, create a problem
if (matchCntr != 0) {
final Object params = new Object[] {name, new Integer(matchCntr)};
final String msg = ModelerCore.Util.getString("StringNameValidator.The_name_{0}_is_the_same_as_{1}_other_objects_under_the_same_parent", params); //$NON-NLS-1$
return msg;
}
// No duplicates ...
return null;
}
/**
* Get a map of sibling to the number of other siblings its name would match.
*
* @param siblings List of all the siblings
* @param nameFeatureID the ID of the feature that represents the name feature
*/
public Map getDuplicateNamesMap( final List siblings,
final int nameFeatureID ) {
CoreArgCheck.isNotNull(siblings);
// ---------------------------------------------
// Defect 22095 - needed to improve validation performance
// Old method was finding the "Name" values inside the for() loops.
// This is expensive and didn't need to be that way.
// Basically the change does everything BUT do a check on Metamodel URI
// OLD CODE = if(siblingURI1.equals(siblingURI2)) but Dennis said we shouldn't need it because the nameFeatureID is being
// passed in.
// ---------------------------------------------
// Let's create a Map of sibling names to EObject for only those names that have this feature
Map siblingNameMap = getSiblingsNameFeatureMap(siblings, nameFeatureID);
Map objectCountMap = new HashMap();
List siblingEObjects = new ArrayList(siblingNameMap.keySet());
// check all the contents of the container
int siblingSize = siblingEObjects.size();
for (int i = 0; i < siblingSize; i++) {
final EObject siblingA = (EObject)siblingEObjects.get(i);
final String siblingNameA = (String)siblingNameMap.get(siblingA);
for (int j = i + 1; j < siblingSize; j++) {
final EObject siblingB = (EObject)siblingEObjects.get(j);
final String siblingNameB = (String)siblingNameMap.get(siblingB);
if ((isCaseSensitive() && siblingNameA.equals(siblingNameB))
|| (!isCaseSensitive() && siblingNameA.equalsIgnoreCase(siblingNameB))) {
Integer matchCnt1 = (Integer)objectCountMap.get(siblingA);
Integer matchCnt2 = (Integer)objectCountMap.get(siblingB);
matchCnt1 = matchCnt1 == null ? INTEGER_ONE : new Integer(matchCnt1.intValue() + 1);
matchCnt2 = matchCnt2 == null ? INTEGER_ONE : new Integer(matchCnt2.intValue() + 1);
objectCountMap.put(siblingA, matchCnt1);
objectCountMap.put(siblingB, matchCnt2);
}
}
}
return objectCountMap;
}
private Map getSiblingsNameFeatureMap( final List siblings,
final int nameFeatureID ) {
int siblingSize = siblings.size();
Map siblingNameMap = new HashMap();
for (int i = 0; i < siblingSize; i++) {
final EObject siblingEObject = (EObject)siblings.get(i);
final EClass siblingClass = siblingEObject.eClass();
final EStructuralFeature eFeature = siblingClass.getEStructuralFeature(nameFeatureID);
// check if feature exists
if (eFeature != null) {
final Object featureValue = siblingEObject.eGet(eFeature);
// check it the feature value is a string
if (featureValue != null && featureValue instanceof String) {
siblingNameMap.put(siblingEObject, featureValue);
}
}
}
return siblingNameMap;
}
/**
* Get a map of feature to the number of other features its name would match.
*
* @param features List of all the features
*/
public Map getDuplicateNamesMap( final List features ) {
CoreArgCheck.isNotNull(features);
Map objectCountMap = new HashMap();
// check all the contents of the container
int featuresSize = features.size();
for (int i = 0; i < featuresSize; i++) {
final EStructuralFeature eFeature1 = (EStructuralFeature)features.get(i);
// check if feature exists
if (eFeature1 != null) {
final String featureName1 = eFeature1.getName();
for (int j = i + 1; j < featuresSize; j++) {
final EStructuralFeature eFeature2 = (EStructuralFeature)features.get(j);
// check if feature exists
if (eFeature2 != null) {
final String featureName2 = eFeature2.getName();
// Increment the matchcounter whenever another feature has the specified name
if (featureName2 != null) {
if ((isCaseSensitive() && featureName1.equals(featureName2))
|| (!isCaseSensitive() && featureName1.equalsIgnoreCase(featureName2))) {
Integer matchCnt1 = (Integer)objectCountMap.get(eFeature1);
Integer matchCnt2 = (Integer)objectCountMap.get(eFeature2);
matchCnt1 = matchCnt1 == null ? INTEGER_ONE : new Integer(matchCnt1.intValue() + 1);
matchCnt2 = matchCnt2 == null ? INTEGER_ONE : new Integer(matchCnt2.intValue() + 1);
objectCountMap.put(eFeature1, matchCnt1);
objectCountMap.put(eFeature2, matchCnt2);
}
}
}
}
}
}
return objectCountMap;
}
/**
* This method modifies the value of name property of an model entity if it is invalid, and makes it valid. Currently, it
* checks whether the entity's is fixed , contains invalid characters, or conflicts with a sibling's name. The name value
* before the change is set as the alias property.
* <p>
* This is equivalent to calling {@link #createValidName(String, char[], int, boolean) createValidName(name,null,255,false)}.
* </p>
*
* @param name the name; may not be null
* @return the new name, or null if the name was already valid (i.e., would be unchanged by this method)
*/
public String createValidName( final String name ) {
return createValidName(name, false);
}
/**
* This method modifies the value of name property of an model entity if it is invalid, and makes it valid. Currently, it
* checks whether the entity's is fixed , contains invalid characters, or conflicts with a sibling's name. The name value
* before the change is set as the alias property.
* <p>
* This is equivalent to calling {@link #createValidName(String, char[], int, boolean)
* createValidName(name,null,255,performValidityCheck)}.
* </p>
*
* @param name the name; may not be null
* @param performValidityCheck true if validity checking should be performed, or false if the validity checking should be
* skipped
* @return the new name, or null if the name was already valid (i.e., would be unchanged by this method)
*/
public String createValidName( final String name,
final boolean performValidityCheck ) {
CoreArgCheck.isNotNull(name);
// Otherwise, the name is presumed to be invalid ...
StringBuffer newName = new StringBuffer(name.length());
boolean changed = false;
int initLength = name.length();
final int maxLength = Math.min(initLength, getMaximumLength());
int actualLength = 0;
if (initLength > 0) {
// Go through the string and ensure that each character is valid ...
boolean foundInitialChar = false;
int index = 0;
for( char nextChar : name.toCharArray()) {
index++;
if( !foundInitialChar ) {
String msg = isValidInitialChar(nextChar);
if (msg == null) {
foundInitialChar = true;
changed = true;
actualLength++;
newName.append(nextChar);
} else {
changed = true;
}
} else {
if( actualLength < maxLength ) {
String msg = isValidChar(nextChar, index);
if (msg != null) {
changed = true;
actualLength++;
newName.append(this.getReplacementCharacter());
} else {
actualLength++;
newName.append(nextChar);
}
} else {
break;
}
}
}
}
// if (length > 0) {
// char c = name.charAt(0);
//
// String msg = isValidInitialChar(c);
//
// if (msg != null) {
// changed = true;
// newName.setCharAt(0, this.getReplacementCharacter());
// }
// for (int index = 1; index < maxLength; index++) {
// c = name.charAt(index);
// msg = isValidChar(c, index);
// if (msg != null) {
// changed = true;
// newName.setCharAt(index, this.getReplacementCharacter());
// }
// }
// }
while (newName.length() < getMinimumLength()) {
changed = true;
newName.append(this.getReplacementCharacter());
}
if (newName.length() > maxLength) {
changed = true;
newName.delete(maxLength, newName.length());
}
if (changed) {
return newName.toString();
}
// Valid, so return no error message
return null;
}
/**
* Create a valid name that is does not match the supplied set of "existing" names.
*
* @param name the name to be made valid; may not be null
* @return the new name, or null if the name was already valid (i.e., would be unchanged by this method)
*/
public String createValidUniqueName( final String name ) {
String result = null;
// Create a valid name ...
String validName = createValidName(name);
if (validName == null) validName = name; // Already was valid ...
else result = validName;
// Create a unique name ...
final String uniqueName = createUniqueName(validName);
if (uniqueName != null) {
// Was not unique ...
result = uniqueName;
}
// Return the result; may be null if name was already valid and unique
return result;
}
/**
* Create a name that is does not match the supplied set of "existing" names.
*
* @param name the name to be made valid; may not be null
* @return the new name, or null if the name was already unique (i.e., would be unchanged by this method)
*/
public String createUniqueName(final String name) {
CoreArgCheck.isNotNull(name);
// Compute the counter at which we have to start taking away characters ...
final int roomForCounterChars = Math.max(0, this.getMaximumLength() - name.length());
final int counterToStartRemoving = ((int)Math.pow(10, roomForCounterChars)) - 1;
String theName = name;
boolean changed = false;
int counter = 0;
while (true) {
if (addExistingName(theName)) {
// It is unique, so return ...
return (changed ? theName : null);
}
// The name is not unique, so compute a new one ...
++counter;
// First check the length ...
final int length = name.length();
if (counter > counterToStartRemoving) {
// Must make room for the counter ...
int numCharsToRemove = 0;
if (counter - counterToStartRemoving < 10) {
numCharsToRemove = 1;
} else if (counter - counterToStartRemoving < 100) {
numCharsToRemove = 2;
} else if (counter - counterToStartRemoving < 1000) {
numCharsToRemove = 3;
} else if (counter - counterToStartRemoving < 10000) {
numCharsToRemove = 4;
} else if (counter - counterToStartRemoving < 100000) {
numCharsToRemove = 5;
} else {
numCharsToRemove = length + 1; // will force a failure
}
// See if there are enough characters to remove ...
if (length > numCharsToRemove) {
theName = name.substring(0, length - numCharsToRemove) + counter;
changed = true;
} else {
final Object[] params = new Object[] {name};
final String msg = ModelerCore.Util.getString("StringNameValidator.Unable_to_make_the_name_{0}_unique_within_the_limits_of_the_maximum_length", params); //$NON-NLS-1$
throw new ModelerCoreRuntimeException(msg);
}
} else {
// Simply append the counter ...
changed = true;
theName = name + counter;
}
}
}
class Node implements Comparable {
final char content;
boolean marker;
private TreeMap<Character, Node> kids;
Node( char c ) {
this.content = c;
}
/**
* {@inheritDoc}
*
* @see java.lang.Comparable#compareTo(java.lang.Object)
*/
@Override
public int compareTo( Object obj ) {
Node that = (Node)obj;
if (this.content == that.content) {
return 0;
}
return (this.content - that.content);
}
/**
* {@inheritDoc}
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals( Object obj ) {
if (this == obj) {
return true;
}
if ((obj == null) || !getClass().equals(obj.getClass())) {
return false;
}
return (this.content == ((Node)obj).content);
}
void clearChildren() {
if (this.kids != null) {
this.kids.clear();
}
}
TreeMap getChildren() {
if (this.kids == null) {
this.kids = new TreeMap<Character, Node>();
}
return this.kids;
}
boolean hasChildren() {
return ((this.kids != null) && !this.kids.isEmpty());
}
/**
* {@inheritDoc}
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
return Character.valueOf(this.content).hashCode();
}
Node subNode( char c ) {
if (this.kids == null) {
return null;
}
return this.kids.get(c);
}
}
private class ExistingNames {
private final boolean caseSensitive;
private Node root;
public ExistingNames( boolean caseSensitive ) {
this.caseSensitive = caseSensitive;
this.root = new Node(' ');
}
public boolean add( String name ) {
boolean added = false;
if (!this.caseSensitive) {
name = name.toUpperCase();
}
Node current = this.root;
for (char c : name.toCharArray()) {
Node child = current.subNode(c);
if (child == null) {
Node newKid = new Node(c);
current.getChildren().put(c, newKid);
current = newKid;
added = true;
} else {
current = child;
}
}
if (added || (current.marker == false)) {
// set marker to indicate end of a name
current.marker = true;
added = true;
}
return added;
}
private void buildNodeNameList( String name,
Node node,
StringBuilder txt ) {
// add if node is an existing name
if (node.marker) {
txt.append(name).append(',');
}
// process children
for (Object obj : node.getChildren().values()) {
Node kid = (Node)obj;
buildNodeNameList(name + kid.content, kid, txt);
}
}
public void clear() {
this.root.clearChildren();
}
/**
* {@inheritDoc}
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
StringBuilder txt = new StringBuilder();
buildNodeNameList("", this.root, txt); //$NON-NLS-1$
return txt.substring(0, txt.length() - 1).toString(); // remove last comma
}
}
//
// private static String getUniqueString(String name, Collection siblingNames) {
// Iterator siblingIter = siblingNames.iterator();
// while(siblingIter.hasNext()) {
// String sibName = (String) siblingIter.next();
// if(sibName.equalsIgnoreCase(name)) {
// char lastChar = name.charAt(name.length()-1);
// int intValue = 0;
// if(Character.isDigit(lastChar)) {
// intValue = Character.getNumericValue(lastChar);
// name = name.substring(0, name.length()-1) + intValue;
// } else {
// intValue++;
// name = name + intValue;
// }
// name = getUniqueString(name, siblingNames);
// }
// }
//
// return name;
// }
}