001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common Development
008: * and Distribution License("CDDL") (collectively, the "License"). You
009: * may not use this file except in compliance with the License. You can obtain
010: * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
011: * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
012: * language governing permissions and limitations under the License.
013: *
014: * When distributing the software, include this License Header Notice in each
015: * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
016: * Sun designates this particular file as subject to the "Classpath" exception
017: * as provided by Sun in the GPL Version 2 section of the License file that
018: * accompanied this code. If applicable, add the following below the License
019: * Header, with the fields enclosed by brackets [] replaced by your own
020: * identifying information: "Portions Copyrighted [year]
021: * [name of copyright owner]"
022: *
023: * Contributor(s):
024: *
025: * If you wish your version of this file to be governed by only the CDDL or
026: * only the GPL Version 2, indicate your decision by adding "[Contributor]
027: * elects to include this software in this distribution under the [CDDL or GPL
028: * Version 2] license." If you don't indicate a single choice of license, a
029: * recipient has the option to distribute your version of this file under
030: * either the CDDL, the GPL Version 2 or to extend the choice of license to
031: * its licensees as provided above. However, if you add GPL Version 2 code
032: * and therefore, elected the GPL Version 2 license, then the option applies
033: * only if the new code is made subject to such option by the copyright
034: * holder.
035: */
036:
037: package com.sun.xml.bind.api.impl;
038:
039: import java.util.ArrayList;
040: import java.util.List;
041: import java.util.StringTokenizer;
042:
043: /**
044: * Converts aribitrary strings into Java identifiers.
045: *
046: * @author
047: * <a href="mailto:kohsuke.kawaguchi@sun.com">Kohsuke KAWAGUCHI</a>
048: */
049: public interface NameConverter {
050: /**
051: * converts a string into an identifier suitable for classes.
052: *
053: * In general, this operation should generate "NamesLikeThis".
054: */
055: String toClassName(String token);
056:
057: /**
058: * converts a string into an identifier suitable for interfaces.
059: *
060: * In general, this operation should generate "NamesLikeThis".
061: * But for example, it can prepend every interface with 'I'.
062: */
063: String toInterfaceName(String token);
064:
065: /**
066: * converts a string into an identifier suitable for properties.
067: *
068: * In general, this operation should generate "NamesLikeThis",
069: * which will be used with known prefixes like "get" or "set".
070: */
071: String toPropertyName(String token);
072:
073: /**
074: * converts a string into an identifier suitable for constants.
075: *
076: * In the standard Java naming convention, this operation should
077: * generate "NAMES_LIKE_THIS".
078: */
079: String toConstantName(String token);
080:
081: /**
082: * Converts a string into an identifier suitable for variables.
083: *
084: * In general it should generate "namesLikeThis".
085: */
086: String toVariableName(String token);
087:
088: /**
089: * Converts a namespace URI into a package name.
090: * This method should expect strings like
091: * "http://foo.bar.zot/org", "urn:abc:def:ghi" "", or even "###"
092: * (basically anything) and expected to return a package name,
093: * liks "org.acme.foo".
094: *
095: */
096: String toPackageName(String namespaceUri);
097:
098: /**
099: * The name converter implemented by Code Model.
100: *
101: * This is the standard name conversion for JAXB.
102: */
103: public static final NameConverter standard = new Standard();
104:
105: static class Standard extends NameUtil implements NameConverter {
106: public String toClassName(String s) {
107: return toMixedCaseName(toWordList(s), true);
108: }
109:
110: public String toVariableName(String s) {
111: return toMixedCaseName(toWordList(s), false);
112: }
113:
114: public String toInterfaceName(String token) {
115: return toClassName(token);
116: }
117:
118: public String toPropertyName(String s) {
119: String prop = toClassName(s);
120: // property name "Class" with collide with Object.getClass,
121: // so escape this.
122: if (prop.equals("Class"))
123: prop = "Clazz";
124: return prop;
125: }
126:
127: public String toConstantName(String token) {
128: return super .toConstantName(token);
129: }
130:
131: /**
132: * Computes a Java package name from a namespace URI,
133: * as specified in the spec.
134: *
135: * @return
136: * null if it fails to derive a package name.
137: */
138: public String toPackageName(String nsUri) {
139: // remove scheme and :, if present
140: // spec only requires us to remove 'http' and 'urn'...
141: int idx = nsUri.indexOf(':');
142: String scheme = "";
143: if (idx >= 0) {
144: scheme = nsUri.substring(0, idx);
145: if (scheme.equalsIgnoreCase("http")
146: || scheme.equalsIgnoreCase("urn"))
147: nsUri = nsUri.substring(idx + 1);
148: }
149:
150: // tokenize string
151: ArrayList<String> tokens = tokenize(nsUri, "/: ");
152: if (tokens.size() == 0) {
153: return null;
154: }
155:
156: // remove trailing file type, if necessary
157: if (tokens.size() > 1) {
158: // for uri's like "www.foo.com" and "foo.com", there is no trailing
159: // file, so there's no need to look at the last '.' and substring
160: // otherwise, we loose the "com" (which would be wrong)
161: String lastToken = tokens.get(tokens.size() - 1);
162: idx = lastToken.lastIndexOf('.');
163: if (idx > 0) {
164: lastToken = lastToken.substring(0, idx);
165: tokens.set(tokens.size() - 1, lastToken);
166: }
167: }
168:
169: // tokenize domain name and reverse. Also remove :port if it exists
170: String domain = tokens.get(0);
171: idx = domain.indexOf(':');
172: if (idx >= 0)
173: domain = domain.substring(0, idx);
174: ArrayList<String> r = reverse(tokenize(domain, scheme
175: .equals("urn") ? ".-" : "."));
176: if (r.get(r.size() - 1).equalsIgnoreCase("www")) {
177: // remove leading www
178: r.remove(r.size() - 1);
179: }
180:
181: // replace the domain name with tokenized items
182: tokens.addAll(1, r);
183: tokens.remove(0);
184:
185: // iterate through the tokens and apply xml->java name algorithm
186: for (int i = 0; i < tokens.size(); i++) {
187:
188: // get the token and remove illegal chars
189: String token = tokens.get(i);
190: token = removeIllegalIdentifierChars(token);
191:
192: // this will check for reserved keywords
193: if (!NameUtil.isJavaIdentifier(token)) {
194: token = '_' + token;
195: }
196:
197: tokens.set(i, token.toLowerCase());
198: }
199:
200: // concat all the pieces and return it
201: return combine(tokens, '.');
202: }
203:
204: private static String removeIllegalIdentifierChars(String token) {
205: StringBuffer newToken = new StringBuffer();
206: for (int i = 0; i < token.length(); i++) {
207: char c = token.charAt(i);
208:
209: if (i == 0 && !Character.isJavaIdentifierStart(c)) {
210: // prefix an '_' if the first char is illegal
211: newToken.append('_').append(c);
212: } else if (!Character.isJavaIdentifierPart(c)) {
213: // replace the char with an '_' if it is illegal
214: newToken.append('_');
215: } else {
216: // add the legal char
217: newToken.append(c);
218: }
219: }
220: return newToken.toString();
221: }
222:
223: private static ArrayList<String> tokenize(String str, String sep) {
224: StringTokenizer tokens = new StringTokenizer(str, sep);
225: ArrayList<String> r = new ArrayList<String>();
226:
227: while (tokens.hasMoreTokens())
228: r.add(tokens.nextToken());
229:
230: return r;
231: }
232:
233: private static <T> ArrayList<T> reverse(List<T> a) {
234: ArrayList<T> r = new ArrayList<T>();
235:
236: for (int i = a.size() - 1; i >= 0; i--)
237: r.add(a.get(i));
238:
239: return r;
240: }
241:
242: private static String combine(List r, char sep) {
243: StringBuilder buf = new StringBuilder(r.get(0).toString());
244:
245: for (int i = 1; i < r.size(); i++) {
246: buf.append(sep);
247: buf.append(r.get(i));
248: }
249:
250: return buf.toString();
251: }
252: }
253:
254: /**
255: * JAX-PRC compatible name converter implementation.
256: *
257: * The only difference is that we treat '_' as a valid character
258: * and not as a word separator.
259: */
260: public static final NameConverter jaxrpcCompatible = new Standard() {
261: protected boolean isPunct(char c) {
262: return (c == '.' || c == '-' || c == ';' /*|| c == '_'*/
263: || c == '\u00b7' || c == '\u0387' || c == '\u06dd' || c == '\u06de');
264: }
265:
266: protected boolean isLetter(char c) {
267: return super .isLetter(c) || c == '_';
268: }
269:
270: protected int classify(char c0) {
271: if (c0 == '_')
272: return NameUtil.OTHER_LETTER;
273: return super .classify(c0);
274: }
275: };
276:
277: /**
278: * Smarter converter used for RELAX NG support.
279: */
280: public static final NameConverter smart = new Standard() {
281: public String toConstantName(String token) {
282: String name = super .toConstantName(token);
283: if (NameUtil.isJavaIdentifier(name))
284: return name;
285: else
286: return '_' + name;
287: }
288: };
289: }
|