package org.springframework.roo.model;
import java.beans.Introspector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
/**
* Immutable representation of a Java field name, method name, or other common
* legal Java identifier.
* <p>
* Ensures the field is properly formed.
*
* @author Ben Alex
* @author Greg Turnquist
* @since 1.0
*/
public class JavaSymbolName implements Comparable<JavaSymbolName> {
/** Constant for keyword "false" */
public static final JavaSymbolName FALSE = new JavaSymbolName("false");
/** Constant for keyword "true" */
public static final JavaSymbolName TRUE = new JavaSymbolName("true");
/**
* Verifies the presented name is a valid Java name. Specifically, the
* following is enforced:
* <ul>
* <li>Textual content must be provided in the name</li>
* <li>Must not have any slashes in the name</li>
* <li>Must not start with a number</li>
* <li>Must not have any spaces or other illegal characters in the name</li>
* <li>Must not start or end with a period</li>
* </ul>
*
* @param name the name to evaluate (required)
*/
public static void assertJavaNameLegal(final String name) {
Validate.notNull(name, "Name required");
// Note regular expression for legal characters found to be x5 slower in
// profiling than this approach
final char[] value = name.toCharArray();
for (int i = 0; i < value.length; i++) {
final char c = value[i];
if ('/' == c || ' ' == c || '*' == c || '>' == c || '<' == c || '!' == c || '@' == c
|| '%' == c || '^' == c || '?' == c || '(' == c || ')' == c || '~' == c || '`' == c
|| '{' == c || '}' == c || '[' == c || ']' == c || '|' == c || '\\' == c || '\'' == c
|| '+' == c || '-' == c) {
throw new IllegalArgumentException("Illegal name '" + name + "' (illegal character)");
}
if (i == 0) {
if ('1' == c || '2' == c || '3' == c || '4' == c || '5' == c || '6' == c || '7' == c
|| '8' == c || '9' == c || '0' == c) {
throw new IllegalArgumentException("Illegal name '" + name
+ "' (cannot start with a number)");
}
}
if (i + 1 == value.length || i == 0) {
if ('.' == c) {
throw new IllegalArgumentException("Illegal name '" + name
+ "' (cannot start or end with a period)");
}
}
}
}
/**
* @return a camel case string in human readable form
*/
public static String getReadableSymbolName(final String camelCase) {
final Pattern p = Pattern.compile("[A-Z][^A-Z]*");
final Matcher m = p.matcher(StringUtils.capitalize(camelCase));
final StringBuilder builder = new StringBuilder();
while (m.find()) {
builder.append(m.group()).append(" ");
}
return builder.toString().trim();
}
/**
* Construct a Java symbol name which adheres to the strict JavaBean naming
* conventions and avoids use of {@link ReservedWords} by suffixing '_'
*
* @param javaType the {@link JavaType} for which the symbol name is created
* @return a Java symbol name adhering to JavaBean conventions and avoids
* reserved words
* @since 1.2.0
*/
public static JavaSymbolName getReservedWordSafeName(final JavaType javaType) {
final String simpleTypeName = javaType.getSimpleTypeName();
String str = Introspector.decapitalize(StringUtils.capitalize(simpleTypeName));
while (ReservedWords.RESERVED_JAVA_KEYWORDS.contains(str)) {
// Prefixing can create names that don't work in the Derby DB
str += "_";
}
if (str.equals(simpleTypeName)) { // ROO-2929
str += "_";
}
return new JavaSymbolName(str);
}
/**
* Construct a Java symbol name which adheres to the strict JavaBean naming
* conventions and avoids use of {@link ReservedWords} by prefixing '_'
*
* @param javaType the {@link JavaType} for which the symbol name is created
* @return a Java symbol name adhering to JavaBean conventions and avoids
* reserved words
* @deprecated use {@link #getReservedWordSafeName(JavaType)} instead (does
* the same thing, just better named)
*/
@Deprecated
public static JavaSymbolName getReservedWordSaveName(final JavaType javaType) {
return getReservedWordSafeName(javaType);
}
public static boolean isLegalJavaName(final String name) {
try {
assertJavaNameLegal(name);
} catch (final IllegalArgumentException e) {
return false;
}
return true;
}
private final String symbolName;
/**
* Construct a Java symbol name.
* <p>
* The name will be enforced as follows:
* <ul>
* <li>The rules listed in {@link #assertJavaNameLegal(String)}
* </ul>
*
* @param symbolName the name
*/
public JavaSymbolName(final String symbolName) {
Validate.notBlank(symbolName, "Fully qualified type name required");
assertJavaNameLegal(symbolName);
this.symbolName = symbolName;
}
public int compareTo(final JavaSymbolName o) {
// NB: If adding more fields to this class ensure the equals(Object)
// method is updated accordingly
if (o == null) {
return -1;
}
return symbolName.compareTo(o.symbolName);
}
@Override
public boolean equals(final Object obj) {
// NB: Not using the normal convention of delegating to compareTo (for
// efficiency reasons)
return obj instanceof JavaSymbolName && symbolName.equals(((JavaSymbolName) obj).symbolName);
}
/**
* @return the symbol name in human readable form
*/
public String getReadableSymbolName() {
final String camelCase = symbolName;
return getReadableSymbolName(camelCase);
}
/**
* @return the symbol name (never null or empty)
*/
public String getSymbolName() {
return symbolName;
}
/**
* @return the symbol name, capitalising the first letter (never null or
* empty)
*/
public String getSymbolNameCapitalisedFirstLetter() {
return StringUtils.capitalize(symbolName);
}
/**
* @return the symbol name, uncapitalising the first letter (never null or
* empty)
*/
public String getSymbolNameUnCapitalisedFirstLetter() {
return StringUtils.uncapitalize(symbolName);
}
@Override
public int hashCode() {
return symbolName.hashCode();
}
@Override
public String toString() {
return symbolName;
}
}