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.xml.internal.bind.api.impl;
027:
028: import java.util.ArrayList;
029: import java.util.List;
030: import java.util.StringTokenizer;
031:
032: /**
033: * Converts aribitrary strings into Java identifiers.
034: *
035: * @author
036: * <a href="mailto:kohsuke.kawaguchi@sun.com">Kohsuke KAWAGUCHI</a>
037: */
038: public interface NameConverter {
039: /**
040: * converts a string into an identifier suitable for classes.
041: *
042: * In general, this operation should generate "NamesLikeThis".
043: */
044: String toClassName(String token);
045:
046: /**
047: * converts a string into an identifier suitable for interfaces.
048: *
049: * In general, this operation should generate "NamesLikeThis".
050: * But for example, it can prepend every interface with 'I'.
051: */
052: String toInterfaceName(String token);
053:
054: /**
055: * converts a string into an identifier suitable for properties.
056: *
057: * In general, this operation should generate "NamesLikeThis",
058: * which will be used with known prefixes like "get" or "set".
059: */
060: String toPropertyName(String token);
061:
062: /**
063: * converts a string into an identifier suitable for constants.
064: *
065: * In the standard Java naming convention, this operation should
066: * generate "NAMES_LIKE_THIS".
067: */
068: String toConstantName(String token);
069:
070: /**
071: * Converts a string into an identifier suitable for variables.
072: *
073: * In general it should generate "namesLikeThis".
074: */
075: String toVariableName(String token);
076:
077: /**
078: * Converts a namespace URI into a package name.
079: * This method should expect strings like
080: * "http://foo.bar.zot/org", "urn:abc:def:ghi" "", or even "###"
081: * (basically anything) and expected to return a package name,
082: * liks "org.acme.foo".
083: *
084: */
085: String toPackageName(String namespaceUri);
086:
087: /**
088: * The name converter implemented by Code Model.
089: *
090: * This is the standard name conversion for JAXB.
091: */
092: public static final NameConverter standard = new Standard();
093:
094: static class Standard extends NameUtil implements NameConverter {
095: public String toClassName(String s) {
096: return toMixedCaseName(toWordList(s), true);
097: }
098:
099: public String toVariableName(String s) {
100: return toMixedCaseName(toWordList(s), false);
101: }
102:
103: public String toInterfaceName(String token) {
104: return toClassName(token);
105: }
106:
107: public String toPropertyName(String s) {
108: String prop = toClassName(s);
109: // property name "Class" with collide with Object.getClass,
110: // so escape this.
111: if (prop.equals("Class"))
112: prop = "Clazz";
113: return prop;
114: }
115:
116: public String toConstantName(String token) {
117: return super .toConstantName(token);
118: }
119:
120: /**
121: * Computes a Java package name from a namespace URI,
122: * as specified in the spec.
123: *
124: * @return
125: * null if it fails to derive a package name.
126: */
127: public String toPackageName(String nsUri) {
128: // remove scheme and :, if present
129: // spec only requires us to remove 'http' and 'urn'...
130: int idx = nsUri.indexOf(':');
131: String scheme = "";
132: if (idx >= 0) {
133: scheme = nsUri.substring(0, idx);
134: if (scheme.equalsIgnoreCase("http")
135: || scheme.equalsIgnoreCase("urn"))
136: nsUri = nsUri.substring(idx + 1);
137: }
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)
162: domain = domain.substring(0, idx);
163: ArrayList<String> r = reverse(tokenize(domain, scheme
164: .equals("urn") ? ".-" : "."));
165: if (r.get(r.size() - 1).equalsIgnoreCase("www")) {
166: // remove leading www
167: r.remove(r.size() - 1);
168: }
169:
170: // replace the domain name with tokenized items
171: tokens.addAll(1, r);
172: tokens.remove(0);
173:
174: // iterate through the tokens and apply xml->java name algorithm
175: for (int i = 0; i < tokens.size(); i++) {
176:
177: // get the token and remove illegal chars
178: String token = tokens.get(i);
179: token = removeIllegalIdentifierChars(token);
180:
181: // this will check for reserved keywords
182: if (!NameUtil.isJavaIdentifier(token)) {
183: token = '_' + token;
184: }
185:
186: tokens.set(i, token.toLowerCase());
187: }
188:
189: // concat all the pieces and return it
190: return combine(tokens, '.');
191: }
192:
193: private static String removeIllegalIdentifierChars(String token) {
194: StringBuffer newToken = new StringBuffer();
195: for (int i = 0; i < token.length(); i++) {
196: char c = token.charAt(i);
197:
198: if (i == 0 && !Character.isJavaIdentifierStart(c)) {
199: // prefix an '_' if the first char is illegal
200: newToken.append('_').append(c);
201: } else if (!Character.isJavaIdentifierPart(c)) {
202: // replace the char with an '_' if it is illegal
203: newToken.append('_');
204: } else {
205: // add the legal char
206: newToken.append(c);
207: }
208: }
209: return newToken.toString();
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 == '_'*/
252: || c == '\u00b7' || c == '\u0387' || c == '\u06dd' || c == '\u06de');
253: }
254:
255: protected boolean isLetter(char c) {
256: return super .isLetter(c) || c == '_';
257: }
258:
259: protected int classify(char c0) {
260: if (c0 == '_')
261: return NameUtil.OTHER_LETTER;
262: return super .classify(c0);
263: }
264: };
265:
266: /**
267: * Smarter converter used for RELAX NG support.
268: */
269: public static final NameConverter smart = new Standard() {
270: public String toConstantName(String token) {
271: String name = super .toConstantName(token);
272: if (NameUtil.isJavaIdentifier(name))
273: return name;
274: else
275: return '_' + name;
276: }
277: };
278: }
|