1 /* 2 * Copyright (c) 1997, 2013, 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 26 package com.sun.xml.internal.bind.api.impl; 27 28 import javax.lang.model.SourceVersion; 29 import java.util.ArrayList; 30 import java.util.List; 31 import java.util.StringTokenizer; 32 33 /** 34 * Converts aribitrary strings into Java identifiers. 35 * 36 * @author 37 * <a href="mailto:kohsuke.kawaguchi@sun.com">Kohsuke KAWAGUCHI</a> 38 */ 39 public interface NameConverter 40 { 41 /** 42 * converts a string into an identifier suitable for classes. 43 * 44 * In general, this operation should generate "NamesLikeThis". 45 */ 46 String toClassName( String token ); 47 48 /** 49 * converts a string into an identifier suitable for interfaces. 50 * 51 * In general, this operation should generate "NamesLikeThis". 52 * But for example, it can prepend every interface with 'I'. 53 */ 54 String toInterfaceName( String token ); 55 56 /** 57 * converts a string into an identifier suitable for properties. 58 * 59 * In general, this operation should generate "NamesLikeThis", 60 * which will be used with known prefixes like "get" or "set". 61 */ 62 String toPropertyName( String token ); 63 64 /** 65 * converts a string into an identifier suitable for constants. 66 * 67 * In the standard Java naming convention, this operation should 68 * generate "NAMES_LIKE_THIS". 69 */ 70 String toConstantName( String token ); 71 72 /** 73 * Converts a string into an identifier suitable for variables. 74 * 75 * In general it should generate "namesLikeThis". 76 */ 77 String toVariableName( String token ); 78 79 /** 80 * Converts a namespace URI into a package name. 81 * This method should expect strings like 82 * "http://foo.bar.zot/org", "urn:abc:def:ghi" "", or even "###" 83 * (basically anything) and expected to return a package name, 84 * liks "org.acme.foo". 85 * 86 */ 87 String toPackageName( String namespaceUri ); 88 89 /** 90 * The name converter implemented by Code Model. 91 * 92 * This is the standard name conversion for JAXB. 93 */ 94 public static final NameConverter standard = new Standard(); 95 96 static class Standard extends NameUtil implements NameConverter { 97 public String toClassName(String s) { 98 return toMixedCaseName(toWordList(s), true); 99 } 100 public String toVariableName(String s) { 101 return toMixedCaseName(toWordList(s), false); 102 } 103 public String toInterfaceName( String token ) { 104 return toClassName(token); 105 } 106 public String toPropertyName(String s) { 107 String prop = toClassName(s); 108 // property name "Class" with collide with Object.getClass, 109 // so escape this. 110 if(prop.equals("Class")) 111 prop = "Clazz"; 112 return prop; 113 } 114 public String toConstantName( String token ) { 115 return super.toConstantName(token); 116 } 117 /** 118 * Computes a Java package name from a namespace URI, 119 * as specified in the spec. 120 * 121 * @return 122 * null if it fails to derive a package name. 123 */ 124 public String toPackageName( String nsUri ) { 125 // remove scheme and :, if present 126 // spec only requires us to remove 'http' and 'urn'... 127 int idx = nsUri.indexOf(':'); 128 String scheme = ""; 129 if(idx>=0) { 130 scheme = nsUri.substring(0,idx); 131 if( scheme.equalsIgnoreCase("http") || scheme.equalsIgnoreCase("urn") ) 132 nsUri = nsUri.substring(idx+1); 133 } 134 135 // tokenize string 136 ArrayList<String> tokens = tokenize( nsUri, "/: " ); 137 if( tokens.size() == 0 ) { 138 return null; 139 } 140 141 // remove trailing file type, if necessary 142 if( tokens.size() > 1 ) { 143 // for uri's like "www.foo.com" and "foo.com", there is no trailing 144 // file, so there's no need to look at the last '.' and substring 145 // otherwise, we loose the "com" (which would be wrong) 146 String lastToken = tokens.get( tokens.size()-1 ); 147 idx = lastToken.lastIndexOf( '.' ); 148 if( idx > 0 ) { 149 lastToken = lastToken.substring( 0, idx ); 150 tokens.set( tokens.size()-1, lastToken ); 151 } 152 } 153 154 // tokenize domain name and reverse. Also remove :port if it exists 155 String domain = tokens.get( 0 ); 156 idx = domain.indexOf(':'); 157 if( idx >= 0) domain = domain.substring(0, idx); 158 ArrayList<String> r = reverse( tokenize( domain, scheme.equals("urn")?".-":"." ) ); 159 if( r.get( r.size()-1 ).equalsIgnoreCase( "www" ) ) { 160 // remove leading www 161 r.remove( r.size()-1 ); 162 } 163 164 // replace the domain name with tokenized items 165 tokens.addAll( 1, r ); 166 tokens.remove( 0 ); 167 168 // iterate through the tokens and apply xml->java name algorithm 169 for( int i = 0; i < tokens.size(); i++ ) { 170 171 // get the token and remove illegal chars 172 String token = tokens.get( i ); 173 token = removeIllegalIdentifierChars( token ); 174 175 // this will check for reserved keywords 176 if (SourceVersion.isKeyword(token.toLowerCase())) { 177 token = '_' + token; 178 } 179 180 tokens.set( i, token.toLowerCase() ); 181 } 182 183 // concat all the pieces and return it 184 return combine( tokens, '.' ); 185 } 186 187 188 private static String removeIllegalIdentifierChars(String token) { 189 StringBuilder newToken = new StringBuilder(token.length() + 1); // max expected length 190 for( int i = 0; i < token.length(); i++ ) { 191 char c = token.charAt( i ); 192 if (i == 0 && !Character.isJavaIdentifierStart(c)) { // c can't be used as FIRST char 193 newToken.append('_'); 194 } 195 if (!Character.isJavaIdentifierPart(c)) { // c can't be used 196 newToken.append('_'); 197 } else { 198 newToken.append(c); // c is valid 199 } 200 } 201 return newToken.toString(); 202 } 203 204 205 private static ArrayList<String> tokenize( String str, String sep ) { 206 StringTokenizer tokens = new StringTokenizer(str,sep); 207 ArrayList<String> r = new ArrayList<String>(); 208 209 while(tokens.hasMoreTokens()) 210 r.add( tokens.nextToken() ); 211 212 return r; 213 } 214 215 private static <T> ArrayList<T> reverse( List<T> a ) { 216 ArrayList<T> r = new ArrayList<T>(); 217 218 for( int i=a.size()-1; i>=0; i-- ) 219 r.add( a.get(i) ); 220 221 return r; 222 } 223 224 private static String combine( List r, char sep ) { 225 StringBuilder buf = new StringBuilder(r.get(0).toString()); 226 227 for( int i=1; i<r.size(); i++ ) { 228 buf.append(sep); 229 buf.append(r.get(i)); 230 } 231 232 return buf.toString(); 233 } 234 } 235 236 /** 237 * JAX-PRC compatible name converter implementation. 238 * 239 * The only difference is that we treat '_' as a valid character 240 * and not as a word separator. 241 */ 242 public static final NameConverter jaxrpcCompatible = new Standard() { 243 protected boolean isPunct(char c) { 244 return (c == '.' || c == '-' || c == ';' /*|| c == '_'*/ || c == '\u00b7' 245 || c == '\u0387' || c == '\u06dd' || c == '\u06de'); 246 } 247 protected boolean isLetter(char c) { 248 return super.isLetter(c) || c=='_'; 249 } 250 251 protected int classify(char c0) { 252 if(c0=='_') return NameUtil.OTHER_LETTER; 253 return super.classify(c0); 254 } 255 }; 256 257 /** 258 * Smarter converter used for RELAX NG support. 259 */ 260 public static final NameConverter smart = new Standard() { 261 public String toConstantName( String token ) { 262 String name = super.toConstantName(token); 263 if(!SourceVersion.isKeyword(name)) 264 return name; 265 else 266 return '_'+name; 267 } 268 }; 269 }