author | mkos |
Sun, 30 Dec 2012 00:00:00 +0100 | |
changeset 22678 | ac1ea46be942 |
parent 12009 | 4abb694f273a |
permissions | -rw-r--r-- |
12009 | 1 |
/* |
22678
ac1ea46be942
8029237: Update copyright year to match last edit in jaxws repository for 2012
mkos
parents:
12009
diff
changeset
|
2 |
* Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. |
12009 | 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 |
} |