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.tools.internal.xjc.reader.xmlschema.bindinfo;
027:
028: import javax.xml.bind.DatatypeConverter;
029: import javax.xml.bind.annotation.XmlAttribute;
030: import javax.xml.bind.annotation.XmlRootElement;
031: import javax.xml.bind.annotation.adapters.XmlAdapter;
032: import javax.xml.namespace.QName;
033:
034: import com.sun.codemodel.internal.JClass;
035: import com.sun.codemodel.internal.JClassAlreadyExistsException;
036: import com.sun.codemodel.internal.JCodeModel;
037: import com.sun.codemodel.internal.JDefinedClass;
038: import com.sun.codemodel.internal.JExpr;
039: import com.sun.codemodel.internal.JExpression;
040: import com.sun.codemodel.internal.JMethod;
041: import com.sun.codemodel.internal.JMod;
042: import com.sun.codemodel.internal.JPackage;
043: import com.sun.codemodel.internal.JType;
044: import com.sun.codemodel.internal.JVar;
045: import com.sun.tools.internal.xjc.ErrorReceiver;
046: import com.sun.tools.internal.xjc.model.CAdapter;
047: import com.sun.tools.internal.xjc.model.CBuiltinLeafInfo;
048: import com.sun.tools.internal.xjc.model.TypeUse;
049: import com.sun.tools.internal.xjc.model.TypeUseFactory;
050: import com.sun.tools.internal.xjc.reader.Const;
051: import com.sun.tools.internal.xjc.reader.Ring;
052: import com.sun.tools.internal.xjc.reader.TypeUtil;
053: import com.sun.tools.internal.xjc.reader.xmlschema.ClassSelector;
054: import com.sun.xml.internal.bind.v2.WellKnownNamespace;
055: import com.sun.xml.internal.xsom.XSSimpleType;
056:
057: import org.xml.sax.Locator;
058:
059: /**
060: * Conversion declaration.
061: *
062: * <p>
063: * A conversion declaration specifies how an XML type gets mapped
064: * to a Java type.
065: *
066: * @author
067: * Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com)
068: */
069: public abstract class BIConversion extends AbstractDeclarationImpl {
070: @Deprecated
071: public BIConversion(Locator loc) {
072: super (loc);
073: }
074:
075: protected BIConversion() {
076: }
077:
078: /**
079: * Gets the {@link TypeUse} object that this conversion represents.
080: * <p>
081: * The returned {@link TypeUse} object is properly adapted.
082: *
083: * @param owner
084: * A {@link BIConversion} is always associated with one
085: * {@link XSSimpleType}, but that's not always available
086: * when a {@link BIConversion} is built. So we pass this
087: * as a parameter to this method.
088: */
089: public abstract TypeUse getTypeUse(XSSimpleType owner);
090:
091: public QName getName() {
092: return NAME;
093: }
094:
095: /** Name of the conversion declaration. */
096: public static final QName NAME = new QName(Const.JAXB_NSURI,
097: "conversion");
098:
099: /**
100: * Implementation that returns a statically-determined constant {@link TypeUse}.
101: */
102: public static final class Static extends BIConversion {
103: /**
104: * Always non-null.
105: */
106: private final TypeUse transducer;
107:
108: public Static(Locator loc, TypeUse transducer) {
109: super (loc);
110: this .transducer = transducer;
111: }
112:
113: public TypeUse getTypeUse(XSSimpleType owner) {
114: return transducer;
115: }
116: }
117:
118: /**
119: * User-specified <javaType> customization.
120: *
121: * The parse/print methods are allowed to be null,
122: * and their default values are determined based on the
123: * owner of the token.
124: */
125: @XmlRootElement(name="javaType")
126: public static class User extends BIConversion {
127: @XmlAttribute
128: private String parseMethod;
129: @XmlAttribute
130: private String printMethod;
131: @XmlAttribute(name="name")
132: private String type = "java.lang.String";
133:
134: /**
135: * If null, computed from {@link #type}.
136: * Sometimes this can be set instead of {@link #type}.
137: */
138: private JType inMemoryType;
139:
140: public User(Locator loc, String parseMethod,
141: String printMethod, JType inMemoryType) {
142: super (loc);
143: this .parseMethod = parseMethod;
144: this .printMethod = printMethod;
145: this .inMemoryType = inMemoryType;
146: }
147:
148: public User() {
149: }
150:
151: /**
152: * Cache used by {@link #getTypeUse(XSSimpleType)} to improve the performance.
153: */
154: private TypeUse typeUse;
155:
156: public TypeUse getTypeUse(XSSimpleType owner) {
157: if (typeUse != null)
158: return typeUse;
159:
160: JCodeModel cm = getCodeModel();
161:
162: if (inMemoryType == null)
163: inMemoryType = TypeUtil.getType(cm, type, Ring
164: .get(ErrorReceiver.class), getLocation());
165:
166: JDefinedClass adapter = generateAdapter(
167: parseMethodFor(owner), printMethodFor(owner), owner);
168:
169: // XmlJavaType customization always converts between string and an user-defined type.
170: typeUse = TypeUseFactory.adapt(CBuiltinLeafInfo.STRING,
171: new CAdapter(adapter));
172:
173: return typeUse;
174: }
175:
176: /**
177: * generate the adapter class.
178: */
179: private JDefinedClass generateAdapter(String parseMethod,
180: String printMethod, XSSimpleType owner) {
181: JDefinedClass adapter = null;
182:
183: int id = 1;
184: while (adapter == null) {
185: try {
186: JPackage pkg = Ring.get(ClassSelector.class)
187: .getClassScope().getOwnerPackage();
188: adapter = pkg._class("Adapter" + id);
189: } catch (JClassAlreadyExistsException e) {
190: // try another name in search for an unique name.
191: // this isn't too efficient, but we expect people to usually use
192: // a very small number of adapters.
193: id++;
194: }
195: }
196:
197: JClass bim = inMemoryType.boxify();
198:
199: adapter._extends(getCodeModel().ref(XmlAdapter.class)
200: .narrow(String.class).narrow(bim));
201:
202: JMethod unmarshal = adapter.method(JMod.PUBLIC, bim,
203: "unmarshal");
204: JVar $value = unmarshal.param(String.class, "value");
205:
206: JExpression inv;
207:
208: if (parseMethod.equals("new")) {
209: // "new" indicates that the constructor of the target type
210: // will do the unmarshalling.
211:
212: // RESULT: new <type>()
213: inv = JExpr._new(bim).arg($value);
214: } else {
215: int idx = parseMethod.lastIndexOf('.');
216: if (idx < 0) {
217: // parseMethod specifies the static method of the target type
218: // which will do the unmarshalling.
219:
220: // because of an error check at the constructor,
221: // we can safely assume that this cast works.
222: inv = bim.staticInvoke(parseMethod).arg($value);
223: } else {
224: inv = JExpr.direct(parseMethod + "(value)");
225: }
226: }
227: unmarshal.body()._return(inv);
228:
229: JMethod marshal = adapter.method(JMod.PUBLIC, String.class,
230: "marshal");
231: $value = marshal.param(bim, "value");
232:
233: if (printMethod
234: .startsWith("javax.xml.bind.DatatypeConverter.")) {
235: // UGLY: if this conversion is the system-driven conversion,
236: // check for null
237: marshal.body()._if($value.eq(JExpr._null()))._then()
238: ._return(JExpr._null());
239: }
240:
241: int idx = printMethod.lastIndexOf('.');
242: if (idx < 0) {
243: // printMethod specifies a method in the target type
244: // which performs the serialization.
245:
246: // RESULT: <value>.<method>()
247: inv = $value.invoke(printMethod);
248: } else {
249: // RESULT: <className>.<method>(<value>)
250: if (this .printMethod == null) {
251: // HACK HACK HACK
252: JType t = inMemoryType.unboxify();
253: inv = JExpr.direct(printMethod + "(("
254: + findBaseConversion(owner).toLowerCase()
255: + ")(" + t.fullName() + ")value)");
256: } else
257: inv = JExpr.direct(printMethod + "(value)");
258: }
259: marshal.body()._return(inv);
260:
261: return adapter;
262: }
263:
264: private String printMethodFor(XSSimpleType owner) {
265: if (printMethod != null)
266: return printMethod;
267:
268: String method = getConversionMethod("print", owner);
269: if (method != null)
270: return method;
271:
272: return "toString";
273: }
274:
275: private String parseMethodFor(XSSimpleType owner) {
276: if (parseMethod != null)
277: return parseMethod;
278:
279: String method = getConversionMethod("parse", owner);
280: if (method != null) {
281: // this cast is necessary for conversion between primitive Java types
282: return '(' + inMemoryType.unboxify().fullName() + ')'
283: + method;
284: }
285:
286: return "new";
287: }
288:
289: private static final String[] knownBases = new String[] {
290: "Float", "Double", "Byte", "Short", "Int", "Long",
291: "Boolean" };
292:
293: private String getConversionMethod(String methodPrefix,
294: XSSimpleType owner) {
295: String bc = findBaseConversion(owner);
296: if (bc == null)
297: return null;
298:
299: return DatatypeConverter.class.getName() + '.'
300: + methodPrefix + bc;
301: }
302:
303: private String findBaseConversion(XSSimpleType owner) {
304: // find the base simple type mapping.
305: for (XSSimpleType st = owner; st != null; st = st
306: .getSimpleBaseType()) {
307: if (!WellKnownNamespace.XML_SCHEMA.equals(st
308: .getTargetNamespace()))
309: continue; // user-defined type
310:
311: String name = st.getName().intern();
312: for (String s : knownBases)
313: if (name.equalsIgnoreCase(s))
314: return s;
315: }
316:
317: return null;
318: }
319:
320: public QName getName() {
321: return NAME;
322: }
323:
324: /** Name of the conversion declaration. */
325: public static final QName NAME = new QName(Const.JAXB_NSURI,
326: "javaType");
327: }
328:
329: @XmlRootElement(name="javaType",namespace=Const.XJC_EXTENSION_URI)
330: public static class UserAdapter extends BIConversion {
331: @XmlAttribute(name="name")
332: private String type = null;
333:
334: @XmlAttribute
335: private String adapter = null;
336:
337: private TypeUse typeUse;
338:
339: public TypeUse getTypeUse(XSSimpleType owner) {
340: if (typeUse != null)
341: return typeUse;
342:
343: JCodeModel cm = getCodeModel();
344:
345: JDefinedClass a;
346: try {
347: a = cm._class(adapter);
348: a.hide(); // we assume this is given by the user
349: a._extends(cm.ref(XmlAdapter.class)
350: .narrow(String.class).narrow(cm.ref(type)));
351: } catch (JClassAlreadyExistsException e) {
352: a = e.getExistingClass();
353: }
354:
355: // TODO: it's not correct to say that it adapts from String,
356: // but OTOH I don't think we can compute that.
357: typeUse = TypeUseFactory.adapt(CBuiltinLeafInfo.STRING,
358: new CAdapter(a));
359:
360: return typeUse;
361: }
362: }
363: }
|