aoqi@0: /* aoqi@0: * Copyright (c) 1997, 2012, 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.dtd.bindinfo; aoqi@0: aoqi@0: import java.io.IOException; aoqi@0: import java.io.StringReader; aoqi@0: import java.util.Map; aoqi@0: aoqi@0: import javax.xml.bind.annotation.adapters.XmlAdapter; aoqi@0: import javax.xml.parsers.DocumentBuilderFactory; aoqi@0: import javax.xml.parsers.ParserConfigurationException; 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.JPrimitiveType; aoqi@0: import com.sun.codemodel.internal.JType; aoqi@0: import com.sun.codemodel.internal.JVar; 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: aoqi@0: import com.sun.xml.internal.bind.v2.util.XmlFactory; aoqi@0: import org.w3c.dom.Element; aoqi@0: import org.xml.sax.InputSource; aoqi@0: import org.xml.sax.Locator; aoqi@0: import org.xml.sax.SAXException; aoqi@0: aoqi@0: /** aoqi@0: * <conversion> declaration in the binding file. aoqi@0: * This declaration declares a conversion by user-specified methods. aoqi@0: */ aoqi@0: public class BIUserConversion implements BIConversion aoqi@0: { aoqi@0: /** aoqi@0: * Wraps a given <conversion> element in the binding file. aoqi@0: */ aoqi@0: BIUserConversion( BindInfo bi, Element _e ) { aoqi@0: this.owner = bi; aoqi@0: this.e = _e; aoqi@0: } aoqi@0: aoqi@0: private static void add( Map m, BIConversion c ) { aoqi@0: m.put( c.name(), c ); aoqi@0: } aoqi@0: aoqi@0: /** Adds all built-in conversions into the given map. */ aoqi@0: static void addBuiltinConversions( BindInfo bi, Map m ) { aoqi@0: add( m, new BIUserConversion( bi, parse(""))); aoqi@0: add( m, new BIUserConversion( bi, parse(""))); aoqi@0: add( m, new BIUserConversion( bi, parse(""))); aoqi@0: add( m, new BIUserConversion( bi, parse(""))); aoqi@0: add( m, new BIUserConversion( bi, parse(""))); aoqi@0: add( m, new BIUserConversion( bi, parse(""))); aoqi@0: add( m, new BIUserConversion( bi, parse(""))); aoqi@0: } aoqi@0: aoqi@0: private static Element parse(String text) { aoqi@0: try { aoqi@0: //this is parsing well known schemas, do not configure secure processing - always true aoqi@0: DocumentBuilderFactory dbf = XmlFactory.createDocumentBuilderFactory(false); aoqi@0: InputSource is = new InputSource(new StringReader(text)); aoqi@0: return dbf.newDocumentBuilder().parse(is).getDocumentElement(); aoqi@0: } catch (SAXException x) { aoqi@0: throw new Error(x); aoqi@0: } catch (IOException x) { aoqi@0: throw new Error(x); aoqi@0: } catch (ParserConfigurationException x) { aoqi@0: throw new Error(x); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: aoqi@0: /** The owner {@link BindInfo} object to which this object belongs. */ aoqi@0: private final BindInfo owner; aoqi@0: aoqi@0: /** <conversion> element which this object is wrapping. */ aoqi@0: private final Element e; aoqi@0: aoqi@0: aoqi@0: aoqi@0: /** Gets the location where this declaration is declared. */ aoqi@0: public Locator getSourceLocation() { aoqi@0: return DOMLocator.getLocationInfo(e); aoqi@0: } aoqi@0: aoqi@0: /** Gets the conversion name. */ aoqi@0: public String name() { return DOMUtil.getAttribute(e,"name"); } aoqi@0: aoqi@0: /** Gets a transducer for this conversion. */ aoqi@0: public TypeUse getTransducer() { aoqi@0: aoqi@0: String ws = DOMUtil.getAttribute(e,"whitespace"); aoqi@0: if(ws==null) ws = "collapse"; aoqi@0: aoqi@0: String type = DOMUtil.getAttribute(e,"type"); aoqi@0: if(type==null) type=name(); aoqi@0: JType t=null; aoqi@0: aoqi@0: int idx = type.lastIndexOf('.'); aoqi@0: if(idx<0) { aoqi@0: // no package name is specified. aoqi@0: try { aoqi@0: t = JPrimitiveType.parse(owner.codeModel,type); aoqi@0: } catch( IllegalArgumentException ex ) { aoqi@0: // otherwise treat it as a class name in the current package aoqi@0: type = owner.getTargetPackage().name()+'.'+type; aoqi@0: } aoqi@0: } aoqi@0: if(t==null) { aoqi@0: try { aoqi@0: // TODO: revisit this later aoqi@0: JDefinedClass cls = owner.codeModel._class(type); aoqi@0: cls.hide(); aoqi@0: t = cls; aoqi@0: } catch( JClassAlreadyExistsException ex ) { aoqi@0: t = ex.getExistingClass(); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: String parse = DOMUtil.getAttribute(e,"parse"); aoqi@0: if(parse==null) parse="new"; aoqi@0: aoqi@0: String print = DOMUtil.getAttribute(e,"print"); aoqi@0: if(print==null) print="toString"; aoqi@0: aoqi@0: JDefinedClass adapter = generateAdapter(owner.codeModel, parse, print, t.boxify()); aoqi@0: aoqi@0: // XmlJavaType customization always converts between string and an user-defined type. aoqi@0: return TypeUseFactory.adapt(CBuiltinLeafInfo.STRING,new CAdapter(adapter)); aoqi@0: } aoqi@0: aoqi@0: // TODO: anyway to reuse this code between XML Schema compiler? aoqi@0: private JDefinedClass generateAdapter(JCodeModel cm, String parseMethod, String printMethod, JClass inMemoryType) { aoqi@0: JDefinedClass adapter = null; aoqi@0: aoqi@0: int id = 1; aoqi@0: while(adapter==null) { aoqi@0: try { aoqi@0: JPackage pkg = owner.getTargetPackage(); aoqi@0: adapter = pkg._class("Adapter"+id); aoqi@0: } catch (JClassAlreadyExistsException ex) { 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: adapter._extends(cm.ref(XmlAdapter.class).narrow(String.class).narrow(inMemoryType)); aoqi@0: aoqi@0: JMethod unmarshal = adapter.method(JMod.PUBLIC, inMemoryType, "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(inMemoryType).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 = inMemoryType.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(inMemoryType,"value"); 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: } else { aoqi@0: // RESULT: .() 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: }