001: /*
002: * The contents of this file are subject to the terms
003: * of the Common Development and Distribution License
004: * (the "License"). You may not use this file except
005: * in compliance with the License.
006: *
007: * You can obtain a copy of the license at
008: * https://jwsdp.dev.java.net/CDDLv1.0.html
009: * See the License for the specific language governing
010: * permissions and limitations under the License.
011: *
012: * When distributing Covered Code, include this CDDL
013: * HEADER in each file and include the License file at
014: * https://jwsdp.dev.java.net/CDDLv1.0.html If applicable,
015: * add the following below this CDDL HEADER, with the
016: * fields enclosed by brackets "[]" replaced with your
017: * own identifying information: Portions Copyright [yyyy]
018: * [name of copyright owner]
019: */
020:
021: package com.sun.codemodel;
022:
023: import java.util.HashSet;
024: import java.util.regex.Matcher;
025: import java.util.regex.Pattern;
026:
027: /**
028: * Utility methods that convert arbitrary strings into Java identifiers.
029: */
030: public class JJavaName {
031:
032: /**
033: * Checks if a given string is usable as a Java identifier.
034: */
035: public static boolean isJavaIdentifier(String s) {
036: if (s.length() == 0)
037: return false;
038: if (reservedKeywords.contains(s))
039: return false;
040:
041: if (!Character.isJavaIdentifierStart(s.charAt(0)))
042: return false;
043:
044: for (int i = 1; i < s.length(); i++)
045: if (!Character.isJavaIdentifierPart(s.charAt(i)))
046: return false;
047:
048: return true;
049: }
050:
051: /**
052: * Checks if the given string is a valid fully qualified name.
053: */
054: public static boolean isFullyQualifiedClassName(String s) {
055: return isJavaPackageName(s);
056: }
057:
058: /**
059: * Checks if the given string is a valid Java package name.
060: */
061: public static boolean isJavaPackageName(String s) {
062: while (s.length() != 0) {
063: int idx = s.indexOf('.');
064: if (idx == -1)
065: idx = s.length();
066: if (!isJavaIdentifier(s.substring(0, idx)))
067: return false;
068:
069: s = s.substring(idx);
070: if (s.length() != 0)
071: s = s.substring(1); // remove '.'
072: }
073: return true;
074: }
075:
076: /**
077: * <b>Experimental API:</b> converts an English word into a plural form.
078: *
079: * @param word
080: * a word, such as "child", "apple". Must not be null.
081: * It accepts word concatanation forms
082: * that are common in programming languages, such as "my_child", "MyChild",
083: * "myChild", "MY-CHILD", "CODE003-child", etc, and mostly tries to do the right thing.
084: * ("my_children","MyChildren","myChildren", and "MY-CHILDREN", "CODE003-children" respectively)
085: * <p>
086: * Although this method only works for English words, it handles non-English
087: * words gracefully (by just returning it as-is.) For example, 日本語
088: * will be returned as-is without modified, not "日本語s"
089: * <p>
090: * This method doesn't handle suffixes very well. For example, passing
091: * "person56" will return "person56s", not "people56".
092: *
093: * @return
094: * always non-null.
095: */
096: public static String getPluralForm(String word) {
097: // remember the casing of the word
098: boolean allUpper = true;
099:
100: // check if the word looks like an English word.
101: // if we see non-ASCII characters, abort
102: for (int i = 0; i < word.length(); i++) {
103: char ch = word.charAt(i);
104: if (ch >= 0x80)
105: return word;
106:
107: // note that this isn't the same as allUpper &= Character.isUpperCase(ch);
108: allUpper &= !Character.isLowerCase(ch);
109: }
110:
111: for (Entry e : TABLE) {
112: String r = e.apply(word);
113: if (r != null) {
114: if (allUpper)
115: r = r.toUpperCase();
116: return r;
117: }
118: }
119:
120: // failed
121: return word;
122: }
123:
124: /** All reserved keywords of Java. */
125: private static HashSet<String> reservedKeywords = new HashSet<String>();
126:
127: static {
128: // see http://java.sun.com/docs/books/tutorial/java/nutsandbolts/_keywords.html
129: String[] words = new String[] { "abstract", "boolean", "break",
130: "byte", "case", "catch", "char", "class", "const",
131: "continue", "default", "do", "double", "else",
132: "extends", "final", "finally", "float", "for", "goto",
133: "if", "implements", "import", "instanceof", "int",
134: "interface", "long", "native", "new", "package",
135: "private", "protected", "public", "return", "short",
136: "static", "strictfp", "super", "switch",
137: "synchronized", "this", "throw", "throws", "transient",
138: "try", "void", "volatile", "while",
139:
140: // technically these are not reserved words but they cannot be used as identifiers.
141: "true", "false", "null",
142:
143: // and I believe assert is also a new keyword
144: "assert",
145:
146: // and 5.0 keywords
147: "enum" };
148: for (String w : words)
149: reservedKeywords.add(w);
150: }
151:
152: private static class Entry {
153: private final Pattern pattern;
154: private final String replacement;
155:
156: public Entry(String pattern, String replacement) {
157: this .pattern = Pattern.compile(pattern,
158: Pattern.CASE_INSENSITIVE);
159: this .replacement = replacement;
160: }
161:
162: String apply(String word) {
163: Matcher m = pattern.matcher(word);
164: if (m.matches()) {
165: StringBuffer buf = new StringBuffer();
166: m.appendReplacement(buf, replacement);
167: return buf.toString();
168: } else {
169: return null;
170: }
171: }
172: }
173:
174: private static final Entry[] TABLE;
175:
176: static {
177: String[] source = { "(.*)child", "$1children", "(.+)fe",
178: "$1ves", "(.*)mouse", "$1mise", "(.+)f", "$1ves",
179: "(.+)ch", "$1ches", "(.+)sh", "$1shes", "(.*)tooth",
180: "$1teeth", "(.+)um", "$1a", "(.+)an", "$1en",
181: "(.+)ato", "$1atoes", "(.*)basis", "$1bases",
182: "(.*)axis", "$1axes", "(.+)is", "$1ises", "(.+)us",
183: "$1uses", "(.+)s", "$1s", "(.*)foot", "$1feet",
184: "(.+)ix", "$1ixes", "(.+)ex", "$1ices", "(.+)nx",
185: "$1nxes", "(.+)x", "$1xes", "(.+)y", "$1ies", "(.+)",
186: "$1s", };
187:
188: TABLE = new Entry[source.length / 2];
189:
190: for (int i = 0; i < source.length; i += 2) {
191: TABLE[i / 2] = new Entry(source[i], source[i + 1]);
192: }
193: }
194: }
|