View Javadoc
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 }