/*
* Copyright (c) 2006-2008, Dennis M. Sosnoski All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
* following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following
* disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of
* JiBX nor the names of its contributors may be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.jibx.schema.codegen;
import java.util.ArrayList;
import java.util.HashSet;
/**
* Utility methods for working with Java names.
*
* @author Dennis M. Sosnoski
*/
public class NameConverter
{
/** Reserved words for Java (keywords and literals). */
private static final HashSet s_reservedWords = new HashSet();
static {
// keywords
s_reservedWords.add("abstract");
s_reservedWords.add("assert");
s_reservedWords.add("boolean");
s_reservedWords.add("break");
s_reservedWords.add("byte");
s_reservedWords.add("case");
s_reservedWords.add("catch");
s_reservedWords.add("char");
s_reservedWords.add("class");
s_reservedWords.add("const");
s_reservedWords.add("continue");
s_reservedWords.add("default");
s_reservedWords.add("do");
s_reservedWords.add("double");
s_reservedWords.add("else");
s_reservedWords.add("enum");
s_reservedWords.add("extends");
s_reservedWords.add("final");
s_reservedWords.add("finally");
s_reservedWords.add("float");
s_reservedWords.add("for");
s_reservedWords.add("goto");
s_reservedWords.add("if");
s_reservedWords.add("implements");
s_reservedWords.add("import");
s_reservedWords.add("instanceof");
s_reservedWords.add("int");
s_reservedWords.add("interface");
s_reservedWords.add("long");
s_reservedWords.add("native");
s_reservedWords.add("new");
s_reservedWords.add("package");
s_reservedWords.add("private");
s_reservedWords.add("protected");
s_reservedWords.add("public");
s_reservedWords.add("return");
s_reservedWords.add("short");
s_reservedWords.add("static");
s_reservedWords.add("strictfp");
s_reservedWords.add("super");
s_reservedWords.add("switch");
s_reservedWords.add("synchronized");
s_reservedWords.add("this");
s_reservedWords.add("throw");
s_reservedWords.add("throws");
s_reservedWords.add("transient");
s_reservedWords.add("try");
s_reservedWords.add("void");
s_reservedWords.add("volatile");
s_reservedWords.add("while");
// literals
s_reservedWords.add("true");
s_reservedWords.add("false");
s_reservedWords.add("null");
}
/** Camelcase field names flag. */
private boolean m_camelCase;
/** Prefix used for normal field names (non-null
, may be empty). */
private String m_fieldPrefix;
/** Suffix used for normal field names (non-null
, may be empty). */
private String m_fieldSuffix;
/** Prefix used for static field names (non-null
, may be empty). */
private String m_staticPrefix;
/** Suffix used for static field names (non-null
, may be empty). */
private String m_staticSuffix;
/**
* Use underscores in field names flag (as substitute for special characters, and to split words).
*/
private boolean m_underscore;
/** Uppercase initial letter of field names flag. */
private boolean m_upperInitial;
/** Set of XML name prefixes to be discarded in conversions. */
private String[] m_discardPrefixSet;
/** Set of XML name suffixes to be discarded in conversions. */
private String[] m_discardSuffixSet;
/** Reusable array for words in name. */
private ArrayList m_wordList;
/**
* Constructor.
*/
public NameConverter() {
m_fieldPrefix = "";
m_fieldSuffix = "";
m_staticPrefix = "";
m_staticSuffix = "";
m_camelCase = true;
m_discardPrefixSet = new String[0];
if (ClassHolder.TRIM_COMMON_SUFFIXES) {
m_discardSuffixSet = new String[] { "Type", "Group", "Union" };
} else {
m_discardSuffixSet = new String[0];
}
m_wordList = new ArrayList();
}
/**
* Check if a name is reserved in Java.
*
* @param name
* @return is reserved
*/
public static boolean isReserved(String name) {
return s_reservedWords.contains(name);
}
/**
* Get prefix text for normal field names.
*
* @return field prefix (non-null
, may be empty)
*/
public String getFieldPrefix() {
return m_fieldPrefix;
}
/**
* Set prefix text for normal field names.
*
* @param pref field prefix (non-null
, may be empty)
*/
public void setFieldPrefix(String pref) {
m_fieldPrefix = pref;
}
/**
* Get suffix text for normal field names.
*
* @return field suffix (non-null
, may be empty)
*/
public String getFieldSuffix() {
return m_fieldPrefix;
}
/**
* Set suffix text for normal field names.
*
* @param suff field suffix (non-null
, may be empty)
*/
public void setFieldSuffix(String suff) {
m_fieldPrefix = suff;
}
/**
* Get prefix text for static field names.
*
* @return field prefix (non-null
, may be empty)
*/
public String getStaticPrefix() {
return m_staticPrefix;
}
/**
* Set prefix text for static field names.
*
* @param pref field prefix (non-null
, may be empty)
*/
public void setStaticPrefix(String pref) {
m_staticPrefix = pref;
}
/**
* Get suffix text for static field names.
*
* @return field suffix (non-null
, may be empty)
*/
public String getStaticSuffix() {
return m_staticPrefix;
}
/**
* Set suffix text for static field names.
*
* @param suff field suffix (non-null
, may be empty)
*/
public void setStaticSuffix(String suff) {
m_staticPrefix = suff;
}
/**
* Get the prefixes to be stripped when converting XML names.
*
* @return prefixes
*/
public String[] getDiscardPrefixSet() {
return m_discardPrefixSet;
}
/**
* Set the prefixes to be stripped when converting XML names.
*
* @param prefixes
*/
public void setDiscardPrefixSet(String[] prefixes) {
m_discardPrefixSet = prefixes;
}
/**
* Get the suffixes to be stripped when converting XML names.
*
* @return suffixes
*/
public String[] getDiscardSuffixSet() {
return m_discardSuffixSet;
}
/**
* Set the suffixes to be stripped when converting XML names.
*
* @param suffixes
*/
public void setDiscardSuffixSet(String[] suffixes) {
m_discardSuffixSet = suffixes;
}
/**
* Trim specified prefixes and/or suffixes from an XML name.
*
* @param xname XML name
* @return trimmed name, with specified prefixes and/or suffixes removed
*/
public String trimXName(String xname) {
// first trim off a prefix on name
for (int i = 0; i < m_discardPrefixSet.length; i++) {
String prefix = m_discardPrefixSet[i];
if (xname.startsWith(prefix) && xname.length() > prefix.length()) {
xname = xname.substring(prefix.length());
break;
}
}
// next trim off a suffix on name
for (int i = 0; i < m_discardSuffixSet.length; i++) {
String suffix = m_discardSuffixSet[i];
if (xname.endsWith(suffix) && xname.length() > suffix.length()) {
xname = xname.substring(0, xname.length() - suffix.length());
break;
}
}
return xname;
}
/**
* Split an XML name into words. This splits first on the basis of separator characters ('.', '-', and '_') in the
* name, and secondly based on case (an uppercase character immediately followed by one or more lowercase characters
* is considered a word, and multiple uppercase characters not followed immediately by a lowercase character are
* also considered a word). Characters which are not valid as parts of identifiers in Java are dropped from the XML
* name before it is split, and words starting with initial uppercase characters have the upper case dropped for
* consistency. Note that this method is not threadsafe.
*
* @param name
* @return array of words
*/
public String[] splitXMLWords(String name) {
// start by finding a valid start character
int offset = 0;
while (!Character.isJavaIdentifierStart(name.charAt(offset))) {
if (++offset >= name.length()) {
return new String[0];
}
}
// accumulate the list of words
StringBuffer word = new StringBuffer();
m_wordList.clear();
boolean lastupper = false;
while (offset < name.length()) {
// check next character of name
char chr = name.charAt(offset++);
if (chr == '-' || chr == '.' || chr == '_') {
// force split at each splitting character
if (word.length() > 0) {
m_wordList.add(word.toString());
word.setLength(0);
}
} else if (Character.isJavaIdentifierPart(chr)) {
// check case of valid identifier part character
if (Character.isUpperCase(chr)) {
if (!lastupper && word.length() > 0) {
// upper after lower, split before upper
m_wordList.add(word.toString());
word.setLength(0);
}
lastupper = true;
} else {
if (lastupper) {
if (word.length() > 1) {
// multiple uppers followed by lower, split before last
int split = word.length() - 1;
m_wordList.add(word.substring(0, split));
char start = Character.toLowerCase(word.charAt(split));
word.setLength(0);
word.append(start);
} else if (word.length() > 0) {
// single upper followed by lower, convert upper to lower
word.setCharAt(0, Character.toLowerCase(word.charAt(0)));
}
}
lastupper = false;
}
word.append(chr);
}
}
if (word.length() > 0) {
m_wordList.add(word.toString());
}
// return array of words
return (String[])m_wordList.toArray(new String[m_wordList.size()]);
}
/**
* Convert a raw package name to a legal Java package name. The raw package name must be in standard package name
* form, with periods separating the individual directory components of the package name.
*
* @param raw basic package name, which may include illegal characters
* @return sanitized package name
*/
public String sanitizePackageName(String raw) {
StringBuffer buff = new StringBuffer(raw.length());
boolean first = true;
for (int i = 0; i < raw.length();) {
char chr = buff.charAt(i);
if (first) {
if (Character.isJavaIdentifierStart(chr)) {
first = false;
i++;
} else {
buff.deleteCharAt(i);
}
} else if (chr == '.') {
first = true;
i++;
} else if (!Character.isJavaIdentifierPart(chr)) {
buff.deleteCharAt(i);
} else {
i++;
}
}
return buff.toString();
}
/**
* Convert an XML name to a legal Java class name.
*
* @param xname XML name
* @return converted name
*/
public String toJavaClassName(String xname) {
// split trimed name into a series of words
String[] words = splitXMLWords(trimXName(xname));
if (words.length == 0) {
return "X";
}
// form name by concatenating words with initial uppercase
StringBuffer buff = new StringBuffer();
for (int i = 0; i < words.length; i++) {
String word = words[i];
buff.append(Character.toUpperCase(word.charAt(0)));
if (word.length() > 1) {
buff.append(word.substring(1, word.length()));
}
}
return buff.toString();
}
/**
* Convert a word to a name component. If the supplied word starts with a lowercase letter, this converts it to
* upper case.
*
* @param word
* @return word with uppercase initial letter
*/
public static String toNameWord(String word) {
char chr = word.charAt(0);
if (Character.isLowerCase(chr)) {
StringBuffer buff = new StringBuffer(word);
buff.setCharAt(0, Character.toUpperCase(chr));
return buff.toString();
} else {
return word;
}
}
/**
* Convert a word to a leading name component. If the supplied word starts with an uppercase letter which is not
* followed by another uppercase letter, this converts the initial uppercase to lowercase.
*
* @param word
* @return word with lowercase initial letter
*/
public static String toNameLead(String word) {
char chr = word.charAt(0);
if (Character.isUpperCase(chr) && (word.length() < 2 || Character.isLowerCase(word.charAt(1)))) {
StringBuffer buff = new StringBuffer(word);
buff.setCharAt(0, Character.toLowerCase(chr));
return buff.toString();
} else {
return word;
}
}
/**
* Convert an XML name to a Java value base name. The base name is guaranteed not to match a Java keyword, and is in
* normalized camelcase form with leading lower case (unless the first word of the name is all uppercase).
*
* @param xname XML name
* @return converted name
*/
public String toBaseName(String xname) {
// split trimed name into a series of words
String[] words = splitXMLWords(trimXName(xname));
if (words.length == 0) {
return "x";
}
// use single underscore for empty result
if (words.length == 0) {
words = new String[] { "_" };
}
// form name by concatenating words with configured style
StringBuffer buff = new StringBuffer();
buff.append(m_fieldPrefix);
for (int i = 0; i < words.length; i++) {
String word = words[i];
if (i > 0 && m_underscore) {
buff.append('_');
}
if ((i == 0 && m_upperInitial) || (i > 0 && m_camelCase)) {
buff.append(Character.toUpperCase(word.charAt(0)));
if (word.length() > 1) {
buff.append(word.substring(1, word.length()));
}
} else {
buff.append(word);
}
}
buff.append(m_fieldSuffix);
// add leading underscore if match to reserved word
String fname = buff.toString();
if (s_reservedWords.contains(fname)) {
fname = "_" + fname;
}
return fname;
}
/**
* Convert text to constant name.
*
* @param text raw text to be converted
* @return constant name
*/
public String toConstantName(String text) {
// insert underscores to separate words of name, and convert invalid characters
StringBuffer buff = new StringBuffer(text.length());
boolean lastup = false;
boolean multup = false;
char lastchar = 0;
for (int index = 0; index < text.length(); index++) {
char chr = text.charAt(index);
if (index == 0 && !Character.isJavaIdentifierStart(chr)) {
buff.append('_');
}
if (Character.isJavaIdentifierPart(chr)) {
if (lastup) {
if (Character.isUpperCase(chr)) {
multup = true;
} else {
lastup = false;
if (chr == '_') {
multup = false;
} else if (multup) {
buff.insert(buff.length()-1, '_');
multup = false;
}
}
} else if (Character.isUpperCase(chr)) {
if (index > 0 && lastchar != '_') {
buff.append('_');
}
lastup = true;
multup = false;
}
lastchar = Character.toUpperCase(chr);
buff.append(lastchar);
}
}
return buff.toString();
}
/**
* Build a field name using supplied prefix and/or suffix.
*
* @param base normalized camelcase base name
* @param prefix text to be added at start of name
* @param suffix text to be added at end of name
* @return field name
*/
private String buildFieldName(String base, String prefix, String suffix) {
// check for any change needed
int added = prefix.length() + suffix.length();
boolean toupper = m_upperInitial && !Character.isUpperCase(base.charAt(0));
if (added == 0) {
// not adding prefix or suffix, check for case conversion
if (toupper) {
// convert first character to uppercase
StringBuffer buff = new StringBuffer(base);
buff.setCharAt(0, Character.toUpperCase(buff.charAt(0)));
return buff.toString();
} else {
// no change needed, just use base name directly
return base;
}
} else {
// append prefix and/or suffix, with case conversion if needed
StringBuffer buff = new StringBuffer(base.length() + added);
buff.append(prefix);
int offset = buff.length();
buff.append(base);
if (toupper) {
buff.setCharAt(offset, Character.toUpperCase(base.charAt(0)));
}
buff.append(suffix);
return buff.toString();
}
}
/**
* Convert base name to normal field name.
*
* @param base normalized camelcase base name
* @return field name
*/
public String toFieldName(String base) {
String prefix = m_fieldPrefix;
String suffix = m_fieldSuffix;
return buildFieldName(base, prefix, suffix);
}
/**
* Convert base name to static field name.
*
* @param base normalized camelcase base name
* @return field name
*/
public String toStaticFieldName(String base) {
String prefix = m_staticPrefix;
String suffix = m_staticSuffix;
return buildFieldName(base, prefix, suffix);
}
/**
* Convert base name to property name (used for all method names). The property name is always in initial-upper
* camelcase form.
*
* @param base normalized camelcase base name
* @return property name in initial-upper camelcase form
*/
public String toPropertyName(String base) {
if (!Character.isUpperCase(base.charAt(0))) {
// convert first character to uppercase
StringBuffer buff = new StringBuffer(base);
buff.setCharAt(0, Character.toUpperCase(buff.charAt(0)));
return buff.toString();
} else {
// no change needed, just use base name directly
return base;
}
}
/**
* Convert property name to read access method name.
*
* @param prop property name in initial-upper camelcase form
* @return read access method name
*/
public String toReadAccessMethodName(String prop) {
return "get" + prop;
}
/**
* Convert property name to write access method name.
*
* @param prop property name in initial-upper camelcase form
* @return write access method name
*/
public String toWriteAccessMethodName(String prop) {
return "set" + prop;
}
/**
* Convert property name to test access method name (for boolean value).
*
* @param prop property name in initial-upper camelcase form
* @return test access method name
*/
public String toTestAccessMethodName(String prop) {
return "is" + prop;
}
/**
* Convert property name to if set access method name (for value in set of alternatives).
*
* @param prop property name in initial-upper camelcase form
* @return if set access method name
*/
public String toIfSetAccessMethodName(String prop) {
return "if" + prop;
}
}