001: /*
002: * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package com.sun.codemodel.internal;
027:
028: import java.util.HashSet;
029: import java.util.regex.Matcher;
030: import java.util.regex.Pattern;
031:
032: /**
033: * Utility methods that convert arbitrary strings into Java identifiers.
034: */
035: public class JJavaName {
036:
037: /**
038: * Checks if a given string is usable as a Java identifier.
039: */
040: public static boolean isJavaIdentifier(String s) {
041: if (s.length() == 0)
042: return false;
043: if (reservedKeywords.contains(s))
044: return false;
045:
046: if (!Character.isJavaIdentifierStart(s.charAt(0)))
047: return false;
048:
049: for (int i = 1; i < s.length(); i++)
050: if (!Character.isJavaIdentifierPart(s.charAt(i)))
051: return false;
052:
053: return true;
054: }
055:
056: /**
057: * Checks if the given string is a valid Java package name.
058: */
059: public static boolean isJavaPackageName(String s) {
060: while (s.length() != 0) {
061: int idx = s.indexOf('.');
062: if (idx == -1)
063: idx = s.length();
064: if (!isJavaIdentifier(s.substring(0, idx)))
065: return false;
066:
067: s = s.substring(idx);
068: if (s.length() != 0)
069: s = s.substring(1); // remove '.'
070: }
071: return true;
072: }
073:
074: /**
075: * <b>Experimental API:</b> converts an English word into a plural form.
076: *
077: * @param word
078: * a word, such as "child", "apple". Must not be null.
079: * It accepts word concatanation forms
080: * that are common in programming languages, such as "my_child", "MyChild",
081: * "myChild", "MY-CHILD", "CODE003-child", etc, and mostly tries to do the right thing.
082: * ("my_children","MyChildren","myChildren", and "MY-CHILDREN", "CODE003-children" respectively)
083: * <p>
084: * Although this method only works for English words, it handles non-English
085: * words gracefully (by just returning it as-is.) For example, 日本語
086: * will be returned as-is without modified, not "日本語s"
087: * <p>
088: * This method doesn't handle suffixes very well. For example, passing
089: * "person56" will return "person56s", not "people56".
090: *
091: * @return
092: * always non-null.
093: */
094: public static String getPluralForm(String word) {
095: // remember the casing of the word
096: boolean allUpper = true;
097:
098: // check if the word looks like an English word.
099: // if we see non-ASCII characters, abort
100: for (int i = 0; i < word.length(); i++) {
101: char ch = word.charAt(i);
102: if (ch >= 0x80)
103: return word;
104:
105: // note that this isn't the same as allUpper &= Character.isUpperCase(ch);
106: allUpper &= !Character.isLowerCase(ch);
107: }
108:
109: for (Entry e : TABLE) {
110: String r = e.apply(word);
111: if (r != null) {
112: if (allUpper)
113: r = r.toUpperCase();
114: return r;
115: }
116: }
117:
118: // failed
119: return word;
120: }
121:
122: /** All reserved keywords of Java. */
123: private static HashSet<String> reservedKeywords = new HashSet<String>();
124:
125: static {
126: // see http://java.sun.com/docs/books/tutorial/java/nutsandbolts/_keywords.html
127: String[] words = new String[] { "abstract", "boolean", "break",
128: "byte", "case", "catch", "char", "class", "const",
129: "continue", "default", "do", "double", "else",
130: "extends", "final", "finally", "float", "for", "goto",
131: "if", "implements", "import", "instanceof", "int",
132: "interface", "long", "native", "new", "package",
133: "private", "protected", "public", "return", "short",
134: "static", "strictfp", "super", "switch",
135: "synchronized", "this", "throw", "throws", "transient",
136: "try", "void", "volatile", "while",
137:
138: // technically these are not reserved words but they cannot be used as identifiers.
139: "true", "false", "null",
140:
141: // and I believe assert is also a new keyword
142: "assert",
143:
144: // and 5.0 keywords
145: "enum" };
146: for (String w : words)
147: reservedKeywords.add(w);
148: }
149:
150: private static class Entry {
151: private final Pattern pattern;
152: private final String replacement;
153:
154: public Entry(String pattern, String replacement) {
155: this .pattern = Pattern.compile(pattern,
156: Pattern.CASE_INSENSITIVE);
157: this .replacement = replacement;
158: }
159:
160: String apply(String word) {
161: Matcher m = pattern.matcher(word);
162: if (m.matches()) {
163: StringBuffer buf = new StringBuffer();
164: m.appendReplacement(buf, replacement);
165: return buf.toString();
166: } else {
167: return null;
168: }
169: }
170: }
171:
172: private static final Entry[] TABLE;
173:
174: static {
175: String[] source = { "(.*)child", "$1children", "(.+)fe",
176: "$1ves", "(.*)mouse", "$1mise", "(.+)f", "$1ves",
177: "(.+)ch", "$1ches", "(.+)sh", "$1shes", "(.*)tooth",
178: "$1teeth", "(.+)um", "$1a", "(.+)an", "$1en",
179: "(.+)ato", "$1atoes", "(.*)basis", "$1bases",
180: "(.*)axis", "$1axes", "(.+)is", "$1ises", "(.+)us",
181: "$1uses", "(.+)s", "$1s", "(.*)foot", "$1feet",
182: "(.+)ix", "$1ixes", "(.+)ex", "$1ices", "(.+)nx",
183: "$1nxes", "(.+)x", "$1xes", "(.+)y", "$1ies", "(.+)",
184: "$1s", };
185:
186: TABLE = new Entry[source.length / 2];
187:
188: for (int i = 0; i < source.length; i += 2) {
189: TABLE[i / 2] = new Entry(source[i], source[i + 1]);
190: }
191: }
192: }
|