src/share/jaxws_classes/com/sun/tools/internal/xjc/generator/bean/ObjectFactoryGeneratorImpl.java

changeset 286
f50545b5e2f1
parent 0
373ffda63c9a
equal deleted inserted replaced
284:88b85470e72c 286:f50545b5e2f1
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.generator.bean;
27
28 import java.util.Collection;
29 import java.util.HashMap;
30 import java.util.Map;
31
32 import javax.xml.bind.JAXBException;
33 import javax.xml.bind.annotation.XmlInlineBinaryData;
34 import javax.xml.namespace.QName;
35
36 import com.sun.codemodel.internal.JClass;
37 import com.sun.codemodel.internal.JCodeModel;
38 import com.sun.codemodel.internal.JDefinedClass;
39 import com.sun.codemodel.internal.JExpr;
40 import com.sun.codemodel.internal.JExpression;
41 import com.sun.codemodel.internal.JFieldVar;
42 import com.sun.codemodel.internal.JInvocation;
43 import com.sun.codemodel.internal.JMethod;
44 import com.sun.codemodel.internal.JMod;
45 import com.sun.codemodel.internal.JPackage;
46 import com.sun.codemodel.internal.JType;
47 import com.sun.codemodel.internal.JVar;
48 import com.sun.tools.internal.xjc.generator.annotation.spec.XmlElementDeclWriter;
49 import com.sun.tools.internal.xjc.generator.annotation.spec.XmlRegistryWriter;
50 import com.sun.tools.internal.xjc.model.CElementInfo;
51 import com.sun.tools.internal.xjc.model.CPropertyInfo;
52 import com.sun.tools.internal.xjc.model.Constructor;
53 import com.sun.tools.internal.xjc.model.Model;
54 import com.sun.tools.internal.xjc.outline.Aspect;
55 import com.sun.tools.internal.xjc.outline.FieldAccessor;
56 import com.sun.tools.internal.xjc.outline.FieldOutline;
57 import com.sun.xml.internal.bind.v2.TODO;
58
59 /**
60 * Generates <code>ObjectFactory</code> then wraps it and provides
61 * access to it.
62 *
63 * <p>
64 * The ObjectFactory contains
65 * factory methods for each schema derived content class
66 *
67 * @author
68 * Ryan Shoemaker
69 */
70 abstract class ObjectFactoryGeneratorImpl extends ObjectFactoryGenerator {
71
72 private final BeanGenerator outline;
73 private final Model model;
74 private final JCodeModel codeModel;
75 /**
76 * Ref to {@link Class}.
77 */
78 private final JClass classRef;
79
80 /**
81 * Reference to the generated ObjectFactory class.
82 */
83 private final JDefinedClass objectFactory;
84
85 /** map of qname to the QName constant field. */
86 private final HashMap<QName,JFieldVar> qnameMap = new HashMap<QName,JFieldVar>();
87
88 /**
89 * Names of the element factory methods that are created.
90 * Used to detect collisions.
91 *
92 * The value is used for reporting error locations.
93 */
94 private final Map<String,CElementInfo> elementFactoryNames = new HashMap<String,CElementInfo>();
95
96 /**
97 * Names of the value factory methods that are created.
98 * Used to detect collisions.
99 *
100 * The value is used for reporting error locations.
101 */
102 private final Map<String,ClassOutlineImpl> valueFactoryNames = new HashMap<String,ClassOutlineImpl>();
103
104 /**
105 * Returns a reference to the generated (public) ObjectFactory
106 */
107 public JDefinedClass getObjectFactory() {
108 return objectFactory;
109 }
110
111
112
113
114 public ObjectFactoryGeneratorImpl( BeanGenerator outline, Model model, JPackage targetPackage ) {
115 this.outline = outline;
116 this.model = model;
117 this.codeModel = this.model.codeModel;
118 this.classRef = codeModel.ref(Class.class);
119
120 // create the ObjectFactory class skeleton
121 objectFactory = this.outline.getClassFactory().createClass(
122 targetPackage, "ObjectFactory", null );
123 objectFactory.annotate2(XmlRegistryWriter.class);
124
125 // generate the default constructor
126 //
127 // m1 result:
128 // public ObjectFactory() {}
129 JMethod m1 = objectFactory.constructor(JMod.PUBLIC);
130 m1.javadoc().append("Create a new ObjectFactory that can be used to " +
131 "create new instances of schema derived classes " +
132 "for package: " + targetPackage.name());
133
134 // add some class javadoc
135 objectFactory.javadoc().append(
136 "This object contains factory methods for each \n" +
137 "Java content interface and Java element interface \n" +
138 "generated in the " + targetPackage.name() + " package. \n" +
139 "<p>An ObjectFactory allows you to programatically \n" +
140 "construct new instances of the Java representation \n" +
141 "for XML content. The Java representation of XML \n" +
142 "content can consist of schema derived interfaces \n" +
143 "and classes representing the binding of schema \n" +
144 "type definitions, element declarations and model \n" +
145 "groups. Factory methods for each of these are \n" +
146 "provided in this class." );
147
148 }
149
150 /**
151 * Adds code for the given {@link CElementInfo} to ObjectFactory.
152 */
153 protected final void populate( CElementInfo ei, Aspect impl, Aspect exposed ) {
154 JType exposedElementType = ei.toType(outline,exposed);
155 JType exposedType = ei.getContentInMemoryType().toType(outline,exposed);
156 JType implType = ei.getContentInMemoryType().toType(outline,impl);
157 String namespaceURI = ei.getElementName().getNamespaceURI();
158 String localPart = ei.getElementName().getLocalPart();
159
160 JClass scope=null;
161 if(ei.getScope()!=null)
162 scope = outline.getClazz(ei.getScope()).implClass;
163
164
165 JMethod m;
166
167 if(ei.isAbstract()) {
168 // TODO: see the "Abstract elements and mighty IXmlElement" e-mail
169 // that I sent to jaxb-tech
170 TODO.checkSpec();
171 }
172
173 {// collision check
174 CElementInfo existing = elementFactoryNames.put(ei.getSqueezedName(),ei);
175 if( existing!=null ) {
176 outline.getErrorReceiver().error(existing.getLocator(),
177 Messages.OBJECT_FACTORY_CONFLICT.format(ei.getSqueezedName()));
178 outline.getErrorReceiver().error(ei.getLocator(),
179 Messages.OBJECT_FACTORY_CONFLICT_RELATED.format());
180 return;
181 }
182 }
183
184 // no arg constructor
185 // [RESULT] if the element doesn't have its own class, something like:
186 //
187 // @XmlElementMapping(uri = "", name = "foo")
188 // public JAXBElement<Foo> createFoo( Foo value ) {
189 // return new JAXBElement<Foo>(
190 // new QName("","foo"),(Class)FooImpl.class,scope,(FooImpl)value);
191 // }
192 // NOTE: when we generate value classes Foo==FooImpl
193 //
194 // [RESULT] otherwise
195 //
196 // @XmlElementMapping(uri = "", name = "foo")
197 // public Foo createFoo( FooType value ) {
198 // return new Foo((FooTypeImpl)value);
199 // }
200 // NOTE: when we generate value classes FooType==FooTypeImpl
201 //
202 // to deal with
203 // new JAXBElement<List<String>>( ..., List.class, ... );
204 // we sometimes have to produce (Class)List.class instead of just List.class
205
206 m = objectFactory.method( JMod.PUBLIC, exposedElementType, "create" + ei.getSqueezedName() );
207 JVar $value = m.param(exposedType,"value");
208
209 JExpression declaredType;
210 if(implType.boxify().isParameterized() || !exposedType.equals(implType))
211 declaredType = JExpr.cast(classRef,implType.boxify().dotclass());
212 else
213 declaredType = implType.boxify().dotclass();
214 JExpression scopeClass = scope==null?JExpr._null():scope.dotclass();
215
216 // build up the return extpression
217 JInvocation exp = JExpr._new(exposedElementType);
218 if(!ei.hasClass()) {
219 exp.arg(getQNameInvocation(ei));
220 exp.arg(declaredType);
221 exp.arg(scopeClass);
222 }
223 if(implType==exposedType)
224 exp.arg($value);
225 else
226 exp.arg(JExpr.cast(implType,$value));
227
228 m.body()._return( exp );
229
230 m.javadoc()
231 .append("Create an instance of ")
232 .append(exposedElementType)
233 .append("}");
234
235 XmlElementDeclWriter xemw = m.annotate2(XmlElementDeclWriter.class);
236 xemw.namespace(namespaceURI).name(localPart);
237 if(scope!=null)
238 xemw.scope(scope);
239
240 if(ei.getSubstitutionHead()!=null) {
241 QName n = ei.getSubstitutionHead().getElementName();
242 xemw.substitutionHeadNamespace(n.getNamespaceURI());
243 xemw.substitutionHeadName(n.getLocalPart());
244 }
245
246 if(ei.getDefaultValue()!=null)
247 xemw.defaultValue(ei.getDefaultValue());
248
249 if(ei.getProperty().inlineBinaryData())
250 m.annotate(XmlInlineBinaryData.class);
251
252 // if the element is adapter, put that annotation on the factory method
253 outline.generateAdapterIfNecessary(ei.getProperty(),m);
254 }
255
256 /**
257 * return a JFieldVar that represents the QName field for the given information.
258 *
259 * if it doesn't exist, create a static field in the class and store a new JFieldVar.
260 */
261 private JExpression getQNameInvocation(CElementInfo ei) {
262 QName name = ei.getElementName();
263 if(qnameMap.containsKey(name)) {
264 return qnameMap.get(name);
265 }
266
267 if(qnameMap.size()>1024)
268 // stop gap measure to avoid 'code too large' error in javac.
269 return createQName(name);
270
271 // [RESULT]
272 // private static final QName _XYZ_NAME = new QName("uri", "local");
273 JFieldVar qnameField = objectFactory.field(
274 JMod.PRIVATE | JMod.STATIC | JMod.FINAL,
275 QName.class,
276 '_' + ei.getSqueezedName() + "_QNAME", createQName(name));
277
278 qnameMap.put(name, qnameField);
279
280 return qnameField;
281 }
282
283 /**
284 * Generates an expression that evaluates to "new QName(...)"
285 */
286 private JInvocation createQName(QName name) {
287 return JExpr._new(codeModel.ref(QName.class)).arg(name.getNamespaceURI()).arg(name.getLocalPart());
288 }
289
290 protected final void populate( ClassOutlineImpl cc, JClass sigType ) {
291 // add static factory method for this class to JAXBContext.
292 //
293 // generate methods like:
294 // public static final SIGTYPE createFoo() {
295 // return new FooImpl();
296 // }
297
298 if(!cc.target.isAbstract()) {
299 JMethod m = objectFactory.method(
300 JMod.PUBLIC, sigType, "create" + cc.target.getSqueezedName() );
301 m.body()._return( JExpr._new(cc.implRef) );
302
303 // add some jdoc to avoid javadoc warnings in jdk1.4
304 m.javadoc()
305 .append("Create an instance of ")
306 .append(cc.ref);
307 }
308
309
310 // add static factory methods for all the other constructors.
311 Collection<? extends Constructor> consl = cc.target.getConstructors();
312 if(consl.size()!=0) {
313 // if we are going to add constructors with parameters,
314 // first we need to have a default constructor.
315 cc.implClass.constructor(JMod.PUBLIC);
316 }
317
318 {// collision check
319 String name = cc.target.getSqueezedName();
320 ClassOutlineImpl existing = valueFactoryNames.put(name,cc);
321 if( existing!=null ) {
322 outline.getErrorReceiver().error(existing.target.getLocator(),
323 Messages.OBJECT_FACTORY_CONFLICT.format(name));
324 outline.getErrorReceiver().error(cc.target.getLocator(),
325 Messages.OBJECT_FACTORY_CONFLICT_RELATED.format());
326 return;
327 }
328 }
329
330 for( Constructor cons : consl ) {
331 // method on ObjectFactory
332 // [RESULT]
333 // Foo createFoo( T1 a, T2 b, T3 c, ... ) throws JAXBException {
334 // return new FooImpl(a,b,c,...);
335 // }
336 JMethod m = objectFactory.method( JMod.PUBLIC,
337 cc.ref, "create" + cc.target.getSqueezedName() );
338 JInvocation inv = JExpr._new(cc.implRef);
339 m.body()._return(inv);
340
341 // let's not throw this exception.
342 // m._throws(codeModel.ref(JAXBException.class));
343
344 // add some jdoc to avoid javadoc warnings in jdk1.4
345 m.javadoc()
346 .append( "Create an instance of " )
347 .append( cc.ref )
348 .addThrows(JAXBException.class).append("if an error occurs");
349
350 // constructor
351 // [RESULT]
352 // FooImpl( T1 a, T2 b, T3 c, ... ) {
353 // }
354 JMethod c = cc.implClass.constructor(JMod.PUBLIC);
355
356 for( String fieldName : cons.fields ) {
357 CPropertyInfo field = cc.target.getProperty(fieldName);
358 if(field==null) {
359 outline.getErrorReceiver().error(cc.target.getLocator(),
360 Messages.ILLEGAL_CONSTRUCTOR_PARAM.format(fieldName));
361 continue;
362 }
363
364 fieldName = camelize(fieldName);
365
366 FieldOutline fo = outline.getField(field);
367 FieldAccessor accessor = fo.create(JExpr._this());
368
369 // declare a parameter on this factory method and set
370 // it to the field
371 inv.arg(m.param( fo.getRawType(), fieldName ));
372
373 JVar $var = c.param( fo.getRawType(), fieldName );
374 accessor.fromRawValue(c.body(),'_'+fieldName,$var);
375 }
376 }
377 }
378
379
380 /** Change the first character to the lower case. */
381 private static String camelize( String s ) {
382 return Character.toLowerCase(s.charAt(0)) + s.substring(1);
383 }
384 }

mercurial