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.field; |
|
27 |
||
28 |
import java.util.ArrayList; |
|
29 |
import java.util.Collection; |
|
30 |
import java.util.List; |
|
31 |
||
32 |
import javax.xml.bind.annotation.W3CDomHandler; |
|
33 |
import javax.xml.bind.annotation.XmlList; |
|
34 |
import javax.xml.bind.annotation.XmlMixed; |
|
35 |
import javax.xml.bind.annotation.XmlNsForm; |
|
36 |
import javax.xml.bind.annotation.XmlValue; |
|
37 |
import javax.xml.bind.annotation.XmlInlineBinaryData; |
|
38 |
import javax.xml.namespace.QName; |
|
39 |
||
40 |
import com.sun.codemodel.internal.JAnnotatable; |
|
41 |
import com.sun.codemodel.internal.JClass; |
|
42 |
import com.sun.codemodel.internal.JCodeModel; |
|
43 |
import com.sun.codemodel.internal.JExpr; |
|
44 |
import com.sun.codemodel.internal.JExpression; |
|
45 |
import com.sun.codemodel.internal.JFieldVar; |
|
46 |
import com.sun.codemodel.internal.JMod; |
|
47 |
import com.sun.codemodel.internal.JType; |
|
48 |
import com.sun.tools.internal.xjc.generator.annotation.spec.XmlAnyElementWriter; |
|
49 |
import com.sun.tools.internal.xjc.generator.annotation.spec.XmlAttributeWriter; |
|
50 |
import com.sun.tools.internal.xjc.generator.annotation.spec.XmlElementRefWriter; |
|
51 |
import com.sun.tools.internal.xjc.generator.annotation.spec.XmlElementRefsWriter; |
|
52 |
import com.sun.tools.internal.xjc.generator.annotation.spec.XmlElementWriter; |
|
53 |
import com.sun.tools.internal.xjc.generator.annotation.spec.XmlElementsWriter; |
|
54 |
import com.sun.tools.internal.xjc.generator.annotation.spec.XmlSchemaTypeWriter; |
|
55 |
import com.sun.tools.internal.xjc.generator.bean.ClassOutlineImpl; |
|
56 |
import com.sun.tools.internal.xjc.model.CAttributePropertyInfo; |
|
57 |
import com.sun.tools.internal.xjc.model.CElement; |
|
58 |
import com.sun.tools.internal.xjc.model.CElementInfo; |
|
59 |
import com.sun.tools.internal.xjc.model.CElementPropertyInfo; |
|
60 |
import com.sun.tools.internal.xjc.model.CPropertyInfo; |
|
61 |
import com.sun.tools.internal.xjc.model.CReferencePropertyInfo; |
|
62 |
import com.sun.tools.internal.xjc.model.CTypeInfo; |
|
63 |
import com.sun.tools.internal.xjc.model.CTypeRef; |
|
64 |
import com.sun.tools.internal.xjc.model.CValuePropertyInfo; |
|
65 |
import com.sun.tools.internal.xjc.model.nav.NClass; |
|
66 |
import com.sun.tools.internal.xjc.outline.Aspect; |
|
67 |
import static com.sun.tools.internal.xjc.outline.Aspect.IMPLEMENTATION; |
|
68 |
import com.sun.tools.internal.xjc.outline.ClassOutline; |
|
69 |
import com.sun.tools.internal.xjc.outline.FieldAccessor; |
|
70 |
import com.sun.tools.internal.xjc.outline.FieldOutline; |
|
71 |
import com.sun.tools.internal.xjc.reader.TypeUtil; |
|
72 |
import com.sun.tools.internal.xjc.Options; |
|
73 |
import com.sun.tools.internal.xjc.api.SpecVersion; |
|
74 |
import com.sun.xml.internal.bind.api.impl.NameConverter; |
|
75 |
import com.sun.xml.internal.bind.v2.TODO; |
|
76 |
||
77 |
/** |
|
78 |
* Useful base class for implementing {@link FieldOutline}. |
|
79 |
* |
|
80 |
* <p> |
|
81 |
* This class just provides a few utility methods and keep some |
|
82 |
* important variables so that they can be readily accessed any time. |
|
83 |
* |
|
84 |
* @author |
|
85 |
* Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com) |
|
86 |
*/ |
|
87 |
abstract class AbstractField implements FieldOutline { |
|
88 |
||
89 |
protected final ClassOutlineImpl outline; |
|
90 |
||
91 |
protected final CPropertyInfo prop; |
|
92 |
||
93 |
protected final JCodeModel codeModel; |
|
94 |
||
95 |
/** |
|
96 |
* The type of this field, which can hold all the possible types. |
|
97 |
*/ |
|
98 |
protected final JType implType; |
|
99 |
||
100 |
/** |
|
101 |
* The publicly visible type of this field. |
|
102 |
* If we are generating value classes implType==exposedType. |
|
103 |
*/ |
|
104 |
protected final JType exposedType; |
|
105 |
||
106 |
protected AbstractField( ClassOutlineImpl outline, CPropertyInfo prop ) { |
|
107 |
this.outline = outline; |
|
108 |
this.prop = prop; |
|
109 |
this.codeModel = outline.parent().getCodeModel(); |
|
110 |
this.implType = getType(IMPLEMENTATION); |
|
111 |
this.exposedType = getType(Aspect.EXPOSED); |
|
112 |
} |
|
113 |
||
114 |
public final ClassOutline parent() { |
|
115 |
return outline; |
|
116 |
} |
|
117 |
||
118 |
public final CPropertyInfo getPropertyInfo() { |
|
119 |
return prop; |
|
120 |
} |
|
121 |
||
122 |
||
123 |
/** |
|
124 |
* Annotate the field according to the recipes given as {@link CPropertyInfo}. |
|
125 |
*/ |
|
126 |
protected void annotate( JAnnotatable field ) { |
|
127 |
||
128 |
assert(field!=null); |
|
129 |
||
130 |
/* |
|
131 |
TODO: consider moving this logic to somewhere else |
|
132 |
so that it can be better shared, for how a field gets |
|
133 |
annotated doesn't really depend on how we generate accessors. |
|
134 |
||
135 |
so perhaps we should separate those two. |
|
136 |
*/ |
|
137 |
||
138 |
// TODO: consider a visitor |
|
139 |
if (prop instanceof CAttributePropertyInfo) { |
|
140 |
annotateAttribute(field); |
|
141 |
} else if (prop instanceof CElementPropertyInfo) { |
|
142 |
annotateElement(field); |
|
143 |
} else if (prop instanceof CValuePropertyInfo) { |
|
144 |
field.annotate(XmlValue.class); |
|
145 |
} else if (prop instanceof CReferencePropertyInfo) { |
|
146 |
annotateReference(field); |
|
147 |
} |
|
148 |
||
149 |
outline.parent().generateAdapterIfNecessary(prop,field); |
|
150 |
||
151 |
QName st = prop.getSchemaType(); |
|
152 |
if(st!=null) |
|
153 |
field.annotate2(XmlSchemaTypeWriter.class) |
|
154 |
.name(st.getLocalPart()) |
|
155 |
.namespace(st.getNamespaceURI()); |
|
156 |
||
157 |
if(prop.inlineBinaryData()) |
|
158 |
field.annotate(XmlInlineBinaryData.class); |
|
159 |
} |
|
160 |
||
161 |
private void annotateReference(JAnnotatable field) { |
|
162 |
CReferencePropertyInfo rp = (CReferencePropertyInfo) prop; |
|
163 |
||
164 |
TODO.prototype(); |
|
165 |
// this is just a quick hack to get the basic test working |
|
166 |
||
167 |
Collection<CElement> elements = rp.getElements(); |
|
168 |
||
169 |
XmlElementRefWriter refw; |
|
170 |
if(elements.size()==1) { |
|
171 |
refw = field.annotate2(XmlElementRefWriter.class); |
|
172 |
CElement e = elements.iterator().next(); |
|
173 |
refw.name(e.getElementName().getLocalPart()) |
|
174 |
.namespace(e.getElementName().getNamespaceURI()) |
|
175 |
.type(e.getType().toType(outline.parent(),IMPLEMENTATION)); |
|
176 |
if(getOptions().target.isLaterThan(SpecVersion.V2_2)) |
|
177 |
refw.required(rp.isRequired()); |
|
178 |
} else |
|
179 |
if(elements.size()>1) { |
|
180 |
XmlElementRefsWriter refsw = field.annotate2(XmlElementRefsWriter.class); |
|
181 |
for( CElement e : elements ) { |
|
182 |
refw = refsw.value(); |
|
183 |
refw.name(e.getElementName().getLocalPart()) |
|
184 |
.namespace(e.getElementName().getNamespaceURI()) |
|
185 |
.type(e.getType().toType(outline.parent(),IMPLEMENTATION)); |
|
186 |
if(getOptions().target.isLaterThan(SpecVersion.V2_2)) |
|
187 |
refw.required(rp.isRequired()); |
|
188 |
} |
|
189 |
} |
|
190 |
||
191 |
if(rp.isMixed()) |
|
192 |
field.annotate(XmlMixed.class); |
|
193 |
||
194 |
NClass dh = rp.getDOMHandler(); |
|
195 |
if(dh!=null) { |
|
196 |
XmlAnyElementWriter xaew = field.annotate2(XmlAnyElementWriter.class); |
|
197 |
xaew.lax(rp.getWildcard().allowTypedObject); |
|
198 |
||
199 |
final JClass value = dh.toType(outline.parent(),IMPLEMENTATION); |
|
200 |
if(!value.equals(codeModel.ref(W3CDomHandler.class))) { |
|
201 |
xaew.value(value); |
|
202 |
} |
|
203 |
} |
|
204 |
||
205 |
} |
|
206 |
||
207 |
/** |
|
208 |
* Annotate the element property 'field' |
|
209 |
*/ |
|
210 |
private void annotateElement(JAnnotatable field) { |
|
211 |
CElementPropertyInfo ep = (CElementPropertyInfo) prop; |
|
212 |
List<CTypeRef> types = ep.getTypes(); |
|
213 |
||
214 |
if(ep.isValueList()) { |
|
215 |
field.annotate(XmlList.class); |
|
216 |
} |
|
217 |
||
218 |
assert ep.getXmlName()==null; |
|
219 |
// if( eName!=null ) { // wrapper |
|
220 |
// XmlElementWrapperWriter xcw = field.annotate2(XmlElementWrapperWriter.class); |
|
221 |
// xcw.name(eName.getLocalPart()) |
|
222 |
// .namespace(eName.getNamespaceURI()); |
|
223 |
// } |
|
224 |
||
225 |
if (types.size() == 1) { |
|
226 |
CTypeRef t = types.get(0); |
|
227 |
writeXmlElementAnnotation(field, t, resolve(t,IMPLEMENTATION), false); |
|
228 |
} else { |
|
229 |
for (CTypeRef t : types) { |
|
230 |
// generate @XmlElements |
|
231 |
writeXmlElementAnnotation(field, t, resolve(t,IMPLEMENTATION), true); |
|
232 |
} |
|
233 |
xesw = null; |
|
234 |
} |
|
235 |
} |
|
236 |
||
237 |
/** |
|
238 |
* Generate the simplest XmlElement annotation possible taking all semantic optimizations |
|
239 |
* into account. This method is essentially equivalent to: |
|
240 |
* |
|
241 |
* xew.name(ctype.getTagName().getLocalPart()) |
|
242 |
* .namespace(ctype.getTagName().getNamespaceURI()) |
|
243 |
* .type(jtype) |
|
244 |
* .defaultValue(ctype.getDefaultValue()); |
|
245 |
* |
|
246 |
* @param field |
|
247 |
* @param ctype |
|
248 |
* @param jtype |
|
249 |
* @param checkWrapper true if the method might need to generate XmlElements |
|
250 |
*/ |
|
251 |
private void writeXmlElementAnnotation( JAnnotatable field, CTypeRef ctype, JType jtype, |
|
252 |
boolean checkWrapper ) { |
|
253 |
||
254 |
// lazily create - we don't know if we need to generate anything yet |
|
255 |
XmlElementWriter xew = null; |
|
256 |
||
257 |
// these values are used to determine how to optimize the generated annotation |
|
258 |
XmlNsForm formDefault = parent()._package().getElementFormDefault(); |
|
259 |
String propName = prop.getName(false); |
|
260 |
||
261 |
String enclosingTypeNS; |
|
262 |
||
263 |
if(parent().target.getTypeName()==null) |
|
264 |
enclosingTypeNS = parent()._package().getMostUsedNamespaceURI(); |
|
265 |
else |
|
266 |
enclosingTypeNS = parent().target.getTypeName().getNamespaceURI(); |
|
267 |
||
268 |
// generate the name property? |
|
269 |
String generatedName = ctype.getTagName().getLocalPart(); |
|
270 |
if(!generatedName.equals(propName)) { |
|
271 |
if(xew == null) xew = getXew(checkWrapper, field); |
|
272 |
xew.name(generatedName); |
|
273 |
} |
|
274 |
||
275 |
// generate the namespace property? |
|
276 |
String generatedNS = ctype.getTagName().getNamespaceURI(); |
|
277 |
if (((formDefault == XmlNsForm.QUALIFIED) && !generatedNS.equals(enclosingTypeNS)) || |
|
278 |
((formDefault == XmlNsForm.UNQUALIFIED) && !generatedNS.equals(""))) { |
|
279 |
if(xew == null) xew = getXew(checkWrapper, field); |
|
280 |
xew.namespace(generatedNS); |
|
281 |
} |
|
282 |
||
283 |
// generate the required() property? |
|
284 |
CElementPropertyInfo ep = (CElementPropertyInfo) prop; |
|
285 |
if(ep.isRequired() && exposedType.isReference()) { |
|
286 |
if(xew == null) xew = getXew(checkWrapper, field); |
|
287 |
xew.required(true); |
|
288 |
} |
|
289 |
||
290 |
// generate the type property? |
|
291 |
||
292 |
// I'm not too sure if this is the right place to handle this, but |
|
293 |
// if the schema definition is requiring this element, we should point to a primitive type, |
|
294 |
// not wrapper type (to correctly carry forward the required semantics.) |
|
295 |
// if it's a collection, we can't use a primitive, however. |
|
296 |
if(ep.isRequired() && !prop.isCollection()) |
|
297 |
jtype = jtype.unboxify(); |
|
298 |
||
299 |
// when generating code for 1.4, the runtime can't infer that ArrayList<Foo> derives |
|
300 |
// from Collection<Foo> (because List isn't parameterized), so always expclitly |
|
301 |
// generate @XmlElement(type=...) |
|
302 |
if( !jtype.equals(exposedType) || (getOptions().runtime14 && prop.isCollection())) { |
|
303 |
if(xew == null) xew = getXew(checkWrapper, field); |
|
304 |
xew.type(jtype); |
|
305 |
} |
|
306 |
||
307 |
// generate defaultValue property? |
|
308 |
final String defaultValue = ctype.getDefaultValue(); |
|
309 |
if (defaultValue!=null) { |
|
310 |
if(xew == null) xew = getXew(checkWrapper, field); |
|
311 |
xew.defaultValue(defaultValue); |
|
312 |
} |
|
313 |
||
314 |
// generate the nillable property? |
|
315 |
if (ctype.isNillable()) { |
|
316 |
if(xew == null) xew = getXew(checkWrapper, field); |
|
317 |
xew.nillable(true); |
|
318 |
} |
|
319 |
} |
|
320 |
||
321 |
/** |
|
322 |
* Gets the {@link Options} in the current compilation context. |
|
323 |
*/ |
|
324 |
protected final Options getOptions() { |
|
325 |
return parent().parent().getModel().options; |
|
326 |
} |
|
327 |
||
328 |
// ugly hack to lazily create |
|
329 |
private XmlElementsWriter xesw = null; |
|
330 |
||
331 |
private XmlElementWriter getXew(boolean checkWrapper, JAnnotatable field) { |
|
332 |
XmlElementWriter xew; |
|
333 |
if(checkWrapper) { |
|
334 |
if(xesw==null) { |
|
335 |
xesw = field.annotate2(XmlElementsWriter.class); |
|
336 |
} |
|
337 |
xew = xesw.value(); |
|
338 |
} else { |
|
339 |
xew = field.annotate2(XmlElementWriter.class); |
|
340 |
} |
|
341 |
return xew; |
|
342 |
} |
|
343 |
||
344 |
/** |
|
345 |
* Annotate the attribute property 'field' |
|
346 |
*/ |
|
347 |
private void annotateAttribute(JAnnotatable field) { |
|
348 |
CAttributePropertyInfo ap = (CAttributePropertyInfo) prop; |
|
349 |
QName attName = ap.getXmlName(); |
|
350 |
||
351 |
// [RESULT] |
|
352 |
// @XmlAttribute(name="foo", required=true, namespace="bar://baz") |
|
353 |
XmlAttributeWriter xaw = field.annotate2(XmlAttributeWriter.class); |
|
354 |
||
355 |
final String generatedName = attName.getLocalPart(); |
|
356 |
final String generatedNS = attName.getNamespaceURI(); |
|
357 |
||
358 |
// Issue 570; always force generating name="" when do it when globalBindings underscoreBinding is set to non default value |
|
359 |
// generate name property? |
|
360 |
if(!generatedName.equals(ap.getName(false)) || !generatedName.equals(ap.getName(true)) || (outline.parent().getModel().getNameConverter() != NameConverter.standard)) { |
|
361 |
xaw.name(generatedName); |
|
362 |
} |
|
363 |
||
364 |
// generate namespace property? |
|
365 |
if(!generatedNS.equals("")) { // assume attributeFormDefault == unqualified |
|
366 |
xaw.namespace(generatedNS); |
|
367 |
} |
|
368 |
||
369 |
// generate required property? |
|
370 |
if(ap.isRequired()) { |
|
371 |
xaw.required(true); |
|
372 |
} |
|
373 |
} |
|
374 |
||
375 |
/** |
|
376 |
* Useful base class for implementing {@link FieldAccessor}. |
|
377 |
*/ |
|
378 |
protected abstract class Accessor implements FieldAccessor { |
|
379 |
||
380 |
/** |
|
381 |
* Evaluates to the target object this accessor should access. |
|
382 |
*/ |
|
383 |
protected final JExpression $target; |
|
384 |
||
385 |
protected Accessor( JExpression $target ) { |
|
386 |
this.$target = $target; |
|
387 |
} |
|
388 |
||
389 |
public final FieldOutline owner() { |
|
390 |
return AbstractField.this; |
|
391 |
} |
|
392 |
||
393 |
public final CPropertyInfo getPropertyInfo() { |
|
394 |
return prop; |
|
395 |
} |
|
396 |
} |
|
397 |
||
398 |
||
399 |
// |
|
400 |
// |
|
401 |
// utility methods |
|
402 |
// |
|
403 |
// |
|
404 |
||
405 |
/** |
|
406 |
* Generates the field declaration. |
|
407 |
*/ |
|
408 |
protected final JFieldVar generateField( JType type ) { |
|
409 |
return outline.implClass.field( JMod.PROTECTED, type, prop.getName(false) ); |
|
410 |
} |
|
411 |
||
412 |
/** |
|
413 |
* Case from {@link #exposedType} to {@link #implType} if necessary. |
|
414 |
*/ |
|
415 |
protected final JExpression castToImplType( JExpression exp ) { |
|
416 |
if(implType==exposedType) |
|
417 |
return exp; |
|
418 |
else |
|
419 |
return JExpr.cast(implType,exp); |
|
420 |
} |
|
421 |
||
422 |
/** |
|
423 |
* Compute the type of a {@link CPropertyInfo} |
|
424 |
* @param aspect |
|
425 |
*/ |
|
426 |
protected JType getType(final Aspect aspect) { |
|
427 |
if(prop.getAdapter()!=null) |
|
428 |
return prop.getAdapter().customType.toType(outline.parent(),aspect); |
|
429 |
||
430 |
final class TypeList extends ArrayList<JType> { |
|
431 |
void add( CTypeInfo t ) { |
|
432 |
add( t.getType().toType(outline.parent(),aspect) ); |
|
433 |
if(t instanceof CElementInfo) { |
|
434 |
// UGLY. element substitution is implemented in a way that |
|
435 |
// the derived elements are not assignable to base elements. |
|
436 |
// so when we compute the signature, we have to take derived types |
|
437 |
// into account |
|
438 |
add( ((CElementInfo)t).getSubstitutionMembers()); |
|
439 |
} |
|
440 |
} |
|
441 |
||
442 |
void add( Collection<? extends CTypeInfo> col ) { |
|
443 |
for (CTypeInfo typeInfo : col) |
|
444 |
add(typeInfo); |
|
445 |
} |
|
446 |
} |
|
447 |
TypeList r = new TypeList(); |
|
448 |
r.add(prop.ref()); |
|
449 |
||
450 |
JType t; |
|
451 |
if(prop.baseType!=null) |
|
452 |
t = prop.baseType; |
|
453 |
else |
|
454 |
t = TypeUtil.getCommonBaseType(codeModel,r); |
|
455 |
||
456 |
// if item type is unboxable, convert t=Integer -> t=int |
|
457 |
// the in-memory data structure can't have primitives directly, |
|
458 |
// but this guarantees that items cannot legal hold null, |
|
459 |
// which helps us improve the boundary signature between our |
|
460 |
// data structure and user code |
|
461 |
if(prop.isUnboxable()) |
|
462 |
t = t.unboxify(); |
|
463 |
return t; |
|
464 |
} |
|
465 |
||
466 |
/** |
|
467 |
* Returns contents to be added to javadoc. |
|
468 |
*/ |
|
469 |
protected final List<Object> listPossibleTypes( CPropertyInfo prop ) { |
|
470 |
List<Object> r = new ArrayList<Object>(); |
|
471 |
for( CTypeInfo tt : prop.ref() ) { |
|
472 |
JType t = tt.getType().toType(outline.parent(),Aspect.EXPOSED); |
|
473 |
if( t.isPrimitive() || t.isArray() ) |
|
474 |
r.add(t.fullName()); |
|
475 |
else { |
|
476 |
r.add(t); |
|
477 |
r.add("\n"); |
|
478 |
} |
|
479 |
} |
|
480 |
||
481 |
return r; |
|
482 |
} |
|
483 |
||
484 |
/** |
|
485 |
* return the Java type for the given type reference in the model. |
|
486 |
*/ |
|
487 |
private JType resolve(CTypeRef typeRef,Aspect a) { |
|
488 |
return outline.parent().resolve(typeRef,a); |
|
489 |
} |
|
490 |
||
491 |
} |