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