|
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.xmlschema; |
|
27 import static com.sun.tools.internal.xjc.reader.xmlschema.BGMBuilder.getName; |
|
28 |
|
29 import java.util.Set; |
|
30 |
|
31 import javax.xml.namespace.QName; |
|
32 |
|
33 import com.sun.codemodel.internal.JJavaName; |
|
34 import com.sun.codemodel.internal.JPackage; |
|
35 import com.sun.istack.internal.NotNull; |
|
36 import com.sun.istack.internal.Nullable; |
|
37 import com.sun.tools.internal.xjc.ErrorReceiver; |
|
38 import com.sun.tools.internal.xjc.model.CClassInfo; |
|
39 import com.sun.tools.internal.xjc.model.CClassInfoParent; |
|
40 import com.sun.tools.internal.xjc.model.CClassRef; |
|
41 import com.sun.tools.internal.xjc.model.CCustomizations; |
|
42 import com.sun.tools.internal.xjc.model.CElement; |
|
43 import com.sun.tools.internal.xjc.model.CElementInfo; |
|
44 import com.sun.tools.internal.xjc.model.Model; |
|
45 import com.sun.tools.internal.xjc.reader.Ring; |
|
46 import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIClass; |
|
47 import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIGlobalBinding; |
|
48 import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BISchemaBinding; |
|
49 import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BindInfo; |
|
50 import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIXSubstitutable; |
|
51 import com.sun.tools.internal.xjc.reader.xmlschema.ct.ComplexTypeFieldBuilder; |
|
52 import com.sun.tools.internal.xjc.reader.xmlschema.ct.ComplexTypeBindingMode; |
|
53 import com.sun.xml.internal.xsom.XSAnnotation; |
|
54 import com.sun.xml.internal.xsom.XSAttGroupDecl; |
|
55 import com.sun.xml.internal.xsom.XSAttributeDecl; |
|
56 import com.sun.xml.internal.xsom.XSAttributeUse; |
|
57 import com.sun.xml.internal.xsom.XSComplexType; |
|
58 import com.sun.xml.internal.xsom.XSComponent; |
|
59 import com.sun.xml.internal.xsom.XSContentType; |
|
60 import com.sun.xml.internal.xsom.XSDeclaration; |
|
61 import com.sun.xml.internal.xsom.XSElementDecl; |
|
62 import com.sun.xml.internal.xsom.XSFacet; |
|
63 import com.sun.xml.internal.xsom.XSIdentityConstraint; |
|
64 import com.sun.xml.internal.xsom.XSModelGroup; |
|
65 import com.sun.xml.internal.xsom.XSModelGroupDecl; |
|
66 import com.sun.xml.internal.xsom.XSNotation; |
|
67 import com.sun.xml.internal.xsom.XSParticle; |
|
68 import com.sun.xml.internal.xsom.XSSchema; |
|
69 import com.sun.xml.internal.xsom.XSSchemaSet; |
|
70 import com.sun.xml.internal.xsom.XSSimpleType; |
|
71 import com.sun.xml.internal.xsom.XSType; |
|
72 import com.sun.xml.internal.xsom.XSWildcard; |
|
73 import com.sun.xml.internal.xsom.XSXPath; |
|
74 |
|
75 import org.xml.sax.Locator; |
|
76 |
|
77 /** |
|
78 * Default classBinder implementation. Honors <jaxb:class> customizations |
|
79 * and default bindings. |
|
80 */ |
|
81 final class DefaultClassBinder implements ClassBinder |
|
82 { |
|
83 private final SimpleTypeBuilder stb = Ring.get(SimpleTypeBuilder.class); |
|
84 private final Model model = Ring.get(Model.class); |
|
85 |
|
86 protected final BGMBuilder builder = Ring.get(BGMBuilder.class); |
|
87 protected final ClassSelector selector = Ring.get(ClassSelector.class); |
|
88 |
|
89 protected final XSSchemaSet schemas = Ring.get(XSSchemaSet.class); |
|
90 |
|
91 public CElement attGroupDecl(XSAttGroupDecl decl) { |
|
92 return allow(decl,decl.getName()); |
|
93 } |
|
94 |
|
95 public CElement attributeDecl(XSAttributeDecl decl) { |
|
96 return allow(decl,decl.getName()); |
|
97 } |
|
98 |
|
99 public CElement modelGroup(XSModelGroup mgroup) { |
|
100 return never(); |
|
101 } |
|
102 |
|
103 public CElement modelGroupDecl(XSModelGroupDecl decl) { |
|
104 return never(); |
|
105 } |
|
106 |
|
107 |
|
108 public CElement complexType(XSComplexType type) { |
|
109 CElement ci = allow(type,type.getName()); |
|
110 if(ci!=null) return ci; |
|
111 |
|
112 // no customization is given -- do as the default binding. |
|
113 |
|
114 BindInfo bi = builder.getBindInfo(type); |
|
115 |
|
116 if(type.isGlobal()) { |
|
117 QName tagName = null; |
|
118 String className = deriveName(type); |
|
119 Locator loc = type.getLocator(); |
|
120 |
|
121 if(getGlobalBinding().isSimpleMode()) { |
|
122 // in the simple mode, we may optimize it away |
|
123 XSElementDecl referer = getSoleElementReferer(type); |
|
124 if(referer!=null && isCollapsable(referer)) { |
|
125 // if a global element contains |
|
126 // a collpsable complex type, we bind this element to a named one |
|
127 // and collapses element and complex type. |
|
128 tagName = getName(referer); |
|
129 className = deriveName(referer); |
|
130 loc = referer.getLocator(); |
|
131 } |
|
132 } |
|
133 |
|
134 // by default, global ones get their own classes. |
|
135 |
|
136 JPackage pkg = selector.getPackage(type.getTargetNamespace()); |
|
137 |
|
138 return new CClassInfo(model,pkg,className, loc,getTypeName(type),tagName,type,bi.toCustomizationList()); |
|
139 } else { |
|
140 XSElementDecl element = type.getScope(); |
|
141 |
|
142 if( element.isGlobal() && isCollapsable(element)) { |
|
143 if(builder.getBindInfo(element).get(BIClass.class)!=null) |
|
144 // the parent element was bound to a class. Don't bind this again to |
|
145 // cause unnecessary wrapping |
|
146 return null; |
|
147 |
|
148 // generate one class from element and complex type together. |
|
149 // this needs to be done before selector.isBound to avoid infinite recursion. |
|
150 |
|
151 // but avoid doing so when the element is mapped to a class, |
|
152 // which creates unnecessary classes |
|
153 return new CClassInfo( model, selector.getClassScope(), |
|
154 deriveName(element), element.getLocator(), null, |
|
155 getName(element), element, bi.toCustomizationList() ); |
|
156 } |
|
157 |
|
158 |
|
159 CElement parentType = selector.isBound(element,type); |
|
160 |
|
161 String className; |
|
162 CClassInfoParent scope; |
|
163 |
|
164 |
|
165 if( parentType!=null |
|
166 && parentType instanceof CElementInfo |
|
167 && ((CElementInfo)parentType).hasClass() ) { |
|
168 // special case where we put a nested 'Type' element |
|
169 scope = (CElementInfo)parentType; |
|
170 className = "Type"; |
|
171 } else { |
|
172 // since the parent element isn't bound to a type, merge the customizations associated to it, too. |
|
173 // custs = CCustomizations.merge( custs, builder.getBindInfo(type.getScope()).toCustomizationList()); |
|
174 className = builder.getNameConverter().toClassName(element.getName()); |
|
175 |
|
176 BISchemaBinding sb = builder.getBindInfo( |
|
177 type.getOwnerSchema() ).get(BISchemaBinding.class); |
|
178 if(sb!=null) className = sb.mangleAnonymousTypeClassName(className); |
|
179 scope = selector.getClassScope(); |
|
180 } |
|
181 |
|
182 return new CClassInfo(model, scope, className, type.getLocator(), null, null, type, bi.toCustomizationList() ); |
|
183 } |
|
184 } |
|
185 |
|
186 private QName getTypeName(XSComplexType type) { |
|
187 if(type.getRedefinedBy()!=null) |
|
188 return null; |
|
189 else |
|
190 return getName(type); |
|
191 } |
|
192 |
|
193 /** |
|
194 * Returns true if the complex type of the given element can be "optimized away" |
|
195 * and unified with its parent element decl to form a single class. |
|
196 */ |
|
197 private boolean isCollapsable(XSElementDecl decl) { |
|
198 XSType type = decl.getType(); |
|
199 |
|
200 if(!type.isComplexType()) |
|
201 return false; // not a complex type |
|
202 |
|
203 if(decl.getSubstitutables().size()>1 || decl.getSubstAffiliation()!=null) |
|
204 // because element substitution calls for a proper JAXBElement hierarchy |
|
205 return false; |
|
206 |
|
207 if(decl.isNillable()) |
|
208 // because nillable needs JAXBElement to represent correctly |
|
209 return false; |
|
210 |
|
211 BIXSubstitutable bixSubstitutable = builder.getBindInfo(decl).get(BIXSubstitutable.class); |
|
212 if(bixSubstitutable !=null) { |
|
213 // see https://jaxb.dev.java.net/issues/show_bug.cgi?id=289 |
|
214 // this customization forces non-collapsing behavior. |
|
215 bixSubstitutable.markAsAcknowledged(); |
|
216 return false; |
|
217 } |
|
218 |
|
219 if( getGlobalBinding().isSimpleMode() && decl.isGlobal()) { |
|
220 // in the simple mode, we do more aggressive optimization, and get rid of |
|
221 // a complex type class if it's only used once from a global element |
|
222 XSElementDecl referer = getSoleElementReferer(decl.getType()); |
|
223 if(referer!=null) { |
|
224 assert referer==decl; // I must be the sole referer |
|
225 return true; |
|
226 } |
|
227 } |
|
228 |
|
229 if(!type.isLocal() || !type.isComplexType()) |
|
230 return false; |
|
231 |
|
232 return true; |
|
233 } |
|
234 |
|
235 /** |
|
236 * If only one global {@link XSElementDecl} is refering to {@link XSType}, |
|
237 * return that element, otherwise null. |
|
238 */ |
|
239 private @Nullable XSElementDecl getSoleElementReferer(@NotNull XSType t) { |
|
240 Set<XSComponent> referer = builder.getReferer(t); |
|
241 |
|
242 XSElementDecl sole = null; |
|
243 for (XSComponent r : referer) { |
|
244 if(r instanceof XSElementDecl) { |
|
245 XSElementDecl x = (XSElementDecl) r; |
|
246 if(!x.isGlobal()) |
|
247 // local element references can be ignored, as their names are either given |
|
248 // by the property, or by the JAXBElement (for things like mixed contents) |
|
249 continue; |
|
250 if(sole==null) sole=x; |
|
251 else return null; // more than one |
|
252 } else { |
|
253 // if another type refers to this type, that means |
|
254 // this type has a sub-type, so type substitution is possible now. |
|
255 return null; |
|
256 } |
|
257 } |
|
258 |
|
259 return sole; |
|
260 } |
|
261 |
|
262 public CElement elementDecl(XSElementDecl decl) { |
|
263 CElement r = allow(decl,decl.getName()); |
|
264 |
|
265 if(r==null) { |
|
266 QName tagName = getName(decl); |
|
267 CCustomizations custs = builder.getBindInfo(decl).toCustomizationList(); |
|
268 |
|
269 if(decl.isGlobal()) { |
|
270 if(isCollapsable(decl)) { |
|
271 // we want the returned type to be built as a complex type, |
|
272 // so the binding cannot be delayed. |
|
273 return selector.bindToType(decl.getType().asComplexType(),decl,true); |
|
274 } else { |
|
275 String className = null; |
|
276 if(getGlobalBinding().isGenerateElementClass()) |
|
277 className = deriveName(decl); |
|
278 |
|
279 // otherwise map global elements to JAXBElement |
|
280 CElementInfo cei = new CElementInfo( |
|
281 model, tagName, selector.getClassScope(), className, custs, decl.getLocator()); |
|
282 selector.boundElements.put(decl,cei); |
|
283 |
|
284 stb.refererStack.push(decl); // referer is element |
|
285 cei.initContentType( selector.bindToType(decl.getType(),decl), decl, decl.getDefaultValue() ); |
|
286 stb.refererStack.pop(); |
|
287 r = cei; |
|
288 } |
|
289 } |
|
290 } |
|
291 |
|
292 // have the substitution member derive from the substitution head |
|
293 XSElementDecl top = decl.getSubstAffiliation(); |
|
294 if(top!=null) { |
|
295 CElement topci = selector.bindToType(top,decl); |
|
296 |
|
297 if(r instanceof CClassInfo && topci instanceof CClassInfo) |
|
298 ((CClassInfo)r).setBaseClass((CClassInfo)topci); |
|
299 if (r instanceof CElementInfo && topci instanceof CElementInfo) |
|
300 ((CElementInfo)r).setSubstitutionHead((CElementInfo)topci); |
|
301 } |
|
302 |
|
303 return r; |
|
304 } |
|
305 |
|
306 public CClassInfo empty( XSContentType ct ) { return null; } |
|
307 |
|
308 public CClassInfo identityConstraint(XSIdentityConstraint xsIdentityConstraint) { |
|
309 return never(); |
|
310 } |
|
311 |
|
312 public CClassInfo xpath(XSXPath xsxPath) { |
|
313 return never(); |
|
314 } |
|
315 |
|
316 public CClassInfo attributeUse(XSAttributeUse use) { |
|
317 return never(); |
|
318 } |
|
319 |
|
320 public CElement simpleType(XSSimpleType type) { |
|
321 CElement c = allow(type,type.getName()); |
|
322 if(c!=null) return c; |
|
323 |
|
324 if(getGlobalBinding().isSimpleTypeSubstitution() && type.isGlobal()) { |
|
325 return new CClassInfo(model,selector.getClassScope(), |
|
326 deriveName(type), type.getLocator(), getName(type), null, type, null ); |
|
327 } |
|
328 |
|
329 return never(); |
|
330 } |
|
331 |
|
332 public CClassInfo particle(XSParticle particle) { |
|
333 return never(); |
|
334 } |
|
335 |
|
336 public CClassInfo wildcard(XSWildcard wc) { |
|
337 return never(); |
|
338 } |
|
339 |
|
340 |
|
341 // these methods won't be used |
|
342 public CClassInfo annotation(XSAnnotation annon) { |
|
343 assert false; |
|
344 return null; |
|
345 } |
|
346 |
|
347 public CClassInfo notation(XSNotation not) { |
|
348 assert false; |
|
349 return null; |
|
350 } |
|
351 |
|
352 public CClassInfo facet(XSFacet decl) { |
|
353 assert false; |
|
354 return null; |
|
355 } |
|
356 public CClassInfo schema(XSSchema schema) { |
|
357 assert false; |
|
358 return null; |
|
359 } |
|
360 |
|
361 |
|
362 |
|
363 |
|
364 |
|
365 /** |
|
366 * Makes sure that the component doesn't carry a {@link BIClass} |
|
367 * customization. |
|
368 * |
|
369 * @return |
|
370 * return value is unused. Since most of the caller needs to |
|
371 * return null, to make the code a little bit shorter, this |
|
372 * method always return null (so that the caller can always |
|
373 * say <code>return never(sc);</code>. |
|
374 */ |
|
375 private CClassInfo never() { |
|
376 // all we need to do here is just not to acknowledge |
|
377 // any class customization. Then this class customization |
|
378 // will be reported as an error later when we check all |
|
379 // unacknowledged customizations. |
|
380 |
|
381 |
|
382 // BIDeclaration cust=owner.getBindInfo(component).get(BIClass.NAME); |
|
383 // if(cust!=null) { |
|
384 // // error |
|
385 // owner.errorReporter.error( |
|
386 // cust.getLocation(), |
|
387 // "test {0}", NameGetter.get(component) ); |
|
388 // } |
|
389 return null; |
|
390 } |
|
391 |
|
392 /** |
|
393 * Checks if a component carries a customization to map it to a class. |
|
394 * If so, make it a class. |
|
395 * |
|
396 * @param defaultBaseName |
|
397 * The token which will be used as the basis of the class name |
|
398 * if the class name is not specified in the customization. |
|
399 * This is usually the name of an element declaration, and so on. |
|
400 * |
|
401 * This parameter can be null, in that case it would be an error |
|
402 * if a name is not given by the customization. |
|
403 */ |
|
404 private CElement allow( XSComponent component, String defaultBaseName ) { |
|
405 |
|
406 BIClass decl = null; |
|
407 |
|
408 if(component instanceof XSComplexType) { |
|
409 XSType complexType = (XSType)component; |
|
410 |
|
411 BIClass lastFoundRecursiveBiClass = null; |
|
412 |
|
413 if(complexType.getName() != null) { |
|
414 while( ! schemas.getAnyType().equals(complexType)) { |
|
415 BindInfo bindInfo = builder.getBindInfo(complexType); |
|
416 BIClass biClass = bindInfo.get(BIClass.class); |
|
417 |
|
418 if(biClass != null && "true".equals(biClass.getRecursive())) |
|
419 lastFoundRecursiveBiClass = biClass; |
|
420 |
|
421 complexType = complexType.getBaseType(); |
|
422 } |
|
423 } |
|
424 |
|
425 // use this as biclass for current component |
|
426 decl = lastFoundRecursiveBiClass; |
|
427 |
|
428 } |
|
429 |
|
430 BindInfo bindInfo = builder.getBindInfo(component); |
|
431 if(decl == null) { |
|
432 decl = bindInfo.get(BIClass.class); |
|
433 if(decl==null) return null; |
|
434 } |
|
435 |
|
436 decl.markAsAcknowledged(); |
|
437 |
|
438 // first consider binding to the class reference. |
|
439 String ref = decl.getExistingClassRef(); |
|
440 if(ref!=null) { |
|
441 if(!JJavaName.isFullyQualifiedClassName(ref)) { |
|
442 Ring.get(ErrorReceiver.class).error( decl.getLocation(), |
|
443 Messages.format(Messages.ERR_INCORRECT_CLASS_NAME,ref) ); |
|
444 // recover by ignoring @ref |
|
445 } else { |
|
446 if(component instanceof XSComplexType) { |
|
447 // UGLY UGLY UGLY |
|
448 // since we are not going to bind this complex type, we need to figure out |
|
449 // its binding mode without actually binding it (and also expose this otherwise |
|
450 // hidden mechanism into this part of the code.) |
|
451 // |
|
452 // this code is potentially dangerous as the base class might have been bound |
|
453 // in different ways. To be correct, we need to figure out how the content type |
|
454 // would have been bound, from the schema. |
|
455 Ring.get(ComplexTypeFieldBuilder.class).recordBindingMode( |
|
456 (XSComplexType)component, ComplexTypeBindingMode.NORMAL |
|
457 ); |
|
458 } |
|
459 return new CClassRef(model, component, decl, bindInfo.toCustomizationList() ); |
|
460 } |
|
461 } |
|
462 |
|
463 String clsName = decl.getClassName(); |
|
464 if(clsName==null) { |
|
465 // if the customiztion doesn't give us a name, derive one |
|
466 // from the current component. |
|
467 if( defaultBaseName==null ) { |
|
468 Ring.get(ErrorReceiver.class).error( decl.getLocation(), |
|
469 Messages.format(Messages.ERR_CLASS_NAME_IS_REQUIRED) ); |
|
470 |
|
471 // recover by generating a pseudo-random name |
|
472 defaultBaseName = "undefined"+component.hashCode(); |
|
473 } |
|
474 clsName = builder.deriveName( defaultBaseName, component ); |
|
475 } else { |
|
476 if( !JJavaName.isJavaIdentifier(clsName) ) { |
|
477 // not a valid Java class name |
|
478 Ring.get(ErrorReceiver.class).error( decl.getLocation(), |
|
479 Messages.format( Messages.ERR_INCORRECT_CLASS_NAME, clsName )); |
|
480 // recover by a dummy name |
|
481 clsName = "Undefined"+component.hashCode(); |
|
482 } |
|
483 } |
|
484 |
|
485 QName typeName = null; |
|
486 QName elementName = null; |
|
487 |
|
488 if(component instanceof XSType) { |
|
489 XSType t = (XSType) component; |
|
490 typeName = getName(t); |
|
491 } |
|
492 |
|
493 if (component instanceof XSElementDecl) { |
|
494 XSElementDecl e = (XSElementDecl) component; |
|
495 elementName = getName(e); |
|
496 } |
|
497 |
|
498 if (component instanceof XSElementDecl && !isCollapsable((XSElementDecl)component)) { |
|
499 XSElementDecl e = ((XSElementDecl)component); |
|
500 |
|
501 CElementInfo cei = new CElementInfo(model, elementName, |
|
502 selector.getClassScope(), clsName, |
|
503 bindInfo.toCustomizationList(), decl.getLocation() ); |
|
504 selector.boundElements.put(e,cei); |
|
505 |
|
506 stb.refererStack.push(component); // referer is element |
|
507 cei.initContentType( |
|
508 selector.bindToType(e.getType(),e), |
|
509 e,e.getDefaultValue()); |
|
510 stb.refererStack.pop(); |
|
511 return cei; |
|
512 // TODO: support javadoc and userSpecifiedImplClass |
|
513 } else { |
|
514 CClassInfo bt = new CClassInfo(model,selector.getClassScope(), |
|
515 clsName, decl.getLocation(), typeName, elementName, component, bindInfo.toCustomizationList() ); |
|
516 |
|
517 // set javadoc class comment. |
|
518 if(decl.getJavadoc()!=null ) |
|
519 bt.javadoc = decl.getJavadoc()+"\n\n"; |
|
520 // add extra blank lines so that the schema fragment |
|
521 // and user-specified javadoc would be separated |
|
522 |
|
523 |
|
524 // if the implClass is given, set it to ClassItem |
|
525 String implClass = decl.getUserSpecifiedImplClass(); |
|
526 if( implClass!=null ) |
|
527 bt.setUserSpecifiedImplClass( implClass ); |
|
528 |
|
529 return bt; |
|
530 } |
|
531 } |
|
532 |
|
533 private BIGlobalBinding getGlobalBinding() { |
|
534 return builder.getGlobalBinding(); |
|
535 } |
|
536 |
|
537 /** |
|
538 * Derives a name from a schema component. |
|
539 * Use the name of the schema component as the default name. |
|
540 */ |
|
541 private String deriveName( XSDeclaration comp ) { |
|
542 return builder.deriveName( comp.getName(), comp ); |
|
543 } |
|
544 |
|
545 /** |
|
546 * Derives a name from a schema component. |
|
547 * For complex types, we take redefinition into account when |
|
548 * deriving a default name. |
|
549 */ |
|
550 private String deriveName( XSComplexType comp ) { |
|
551 String seed = builder.deriveName( comp.getName(), comp ); |
|
552 int cnt = comp.getRedefinedCount(); |
|
553 for( ; cnt>0; cnt-- ) |
|
554 seed = "Original"+seed; |
|
555 return seed; |
|
556 } |
|
557 |
|
558 } |