325N/A/*
325N/A * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
325N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
325N/A *
325N/A * This code is free software; you can redistribute it and/or modify it
325N/A * under the terms of the GNU General Public License version 2 only, as
325N/A * published by the Free Software Foundation. Oracle designates this
325N/A * particular file as subject to the "Classpath" exception as provided
325N/A * by Oracle in the LICENSE file that accompanied this code.
325N/A *
325N/A * This code is distributed in the hope that it will be useful, but WITHOUT
325N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
325N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
325N/A * version 2 for more details (a copy is included in the LICENSE file that
325N/A * accompanied this code).
325N/A *
325N/A * You should have received a copy of the GNU General Public License version
325N/A * 2 along with this work; if not, write to the Free Software Foundation,
325N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
325N/A *
325N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
325N/A * or visit www.oracle.com if you need additional information or have any
325N/A * questions.
325N/A */
325N/A
325N/Apackage com.sun.codemodel.internal;
325N/A
325N/Aimport java.util.HashSet;
325N/Aimport java.util.regex.Matcher;
325N/Aimport java.util.regex.Pattern;
325N/A
325N/A/**
325N/A * Utility methods that convert arbitrary strings into Java identifiers.
325N/A */
325N/Apublic class JJavaName {
325N/A
325N/A
325N/A /**
325N/A * Checks if a given string is usable as a Java identifier.
325N/A */
325N/A public static boolean isJavaIdentifier(String s) {
325N/A if(s.length()==0) return false;
325N/A if( reservedKeywords.contains(s) ) return false;
325N/A
325N/A if(!Character.isJavaIdentifierStart(s.charAt(0))) return false;
325N/A
325N/A for (int i = 1; i < s.length(); i++)
325N/A if (!Character.isJavaIdentifierPart(s.charAt(i)))
325N/A return false;
325N/A
325N/A return true;
325N/A }
325N/A
325N/A /**
325N/A * Checks if the given string is a valid fully qualified name.
325N/A */
325N/A public static boolean isFullyQualifiedClassName(String s) {
325N/A return isJavaPackageName(s);
325N/A }
325N/A
325N/A /**
325N/A * Checks if the given string is a valid Java package name.
325N/A */
325N/A public static boolean isJavaPackageName(String s) {
325N/A while(s.length()!=0) {
325N/A int idx = s.indexOf('.');
325N/A if(idx==-1) idx=s.length();
325N/A if( !isJavaIdentifier(s.substring(0,idx)) )
325N/A return false;
325N/A
325N/A s = s.substring(idx);
325N/A if(s.length()!=0) s = s.substring(1); // remove '.'
325N/A }
325N/A return true;
325N/A }
325N/A
325N/A /**
325N/A * <b>Experimental API:</b> converts an English word into a plural form.
325N/A *
325N/A * @param word
325N/A * a word, such as "child", "apple". Must not be null.
325N/A * It accepts word concatanation forms
325N/A * that are common in programming languages, such as "my_child", "MyChild",
325N/A * "myChild", "MY-CHILD", "CODE003-child", etc, and mostly tries to do the right thing.
325N/A * ("my_children","MyChildren","myChildren", and "MY-CHILDREN", "CODE003-children" respectively)
325N/A * <p>
325N/A * Although this method only works for English words, it handles non-English
325N/A * words gracefully (by just returning it as-is.) For example, &#x65E5;&#x672C;&#x8A9E;
325N/A * will be returned as-is without modified, not "&#x65E5;&#x672C;&#x8A9E;s"
325N/A * <p>
325N/A * This method doesn't handle suffixes very well. For example, passing
325N/A * "person56" will return "person56s", not "people56".
325N/A *
325N/A * @return
325N/A * always non-null.
325N/A */
325N/A public static String getPluralForm(String word) {
325N/A // remember the casing of the word
325N/A boolean allUpper = true;
325N/A
325N/A // check if the word looks like an English word.
325N/A // if we see non-ASCII characters, abort
325N/A for(int i=0; i<word.length(); i++ ) {
325N/A char ch = word.charAt(i);
325N/A if(ch >=0x80)
325N/A return word;
325N/A
325N/A // note that this isn't the same as allUpper &= Character.isUpperCase(ch);
325N/A allUpper &= !Character.isLowerCase(ch);
325N/A }
325N/A
325N/A for (Entry e : TABLE) {
325N/A String r = e.apply(word);
325N/A if(r!=null) {
325N/A if(allUpper) r=r.toUpperCase();
325N/A return r;
325N/A }
325N/A }
325N/A
325N/A // failed
325N/A return word;
325N/A }
325N/A
325N/A
325N/A /** All reserved keywords of Java. */
325N/A private static HashSet<String> reservedKeywords = new HashSet<String>();
325N/A
325N/A static {
325N/A // see http://java.sun.com/docs/books/tutorial/java/nutsandbolts/_keywords.html
325N/A String[] words = new String[]{
325N/A "abstract",
325N/A "boolean",
325N/A "break",
325N/A "byte",
325N/A "case",
325N/A "catch",
325N/A "char",
325N/A "class",
325N/A "const",
325N/A "continue",
325N/A "default",
325N/A "do",
325N/A "double",
325N/A "else",
325N/A "extends",
325N/A "final",
325N/A "finally",
325N/A "float",
325N/A "for",
325N/A "goto",
325N/A "if",
325N/A "implements",
325N/A "import",
325N/A "instanceof",
325N/A "int",
325N/A "interface",
325N/A "long",
325N/A "native",
325N/A "new",
325N/A "package",
325N/A "private",
325N/A "protected",
325N/A "public",
325N/A "return",
325N/A "short",
325N/A "static",
325N/A "strictfp",
325N/A "super",
325N/A "switch",
325N/A "synchronized",
325N/A "this",
325N/A "throw",
325N/A "throws",
325N/A "transient",
325N/A "try",
325N/A "void",
325N/A "volatile",
325N/A "while",
325N/A
325N/A // technically these are not reserved words but they cannot be used as identifiers.
325N/A "true",
325N/A "false",
325N/A "null",
325N/A
325N/A // and I believe assert is also a new keyword
325N/A "assert",
325N/A
325N/A // and 5.0 keywords
325N/A "enum"
325N/A };
325N/A for (String w : words)
325N/A reservedKeywords.add(w);
325N/A }
325N/A
325N/A
325N/A private static class Entry {
325N/A private final Pattern pattern;
325N/A private final String replacement;
325N/A
325N/A public Entry(String pattern, String replacement) {
325N/A this.pattern = Pattern.compile(pattern,Pattern.CASE_INSENSITIVE);
325N/A this.replacement = replacement;
325N/A }
325N/A
325N/A String apply(String word) {
325N/A Matcher m = pattern.matcher(word);
325N/A if(m.matches()) {
325N/A StringBuffer buf = new StringBuffer();
325N/A m.appendReplacement(buf,replacement);
325N/A return buf.toString();
325N/A } else {
325N/A return null;
325N/A }
325N/A }
325N/A }
325N/A
325N/A private static final Entry[] TABLE;
325N/A
325N/A static {
325N/A String[] source = {
325N/A "(.*)child","$1children",
325N/A "(.+)fe","$1ves",
325N/A "(.*)mouse","$1mise",
325N/A "(.+)f","$1ves",
325N/A "(.+)ch","$1ches",
325N/A "(.+)sh","$1shes",
325N/A "(.*)tooth","$1teeth",
325N/A "(.+)um","$1a",
325N/A "(.+)an","$1en",
325N/A "(.+)ato","$1atoes",
325N/A "(.*)basis","$1bases",
325N/A "(.*)axis","$1axes",
325N/A "(.+)is","$1ises",
325N/A "(.+)ss","$1sses",
325N/A "(.+)us","$1uses",
325N/A "(.+)s","$1s",
325N/A "(.*)foot","$1feet",
325N/A "(.+)ix","$1ixes",
325N/A "(.+)ex","$1ices",
325N/A "(.+)nx","$1nxes",
325N/A "(.+)x","$1xes",
325N/A "(.+)y","$1ies",
325N/A "(.+)","$1s",
325N/A };
325N/A
325N/A TABLE = new Entry[source.length/2];
325N/A
325N/A for( int i=0; i<source.length; i+=2 ) {
325N/A TABLE[i/2] = new Entry(source[i],source[i+1]);
325N/A }
325N/A }
325N/A}