aoqi@0: /* aoqi@0: * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. aoqi@0: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. aoqi@0: * aoqi@0: * This code is free software; you can redistribute it and/or modify it aoqi@0: * under the terms of the GNU General Public License version 2 only, as aoqi@0: * published by the Free Software Foundation. Oracle designates this aoqi@0: * particular file as subject to the "Classpath" exception as provided aoqi@0: * by Oracle in the LICENSE file that accompanied this code. aoqi@0: * aoqi@0: * This code is distributed in the hope that it will be useful, but WITHOUT aoqi@0: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or aoqi@0: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License aoqi@0: * version 2 for more details (a copy is included in the LICENSE file that aoqi@0: * accompanied this code). aoqi@0: * aoqi@0: * You should have received a copy of the GNU General Public License version aoqi@0: * 2 along with this work; if not, write to the Free Software Foundation, aoqi@0: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. aoqi@0: * aoqi@0: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA aoqi@0: * or visit www.oracle.com if you need additional information or have any aoqi@0: * questions. aoqi@0: */ aoqi@0: aoqi@0: package com.sun.tools.internal.xjc.reader.xmlschema.bindinfo; aoqi@0: aoqi@0: import javax.xml.bind.DatatypeConverter; aoqi@0: import javax.xml.bind.annotation.XmlAttribute; aoqi@0: import javax.xml.bind.annotation.XmlRootElement; aoqi@0: import javax.xml.bind.annotation.adapters.XmlAdapter; aoqi@0: import javax.xml.namespace.QName; aoqi@0: aoqi@0: import com.sun.codemodel.internal.JClass; aoqi@0: import com.sun.codemodel.internal.JClassAlreadyExistsException; aoqi@0: import com.sun.codemodel.internal.JCodeModel; aoqi@0: import com.sun.codemodel.internal.JDefinedClass; aoqi@0: import com.sun.codemodel.internal.JExpr; aoqi@0: import com.sun.codemodel.internal.JExpression; aoqi@0: import com.sun.codemodel.internal.JMethod; aoqi@0: import com.sun.codemodel.internal.JMod; aoqi@0: import com.sun.codemodel.internal.JPackage; aoqi@0: import com.sun.codemodel.internal.JType; aoqi@0: import com.sun.codemodel.internal.JVar; aoqi@0: import com.sun.codemodel.internal.JConditional; aoqi@0: import com.sun.tools.internal.xjc.ErrorReceiver; aoqi@0: import com.sun.tools.internal.xjc.model.CAdapter; aoqi@0: import com.sun.tools.internal.xjc.model.CBuiltinLeafInfo; aoqi@0: import com.sun.tools.internal.xjc.model.TypeUse; aoqi@0: import com.sun.tools.internal.xjc.model.TypeUseFactory; aoqi@0: import com.sun.tools.internal.xjc.reader.Const; aoqi@0: import com.sun.tools.internal.xjc.reader.Ring; aoqi@0: import com.sun.tools.internal.xjc.reader.TypeUtil; aoqi@0: import com.sun.tools.internal.xjc.reader.xmlschema.ClassSelector; aoqi@0: import com.sun.xml.internal.bind.v2.WellKnownNamespace; aoqi@0: import com.sun.xml.internal.xsom.XSSimpleType; aoqi@0: aoqi@0: import org.xml.sax.Locator; aoqi@0: aoqi@0: /** aoqi@0: * Conversion declaration. aoqi@0: * aoqi@0: *

aoqi@0: * A conversion declaration specifies how an XML type gets mapped aoqi@0: * to a Java type. aoqi@0: * aoqi@0: * @author aoqi@0: * Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com) aoqi@0: */ aoqi@0: public abstract class BIConversion extends AbstractDeclarationImpl { aoqi@0: @Deprecated aoqi@0: public BIConversion( Locator loc ) { aoqi@0: super(loc); aoqi@0: } aoqi@0: aoqi@0: protected BIConversion() { aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Gets the {@link TypeUse} object that this conversion represents. aoqi@0: *

aoqi@0: * The returned {@link TypeUse} object is properly adapted. aoqi@0: * aoqi@0: * @param owner aoqi@0: * A {@link BIConversion} is always associated with one aoqi@0: * {@link XSSimpleType}, but that's not always available aoqi@0: * when a {@link BIConversion} is built. So we pass this aoqi@0: * as a parameter to this method. aoqi@0: */ aoqi@0: public abstract TypeUse getTypeUse( XSSimpleType owner ); aoqi@0: aoqi@0: public QName getName() { return NAME; } aoqi@0: aoqi@0: /** Name of the conversion declaration. */ aoqi@0: public static final QName NAME = new QName( aoqi@0: Const.JAXB_NSURI, "conversion" ); aoqi@0: aoqi@0: /** aoqi@0: * Implementation that returns a statically-determined constant {@link TypeUse}. aoqi@0: */ aoqi@0: public static final class Static extends BIConversion { aoqi@0: /** aoqi@0: * Always non-null. aoqi@0: */ aoqi@0: private final TypeUse transducer; aoqi@0: aoqi@0: public Static(Locator loc, TypeUse transducer) { aoqi@0: super(loc); aoqi@0: this.transducer = transducer; aoqi@0: } aoqi@0: aoqi@0: public TypeUse getTypeUse(XSSimpleType owner) { aoqi@0: return transducer; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * User-specified <javaType> customization. aoqi@0: * aoqi@0: * The parse/print methods are allowed to be null, aoqi@0: * and their default values are determined based on the aoqi@0: * owner of the token. aoqi@0: */ aoqi@0: @XmlRootElement(name="javaType") aoqi@0: public static class User extends BIConversion { aoqi@0: @XmlAttribute aoqi@0: private String parseMethod; aoqi@0: @XmlAttribute aoqi@0: private String printMethod; aoqi@0: @XmlAttribute(name="name") aoqi@0: private String type = "java.lang.String"; aoqi@0: aoqi@0: /** aoqi@0: * If null, computed from {@link #type}. aoqi@0: * Sometimes this can be set instead of {@link #type}. aoqi@0: */ aoqi@0: private JType inMemoryType; aoqi@0: aoqi@0: public User(Locator loc, String parseMethod, String printMethod, JType inMemoryType) { aoqi@0: super(loc); aoqi@0: this.parseMethod = parseMethod; aoqi@0: this.printMethod = printMethod; aoqi@0: this.inMemoryType = inMemoryType; aoqi@0: } aoqi@0: aoqi@0: public User() { aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Cache used by {@link #getTypeUse(XSSimpleType)} to improve the performance. aoqi@0: */ aoqi@0: private TypeUse typeUse; aoqi@0: aoqi@0: public TypeUse getTypeUse(XSSimpleType owner) { aoqi@0: if(typeUse!=null) aoqi@0: return typeUse; aoqi@0: aoqi@0: JCodeModel cm = getCodeModel(); aoqi@0: aoqi@0: if(inMemoryType==null) aoqi@0: inMemoryType = TypeUtil.getType(cm,type,Ring.get(ErrorReceiver.class),getLocation()); aoqi@0: aoqi@0: JDefinedClass adapter = generateAdapter(parseMethodFor(owner),printMethodFor(owner),owner); aoqi@0: aoqi@0: // XmlJavaType customization always converts between string and an user-defined type. aoqi@0: typeUse = TypeUseFactory.adapt(CBuiltinLeafInfo.STRING,new CAdapter(adapter)); aoqi@0: aoqi@0: return typeUse; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * generate the adapter class. aoqi@0: */ aoqi@0: private JDefinedClass generateAdapter(String parseMethod, String printMethod,XSSimpleType owner) { aoqi@0: JDefinedClass adapter = null; aoqi@0: aoqi@0: int id = 1; aoqi@0: while(adapter==null) { aoqi@0: try { aoqi@0: JPackage pkg = Ring.get(ClassSelector.class).getClassScope().getOwnerPackage(); aoqi@0: adapter = pkg._class("Adapter"+id); aoqi@0: } catch (JClassAlreadyExistsException e) { aoqi@0: // try another name in search for an unique name. aoqi@0: // this isn't too efficient, but we expect people to usually use aoqi@0: // a very small number of adapters. aoqi@0: id++; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: JClass bim = inMemoryType.boxify(); aoqi@0: aoqi@0: adapter._extends(getCodeModel().ref(XmlAdapter.class).narrow(String.class).narrow(bim)); aoqi@0: aoqi@0: JMethod unmarshal = adapter.method(JMod.PUBLIC, bim, "unmarshal"); aoqi@0: JVar $value = unmarshal.param(String.class, "value"); aoqi@0: aoqi@0: JExpression inv; aoqi@0: aoqi@0: if( parseMethod.equals("new") ) { aoqi@0: // "new" indicates that the constructor of the target type aoqi@0: // will do the unmarshalling. aoqi@0: aoqi@0: // RESULT: new () aoqi@0: inv = JExpr._new(bim).arg($value); aoqi@0: } else { aoqi@0: int idx = parseMethod.lastIndexOf('.'); aoqi@0: if(idx<0) { aoqi@0: // parseMethod specifies the static method of the target type aoqi@0: // which will do the unmarshalling. aoqi@0: aoqi@0: // because of an error check at the constructor, aoqi@0: // we can safely assume that this cast works. aoqi@0: inv = bim.staticInvoke(parseMethod).arg($value); aoqi@0: } else { aoqi@0: inv = JExpr.direct(parseMethod+"(value)"); aoqi@0: } aoqi@0: } aoqi@0: unmarshal.body()._return(inv); aoqi@0: aoqi@0: aoqi@0: JMethod marshal = adapter.method(JMod.PUBLIC, String.class, "marshal"); aoqi@0: $value = marshal.param(bim,"value"); aoqi@0: aoqi@0: if(printMethod.startsWith("javax.xml.bind.DatatypeConverter.")) { aoqi@0: // UGLY: if this conversion is the system-driven conversion, aoqi@0: // check for null aoqi@0: marshal.body()._if($value.eq(JExpr._null()))._then()._return(JExpr._null()); aoqi@0: } aoqi@0: aoqi@0: int idx = printMethod.lastIndexOf('.'); aoqi@0: if(idx<0) { aoqi@0: // printMethod specifies a method in the target type aoqi@0: // which performs the serialization. aoqi@0: aoqi@0: // RESULT: .() aoqi@0: inv = $value.invoke(printMethod); aoqi@0: aoqi@0: // check value is not null ... if(value == null) return null; aoqi@0: JConditional jcon = marshal.body()._if($value.eq(JExpr._null())); aoqi@0: jcon._then()._return(JExpr._null()); aoqi@0: } else { aoqi@0: // RESULT: .() aoqi@0: if(this.printMethod==null) { aoqi@0: // HACK HACK HACK aoqi@0: JType t = inMemoryType.unboxify(); aoqi@0: inv = JExpr.direct(printMethod+"(("+findBaseConversion(owner).toLowerCase()+")("+t.fullName()+")value)"); aoqi@0: } else aoqi@0: inv = JExpr.direct(printMethod+"(value)"); aoqi@0: } aoqi@0: marshal.body()._return(inv); aoqi@0: aoqi@0: return adapter; aoqi@0: } aoqi@0: aoqi@0: private String printMethodFor(XSSimpleType owner) { aoqi@0: if(printMethod!=null) return printMethod; aoqi@0: aoqi@0: if(inMemoryType.unboxify().isPrimitive()) { aoqi@0: String method = getConversionMethod("print",owner); aoqi@0: if(method!=null) aoqi@0: return method; aoqi@0: } aoqi@0: aoqi@0: return "toString"; aoqi@0: } aoqi@0: aoqi@0: private String parseMethodFor(XSSimpleType owner) { aoqi@0: if(parseMethod!=null) return parseMethod; aoqi@0: aoqi@0: if(inMemoryType.unboxify().isPrimitive()) { aoqi@0: String method = getConversionMethod("parse", owner); aoqi@0: if(method!=null) { aoqi@0: // this cast is necessary for conversion between primitive Java types aoqi@0: return '('+inMemoryType.unboxify().fullName()+')'+method; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: return "new"; aoqi@0: } aoqi@0: aoqi@0: private static final String[] knownBases = new String[]{ aoqi@0: "Float", "Double", "Byte", "Short", "Int", "Long", "Boolean" aoqi@0: }; aoqi@0: aoqi@0: private String getConversionMethod(String methodPrefix, XSSimpleType owner) { aoqi@0: String bc = findBaseConversion(owner); aoqi@0: if(bc==null) return null; aoqi@0: aoqi@0: return DatatypeConverter.class.getName()+'.'+methodPrefix+bc; aoqi@0: } aoqi@0: aoqi@0: private String findBaseConversion(XSSimpleType owner) { aoqi@0: // find the base simple type mapping. aoqi@0: for( XSSimpleType st=owner; st!=null; st = st.getSimpleBaseType() ) { aoqi@0: if( !WellKnownNamespace.XML_SCHEMA.equals(st.getTargetNamespace()) ) aoqi@0: continue; // user-defined type aoqi@0: aoqi@0: String name = st.getName().intern(); aoqi@0: for( String s : knownBases ) aoqi@0: if(name.equalsIgnoreCase(s)) aoqi@0: return s; aoqi@0: } aoqi@0: aoqi@0: return null; aoqi@0: } aoqi@0: aoqi@0: public QName getName() { return NAME; } aoqi@0: aoqi@0: /** Name of the conversion declaration. */ aoqi@0: public static final QName NAME = new QName( aoqi@0: Const.JAXB_NSURI, "javaType" ); aoqi@0: } aoqi@0: aoqi@0: @XmlRootElement(name="javaType",namespace=Const.XJC_EXTENSION_URI) aoqi@0: public static class UserAdapter extends BIConversion { aoqi@0: @XmlAttribute(name="name") aoqi@0: private String type = null; aoqi@0: aoqi@0: @XmlAttribute aoqi@0: private String adapter = null; aoqi@0: aoqi@0: private TypeUse typeUse; aoqi@0: aoqi@0: public TypeUse getTypeUse(XSSimpleType owner) { aoqi@0: if(typeUse!=null) aoqi@0: return typeUse; aoqi@0: aoqi@0: JCodeModel cm = getCodeModel(); aoqi@0: aoqi@0: JDefinedClass a; aoqi@0: try { aoqi@0: a = cm._class(adapter); aoqi@0: a.hide(); // we assume this is given by the user aoqi@0: a._extends(cm.ref(XmlAdapter.class).narrow(String.class).narrow( aoqi@0: cm.ref(type))); aoqi@0: } catch (JClassAlreadyExistsException e) { aoqi@0: a = e.getExistingClass(); aoqi@0: } aoqi@0: aoqi@0: // TODO: it's not correct to say that it adapts from String, aoqi@0: // but OTOH I don't think we can compute that. aoqi@0: typeUse = TypeUseFactory.adapt( aoqi@0: CBuiltinLeafInfo.STRING, aoqi@0: new CAdapter(a)); aoqi@0: aoqi@0: return typeUse; aoqi@0: } aoqi@0: } aoqi@0: }