1 /* 2 * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.tools.internal.xjc.reader.dtd.bindinfo; 27 28 import java.io.IOException; 29 import java.io.StringReader; 30 import java.util.Map; 31 32 import javax.xml.bind.annotation.adapters.XmlAdapter; 33 import javax.xml.parsers.DocumentBuilderFactory; 34 import javax.xml.parsers.ParserConfigurationException; 35 36 import com.sun.codemodel.internal.JClass; 37 import com.sun.codemodel.internal.JClassAlreadyExistsException; 38 import com.sun.codemodel.internal.JCodeModel; 39 import com.sun.codemodel.internal.JDefinedClass; 40 import com.sun.codemodel.internal.JExpr; 41 import com.sun.codemodel.internal.JExpression; 42 import com.sun.codemodel.internal.JMethod; 43 import com.sun.codemodel.internal.JMod; 44 import com.sun.codemodel.internal.JPackage; 45 import com.sun.codemodel.internal.JPrimitiveType; 46 import com.sun.codemodel.internal.JType; 47 import com.sun.codemodel.internal.JVar; 48 import com.sun.tools.internal.xjc.model.CAdapter; 49 import com.sun.tools.internal.xjc.model.CBuiltinLeafInfo; 50 import com.sun.tools.internal.xjc.model.TypeUse; 51 import com.sun.tools.internal.xjc.model.TypeUseFactory; 52 53 import org.w3c.dom.Element; 54 import org.xml.sax.InputSource; 55 import org.xml.sax.Locator; 56 import org.xml.sax.SAXException; 57 58 /** 59 * <conversion> declaration in the binding file. 60 * This declaration declares a conversion by user-specified methods. 61 */ 62 public class BIUserConversion implements BIConversion 63 { 64 /** 65 * Wraps a given <conversion> element in the binding file. 66 */ 67 BIUserConversion( BindInfo bi, Element _e ) { 68 this.owner = bi; 69 this.e = _e; 70 } 71 72 private static void add( Map<String,BIConversion> m, BIConversion c ) { 73 m.put( c.name(), c ); 74 } 75 76 /** Adds all built-in conversions into the given map. */ 77 static void addBuiltinConversions( BindInfo bi, Map<String,BIConversion> m ) { 78 add( m, new BIUserConversion( bi, parse("<conversion name='boolean' type='java.lang.Boolean' parse='getBoolean' />"))); 79 add( m, new BIUserConversion( bi, parse("<conversion name='byte' type='java.lang.Byte' parse='parseByte' />"))); 80 add( m, new BIUserConversion( bi, parse("<conversion name='short' type='java.lang.Short' parse='parseShort' />"))); 81 add( m, new BIUserConversion( bi, parse("<conversion name='int' type='java.lang.Integer' parse='parseInt' />"))); 82 add( m, new BIUserConversion( bi, parse("<conversion name='long' type='java.lang.Long' parse='parseLong' />"))); 83 add( m, new BIUserConversion( bi, parse("<conversion name='float' type='java.lang.Float' parse='parseFloat' />"))); 84 add( m, new BIUserConversion( bi, parse("<conversion name='double' type='java.lang.Double' parse='parseDouble' />"))); 85 } 86 87 private static Element parse(String text) { 88 try { 89 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 90 dbf.setNamespaceAware(true); 91 InputSource is = new InputSource(new StringReader(text)); 92 return dbf.newDocumentBuilder().parse(is).getDocumentElement(); 93 } catch (SAXException x) { 94 throw new Error(x); 95 } catch (IOException x) { 96 throw new Error(x); 97 } catch (ParserConfigurationException x) { 98 throw new Error(x); 99 } 100 } 101 102 103 /** The owner {@link BindInfo} object to which this object belongs. */ 104 private final BindInfo owner; 105 106 /** <conversion> element which this object is wrapping. */ 107 private final Element e; 108 109 110 111 /** Gets the location where this declaration is declared. */ 112 public Locator getSourceLocation() { 113 return DOMLocator.getLocationInfo(e); 114 } 115 116 /** Gets the conversion name. */ 117 public String name() { return DOMUtil.getAttribute(e,"name"); } 118 119 /** Gets a transducer for this conversion. */ 120 public TypeUse getTransducer() { 121 122 String ws = DOMUtil.getAttribute(e,"whitespace"); 123 if(ws==null) ws = "collapse"; 124 125 String type = DOMUtil.getAttribute(e,"type"); 126 if(type==null) type=name(); 127 JType t=null; 128 129 int idx = type.lastIndexOf('.'); 130 if(idx<0) { 131 // no package name is specified. 132 try { 133 t = JPrimitiveType.parse(owner.codeModel,type); 134 } catch( IllegalArgumentException e ) { 135 // otherwise treat it as a class name in the current package 136 type = owner.getTargetPackage().name()+'.'+type; 137 } 138 } 139 if(t==null) { 140 try { 141 // TODO: revisit this later 142 JDefinedClass cls = owner.codeModel._class(type); 143 cls.hide(); 144 t = cls; 145 } catch( JClassAlreadyExistsException e ) { 146 t = e.getExistingClass(); 147 } 148 } 149 150 String parse = DOMUtil.getAttribute(e,"parse"); 151 if(parse==null) parse="new"; 152 153 String print = DOMUtil.getAttribute(e,"print"); 154 if(print==null) print="toString"; 155 156 JDefinedClass adapter = generateAdapter(owner.codeModel, parse, print, t.boxify()); 157 158 // XmlJavaType customization always converts between string and an user-defined type. 159 return TypeUseFactory.adapt(CBuiltinLeafInfo.STRING,new CAdapter(adapter)); 160 } 161 162 // TODO: anyway to reuse this code between XML Schema compiler? 163 private JDefinedClass generateAdapter(JCodeModel cm, String parseMethod, String printMethod, JClass inMemoryType) { 164 JDefinedClass adapter = null; 165 166 int id = 1; 167 while(adapter==null) { 168 try { 169 JPackage pkg = owner.getTargetPackage(); 170 adapter = pkg._class("Adapter"+id); 171 } catch (JClassAlreadyExistsException e) { 172 // try another name in search for an unique name. 173 // this isn't too efficient, but we expect people to usually use 174 // a very small number of adapters. 175 id++; 176 } 177 } 178 179 adapter._extends(cm.ref(XmlAdapter.class).narrow(String.class).narrow(inMemoryType)); 180 181 JMethod unmarshal = adapter.method(JMod.PUBLIC, inMemoryType, "unmarshal"); 182 JVar $value = unmarshal.param(String.class, "value"); 183 184 JExpression inv; 185 186 if( parseMethod.equals("new") ) { 187 // "new" indicates that the constructor of the target type 188 // will do the unmarshalling. 189 190 // RESULT: new <type>() 191 inv = JExpr._new(inMemoryType).arg($value); 192 } else { 193 int idx = parseMethod.lastIndexOf('.'); 194 if(idx<0) { 195 // parseMethod specifies the static method of the target type 196 // which will do the unmarshalling. 197 198 // because of an error check at the constructor, 199 // we can safely assume that this cast works. 200 inv = inMemoryType.staticInvoke(parseMethod).arg($value); 201 } else { 202 inv = JExpr.direct(parseMethod+"(value)"); 203 } 204 } 205 unmarshal.body()._return(inv); 206 207 208 JMethod marshal = adapter.method(JMod.PUBLIC, String.class, "marshal"); 209 $value = marshal.param(inMemoryType,"value"); 210 211 int idx = printMethod.lastIndexOf('.'); 212 if(idx<0) { 213 // printMethod specifies a method in the target type 214 // which performs the serialization. 215 216 // RESULT: <value>.<method>() 217 inv = $value.invoke(printMethod); 218 } else { 219 // RESULT: <className>.<method>(<value>) 220 inv = JExpr.direct(printMethod+"(value)"); 221 } 222 marshal.body()._return(inv); 223 224 return adapter; 225 } 226 }