/* * 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; } }