1/* 2 * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26package com.sun.codemodel.internal; 27 28import java.util.HashSet; 29import java.util.regex.Matcher; 30import java.util.regex.Pattern; 31 32/** 33 * Utility methods that convert arbitrary strings into Java identifiers. 34 */ 35public class JJavaName { 36 37 38 /** 39 * Checks if a given string is usable as a Java identifier. 40 */ 41 public static boolean isJavaIdentifier(String s) { 42 if(s.length()==0) return false; 43 if( reservedKeywords.contains(s) ) return false; 44 45 if(!Character.isJavaIdentifierStart(s.charAt(0))) return false; 46 47 for (int i = 1; i < s.length(); i++) 48 if (!Character.isJavaIdentifierPart(s.charAt(i))) 49 return false; 50 51 return true; 52 } 53 54 /** 55 * Checks if the given string is a valid fully qualified name. 56 */ 57 public static boolean isFullyQualifiedClassName(String s) { 58 return isJavaPackageName(s); 59 } 60 61 /** 62 * Checks if the given string is a valid Java package name. 63 */ 64 public static boolean isJavaPackageName(String s) { 65 while(s.length()!=0) { 66 int idx = s.indexOf('.'); 67 if(idx==-1) idx=s.length(); 68 if( !isJavaIdentifier(s.substring(0,idx)) ) 69 return false; 70 71 s = s.substring(idx); 72 if(s.length()!=0) s = s.substring(1); // remove '.' 73 } 74 return true; 75 } 76 77 /** 78 * <b>Experimental API:</b> converts an English word into a plural form. 79 * 80 * @param word 81 * a word, such as "child", "apple". Must not be null. 82 * It accepts word concatanation forms 83 * that are common in programming languages, such as "my_child", "MyChild", 84 * "myChild", "MY-CHILD", "CODE003-child", etc, and mostly tries to do the right thing. 85 * ("my_children","MyChildren","myChildren", and "MY-CHILDREN", "CODE003-children" respectively) 86 * <p> 87 * Although this method only works for English words, it handles non-English 88 * words gracefully (by just returning it as-is.) For example, "{@literal 日本語}" 89 * will be returned as-is without modified, not "{@literal 日本語s}" 90 * <p> 91 * This method doesn't handle suffixes very well. For example, passing 92 * "person56" will return "person56s", not "people56". 93 * 94 * @return 95 * always non-null. 96 */ 97 public static String getPluralForm(String word) { 98 // remember the casing of the word 99 boolean allUpper = true; 100 101 // check if the word looks like an English word. 102 // if we see non-ASCII characters, abort 103 for(int i=0; i<word.length(); i++ ) { 104 char ch = word.charAt(i); 105 if(ch >=0x80) 106 return word; 107 108 // note that this isn't the same as allUpper &= Character.isUpperCase(ch); 109 allUpper &= !Character.isLowerCase(ch); 110 } 111 112 for (Entry e : TABLE) { 113 String r = e.apply(word); 114 if(r!=null) { 115 if(allUpper) r=r.toUpperCase(); 116 return r; 117 } 118 } 119 120 // failed 121 return word; 122 } 123 124 125 /** All reserved keywords of Java. */ 126 private static HashSet<String> reservedKeywords = new HashSet<String>(); 127 128 static { 129 // see http://java.sun.com/docs/books/tutorial/java/nutsandbolts/_keywords.html 130 String[] words = new String[]{ 131 "abstract", 132 "boolean", 133 "break", 134 "byte", 135 "case", 136 "catch", 137 "char", 138 "class", 139 "const", 140 "continue", 141 "default", 142 "do", 143 "double", 144 "else", 145 "extends", 146 "final", 147 "finally", 148 "float", 149 "for", 150 "goto", 151 "if", 152 "implements", 153 "import", 154 "instanceof", 155 "int", 156 "interface", 157 "long", 158 "native", 159 "new", 160 "package", 161 "private", 162 "protected", 163 "public", 164 "return", 165 "short", 166 "static", 167 "strictfp", 168 "super", 169 "switch", 170 "synchronized", 171 "this", 172 "throw", 173 "throws", 174 "transient", 175 "try", 176 "void", 177 "volatile", 178 "while", 179 180 // technically these are not reserved words but they cannot be used as identifiers. 181 "true", 182 "false", 183 "null", 184 185 // and I believe assert is also a new keyword 186 "assert", 187 188 // and 5.0 keywords 189 "enum" 190 }; 191 for (String w : words) 192 reservedKeywords.add(w); 193 } 194 195 196 private static class Entry { 197 private final Pattern pattern; 198 private final String replacement; 199 200 public Entry(String pattern, String replacement) { 201 this.pattern = Pattern.compile(pattern,Pattern.CASE_INSENSITIVE); 202 this.replacement = replacement; 203 } 204 205 String apply(String word) { 206 Matcher m = pattern.matcher(word); 207 if(m.matches()) { 208 StringBuffer buf = new StringBuffer(); 209 m.appendReplacement(buf,replacement); 210 return buf.toString(); 211 } else { 212 return null; 213 } 214 } 215 } 216 217 private static final Entry[] TABLE; 218 219 static { 220 String[] source = { 221 "(.*)child","$1children", 222 "(.+)fe","$1ves", 223 "(.*)mouse","$1mise", 224 "(.+)f","$1ves", 225 "(.+)ch","$1ches", 226 "(.+)sh","$1shes", 227 "(.*)tooth","$1teeth", 228 "(.+)um","$1a", 229 "(.+)an","$1en", 230 "(.+)ato","$1atoes", 231 "(.*)basis","$1bases", 232 "(.*)axis","$1axes", 233 "(.+)is","$1ises", 234 "(.+)ss","$1sses", 235 "(.+)us","$1uses", 236 "(.+)s","$1s", 237 "(.*)foot","$1feet", 238 "(.+)ix","$1ixes", 239 "(.+)ex","$1ices", 240 "(.+)nx","$1nxes", 241 "(.+)x","$1xes", 242 "(.+)y","$1ies", 243 "(.+)","$1s", 244 }; 245 246 TABLE = new Entry[source.length/2]; 247 248 for( int i=0; i<source.length; i+=2 ) { 249 TABLE[i/2] = new Entry(source[i],source[i+1]); 250 } 251 } 252} 253