001: /**
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */package org.apache.cxf.jaxb;
019:
020: import java.lang.reflect.Method;
021: import java.net.URI;
022: import java.net.URISyntaxException;
023:
024: import java.util.ArrayList;
025: import java.util.HashMap;
026: import java.util.List;
027: import java.util.Map;
028: import java.util.StringTokenizer;
029: import java.util.concurrent.Future;
030:
031: import javax.xml.ws.AsyncHandler;
032: import javax.xml.ws.Holder;
033: import javax.xml.ws.Response;
034:
035: import org.apache.cxf.helpers.JavaUtils;
036:
037: public final class JAXBUtils {
038:
039: public enum IdentifierType {
040: CLASS, INTERFACE, GETTER, SETTER, VARIABLE, CONSTANT
041: };
042:
043: public static final String JAXB_URI = "http://java.sun.com/xml/ns/jaxb";
044:
045: private static final char[] XML_NAME_PUNCTUATION_CHARS = new char[] {
046: /* hyphen */'\u002D',
047: /* period */'\u002E',
048: /* colon */'\u003A',
049: /* dot */'\u00B7',
050: /* greek ano teleia */'\u0387',
051: /* arabic end of ayah */'\u06DD',
052: /* arabic start of rub el hizb */'\u06DE',
053: /* underscore */'\u005F', };
054:
055: private static final String XML_NAME_PUNCTUATION_STRING = new String(
056: XML_NAME_PUNCTUATION_CHARS);
057:
058: private static final Map<String, String> BUILTIN_DATATYPES_MAP;
059: private static final Map<String, Class<?>> HOLDER_TYPES_MAP;
060:
061: static {
062: BUILTIN_DATATYPES_MAP = new HashMap<String, String>();
063: BUILTIN_DATATYPES_MAP.put("string", "java.lang.String");
064: BUILTIN_DATATYPES_MAP.put("integer", "java.math.BigInteger");
065: BUILTIN_DATATYPES_MAP.put("int", "int");
066: BUILTIN_DATATYPES_MAP.put("long", "long");
067: BUILTIN_DATATYPES_MAP.put("short", "short");
068: BUILTIN_DATATYPES_MAP.put("decimal", "java.math.BigDecimal");
069: BUILTIN_DATATYPES_MAP.put("float", "float");
070: BUILTIN_DATATYPES_MAP.put("double", "double");
071: BUILTIN_DATATYPES_MAP.put("boolean", "boolean");
072: BUILTIN_DATATYPES_MAP.put("byte", "byte");
073: BUILTIN_DATATYPES_MAP.put("QName", "javax.xml.namespace.QName");
074: BUILTIN_DATATYPES_MAP.put("dateTime",
075: "javax.xml.datatype.XMLGregorianCalendar");
076: BUILTIN_DATATYPES_MAP.put("base64Binary", "byte[]");
077: BUILTIN_DATATYPES_MAP.put("hexBinary", "byte[]");
078: BUILTIN_DATATYPES_MAP.put("unsignedInt", "long");
079: BUILTIN_DATATYPES_MAP.put("unsignedShort", "short");
080: BUILTIN_DATATYPES_MAP.put("unsignedByte", "byte");
081: BUILTIN_DATATYPES_MAP.put("time",
082: "javax.xml.datatype.XMLGregorianCalendar");
083: BUILTIN_DATATYPES_MAP.put("date",
084: "javax.xml.datatype.XMLGregorianCalendar");
085: BUILTIN_DATATYPES_MAP.put("gYear",
086: "javax.xml.datatype.XMLGregorianCalendar");
087: BUILTIN_DATATYPES_MAP.put("gYearMonth",
088: "javax.xml.datatype.XMLGregorianCalendar");
089: BUILTIN_DATATYPES_MAP.put("gMonth",
090: "javax.xml.datatype.XMLGregorianCalendar");
091: BUILTIN_DATATYPES_MAP.put("gMonthDay",
092: "javax.xml.datatype.XMLGregorianCalendar");
093: BUILTIN_DATATYPES_MAP.put("gDay",
094: "javax.xml.datatype.XMLGregorianCalendar");
095: BUILTIN_DATATYPES_MAP.put("duration",
096: "javax.xml.datatype.Duration");
097: BUILTIN_DATATYPES_MAP.put("NOTATION",
098: "javax.xml.namespace.QName");
099: BUILTIN_DATATYPES_MAP.put("string", "java.lang.String");
100:
101: HOLDER_TYPES_MAP = new HashMap<String, Class<?>>();
102: HOLDER_TYPES_MAP.put("int", java.lang.Integer.class);
103: HOLDER_TYPES_MAP.put("long", java.lang.Long.class);
104: HOLDER_TYPES_MAP.put("short", java.lang.Short.class);
105: HOLDER_TYPES_MAP.put("float", java.lang.Float.class);
106: HOLDER_TYPES_MAP.put("double", java.lang.Double.class);
107: HOLDER_TYPES_MAP.put("boolean", java.lang.Boolean.class);
108: HOLDER_TYPES_MAP.put("byte", java.lang.Byte.class);
109: }
110:
111: /**
112: * prevents instantiation
113: *
114: */
115: private JAXBUtils() {
116: }
117:
118: public static String builtInTypeToJavaType(String type) {
119: return BUILTIN_DATATYPES_MAP.get(type);
120: }
121:
122: public static Class<?> holderClass(String type) {
123: return HOLDER_TYPES_MAP.get(type);
124: }
125:
126: /**
127: * Checks if the specified word is a Java keyword (as of 1.5).
128: *
129: * @param word the word to check.
130: * @return true if the word is a keyword.
131: */
132: public static boolean isJavaKeyword(String word) {
133: return JavaUtils.isJavaKeyword(word);
134: }
135:
136: /**
137: * Generates a Java package name from a URI according to the
138: * algorithm outlined in JAXB 2.0.
139: *
140: * @param namespaceURI the namespace URI.
141: * @return the package name.
142: */
143: public static String namespaceURIToPackage(String namespaceURI) {
144: try {
145: return nameSpaceURIToPackage(new URI(namespaceURI));
146: } catch (URISyntaxException ex) {
147: return null;
148: }
149: }
150:
151: /**
152: * Generates a Java package name from a URI according to the
153: * algorithm outlined in JAXB 2.0.
154: *
155: * @param namespaceURI the namespace URI.
156: * @return the package name.
157: */
158: public static String nameSpaceURIToPackage(URI uri) {
159:
160: StringBuffer packageName = new StringBuffer();
161: String authority = uri.getAuthority();
162: if (authority == null && "urn".equals(uri.getScheme())) {
163: authority = uri.getSchemeSpecificPart();
164: }
165:
166: if (null != authority && !"".equals(authority)) {
167: if ("urn".equals(uri.getScheme())) {
168: packageName.append(authority);
169: for (int i = 0; i < packageName.length(); i++) {
170: if (packageName.charAt(i) == '-') {
171: packageName.setCharAt(i, '.');
172: }
173: }
174: authority = packageName.toString();
175: packageName.setLength(0);
176:
177: StringTokenizer st = new StringTokenizer(authority, ":");
178: while (st.hasMoreTokens()) {
179: String token = st.nextToken();
180: if (packageName.length() > 0) {
181: packageName.insert(0, ".");
182: packageName.insert(0,
183: normalizePackageNamePart(token));
184: } else {
185: packageName.insert(0, token);
186: }
187: }
188: authority = packageName.toString();
189: packageName.setLength(0);
190:
191: }
192:
193: StringTokenizer st = new StringTokenizer(authority, ".");
194: if (st.hasMoreTokens()) {
195: String token = null;
196: while (st.hasMoreTokens()) {
197: token = st.nextToken();
198: if (packageName.length() == 0) {
199: if ("www".equals(token)) {
200: continue;
201: }
202: } else {
203: packageName.insert(0, ".");
204: }
205: packageName.insert(0,
206: normalizePackageNamePart(token));
207: }
208:
209: }
210: }
211:
212: String path = uri.getPath();
213: if (path == null) {
214: path = "";
215: }
216: int index = path.lastIndexOf('.');
217: if (index < 0) {
218: index = path.length();
219: }
220: StringTokenizer st = new StringTokenizer(path.substring(0,
221: index), "/");
222: while (st.hasMoreTokens()) {
223: String token = st.nextToken();
224: if (packageName.length() > 0) {
225: packageName.append('.');
226: }
227: packageName.append(normalizePackageNamePart(token));
228: }
229: return packageName.toString();
230: }
231:
232: public static boolean isAsync(Method method) {
233: return method.getName().endsWith("Async")
234: && (method.getReturnType().equals(Response.class) || method
235: .getReturnType().equals(Future.class));
236: }
237:
238: public static boolean isAsyncPolling(Method method) {
239: return method.getName().endsWith("Async")
240: && (method.getReturnType().equals(Response.class));
241: }
242:
243: public static boolean isAsyncCallback(Method method) {
244: Class[] paramTypes = method.getParameterTypes();
245: return method.getName().endsWith("Async")
246: && (method.getReturnType().equals(Future.class) && AsyncHandler.class
247: .isAssignableFrom(paramTypes[paramTypes.length - 1]));
248: }
249:
250: private static String normalizePackageNamePart(String name) {
251: StringBuffer sname = new StringBuffer(name.toLowerCase());
252:
253: for (int i = 0; i < sname.length(); i++) {
254: sname.setCharAt(i, Character.toLowerCase(sname.charAt(i)));
255: }
256:
257: for (int i = 0; i < sname.length(); i++) {
258: if (!Character.isJavaIdentifierPart(sname.charAt(i))) {
259: sname.setCharAt(i, '_');
260: }
261: }
262:
263: if (isJavaKeyword(sname.toString())) {
264: sname.insert(0, '_');
265: }
266:
267: if (!Character.isJavaIdentifierStart(sname.charAt(0))) {
268: sname.insert(0, '_');
269: }
270:
271: return sname.toString();
272: }
273:
274: /**
275: * Converts an XML name to a Java identifier according to the mapping
276: * algorithm outlines in the JAXB specification
277: *
278: * @param name the XML name
279: * @return the Java identifier
280: */
281: public static String nameToIdentifier(String name,
282: IdentifierType type) {
283:
284: if (null == name || name.length() == 0) {
285: return name;
286: }
287:
288: // algorithm will not change an XML name that is already a legal and
289: // conventional (!) Java class, method, or constant identifier
290:
291: boolean legalIdentifier = false;
292: StringBuffer buf = new StringBuffer(name);
293: legalIdentifier = Character
294: .isJavaIdentifierStart(buf.charAt(0));
295:
296: for (int i = 1; i < name.length() && legalIdentifier; i++) {
297: legalIdentifier = legalIdentifier
298: && Character.isJavaIdentifierPart(buf.charAt(i));
299: }
300:
301: boolean conventionalIdentifier = isConventionalIdentifier(buf,
302: type);
303: if (legalIdentifier && conventionalIdentifier) {
304: if (JAXBUtils.isJavaKeyword(name)
305: && type == IdentifierType.VARIABLE) {
306: name = normalizePackageNamePart(name.toString());
307: }
308: return name;
309: }
310:
311: // split into words
312:
313: List<String> words = new ArrayList<String>();
314:
315: StringTokenizer st = new StringTokenizer(name,
316: XML_NAME_PUNCTUATION_STRING);
317: while (st.hasMoreTokens()) {
318: words.add(st.nextToken());
319: }
320:
321: for (int i = 0; i < words.size(); i++) {
322: splitWord(words, i);
323: }
324:
325: return makeConventionalIdentifier(words, type);
326: }
327:
328: private static void splitWord(List<String> words, int listIndex) {
329: String word = words.get(listIndex);
330: if (word.length() <= 1) {
331: return;
332: }
333: int index = listIndex + 1;
334: StringBuffer sword = new StringBuffer(word);
335: int first = 0;
336: char firstChar = sword.charAt(first);
337: if (Character.isLowerCase(firstChar)) {
338: sword.setCharAt(first, Character.toUpperCase(firstChar));
339: }
340: int i = 1;
341:
342: while (i < sword.length()) {
343: if (Character.isDigit(firstChar)) {
344: while (i < sword.length()
345: && Character.isDigit(sword.charAt(i))) {
346: i++;
347: }
348: } else if (isCasedLetter(firstChar)) {
349: boolean previousIsLower = Character
350: .isLowerCase(firstChar);
351: while (i < sword.length()
352: && isCasedLetter(sword.charAt(i))) {
353: if (Character.isUpperCase(sword.charAt(i))
354: && previousIsLower) {
355: break;
356: }
357: previousIsLower = Character.isLowerCase(sword
358: .charAt(i));
359: i++;
360: }
361: } else {
362: // first must be a mark or an uncased letter
363: while (i < sword.length()
364: && (isMark(sword.charAt(i)) || !isCasedLetter(sword
365: .charAt(i)))) {
366: i++;
367: }
368: }
369:
370: // characters from first to i are all either
371: // * digits
372: // * upper or lower case letters, with only the first one an upper
373: // * uncased letters or marks
374:
375: String newWord = sword.substring(first, i);
376: words.add(index, newWord);
377: index++;
378: if (i >= sword.length()) {
379: break;
380: } else {
381: first = i;
382: firstChar = sword.charAt(first);
383: }
384: }
385:
386: if (index > (listIndex + 1)) {
387: words.remove(listIndex);
388: }
389: }
390:
391: private static boolean isMark(char c) {
392: return Character.isJavaIdentifierPart(c)
393: && !Character.isLetter(c) && !Character.isDigit(c);
394: }
395:
396: private static boolean isCasedLetter(char c) {
397: return Character.isUpperCase(c) || Character.isLowerCase(c);
398: }
399:
400: private static boolean isConventionalIdentifier(StringBuffer buf,
401: IdentifierType type) {
402: if (null == buf || buf.length() == 0) {
403: return false;
404: }
405: boolean result = false;
406: if (IdentifierType.CONSTANT == type) {
407: for (int i = 0; i < buf.length(); i++) {
408: if (Character.isLowerCase(buf.charAt(i))) {
409: return false;
410: }
411: }
412: result = true;
413: } else if (IdentifierType.VARIABLE == type) {
414: result = Character.isLowerCase(buf.charAt(0));
415: } else {
416: int pos = 3;
417: if (IdentifierType.GETTER == type
418: && !(buf.length() >= pos && "get".equals(buf
419: .subSequence(0, 3)))) {
420: return false;
421: } else if (IdentifierType.SETTER == type
422: && !(buf.length() >= pos && "set".equals(buf
423: .subSequence(0, 3)))) {
424: return false;
425: } else {
426: pos = 0;
427: }
428: result = Character.isUpperCase(buf.charAt(pos));
429: }
430: return result;
431: }
432:
433: private static String makeConventionalIdentifier(
434: List<String> words, IdentifierType type) {
435: StringBuffer buf = new StringBuffer();
436: boolean firstWord = true;
437: if (IdentifierType.GETTER == type) {
438: buf.append("get");
439: } else if (IdentifierType.SETTER == type) {
440: buf.append("set");
441: }
442: for (String w : words) {
443: int l = buf.length();
444: if (l > 0 && IdentifierType.CONSTANT == type) {
445: buf.append('_');
446: l++;
447: }
448: buf.append(w);
449: if (IdentifierType.CONSTANT == type) {
450: for (int i = l; i < buf.length(); i++) {
451: if (Character.isLowerCase(buf.charAt(i))) {
452: buf.setCharAt(i, Character.toUpperCase(buf
453: .charAt(i)));
454: }
455: }
456: } else if (IdentifierType.VARIABLE == type) {
457: if (firstWord && Character.isUpperCase(buf.charAt(l))) {
458: buf.setCharAt(l, Character.toLowerCase(buf
459: .charAt(l)));
460: }
461: } else {
462: if (firstWord && Character.isLowerCase(buf.charAt(l))) {
463: buf.setCharAt(l, Character.toUpperCase(buf
464: .charAt(l)));
465: }
466: }
467: firstWord = false;
468: }
469: return buf.toString();
470: }
471:
472: public static Class<?> getValidClass(Class<?> cls) {
473: if (cls.isEnum()) {
474: return cls;
475: }
476: if (cls.isArray()) {
477: return cls;
478: }
479:
480: if (cls == Object.class || cls == String.class
481: || cls == Holder.class) {
482: cls = null;
483: } else if (cls.isPrimitive() || cls.isInterface()
484: || cls.isAnnotation()) {
485: cls = null;
486: }
487: if (cls != null) {
488: try {
489: if (cls.getConstructor(new Class[0]) == null) {
490: cls = null;
491: }
492: } catch (NoSuchMethodException ex) {
493: cls = null;
494: }
495: }
496: return cls;
497: }
498:
499: }
|