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.bindinfo;
27  
28  import java.util.Collections;
29  import java.util.HashMap;
30  import java.util.Map;
31  import java.util.Set;
32  
33  import javax.xml.bind.annotation.XmlAttribute;
34  import javax.xml.bind.annotation.XmlElement;
35  import javax.xml.bind.annotation.XmlEnumValue;
36  import javax.xml.bind.annotation.XmlRootElement;
37  import javax.xml.bind.annotation.XmlTransient;
38  import javax.xml.namespace.QName;
39  
40  import com.sun.codemodel.internal.ClassType;
41  import com.sun.codemodel.internal.JClassAlreadyExistsException;
42  import com.sun.codemodel.internal.JCodeModel;
43  import com.sun.codemodel.internal.JDefinedClass;
44  import com.sun.tools.internal.xjc.ErrorReceiver;
45  import com.sun.tools.internal.xjc.generator.bean.ImplStructureStrategy;
46  import static com.sun.tools.internal.xjc.generator.bean.ImplStructureStrategy.BEAN_ONLY;
47  import com.sun.tools.internal.xjc.model.Model;
48  import com.sun.tools.internal.xjc.reader.Const;
49  import com.sun.tools.internal.xjc.reader.Ring;
50  import com.sun.tools.internal.xjc.reader.xmlschema.SimpleTypeBuilder;
51  import com.sun.tools.internal.xjc.util.ReadOnlyAdapter;
52  import com.sun.xml.internal.bind.api.impl.NameConverter;
53  import com.sun.xml.internal.bind.v2.WellKnownNamespace;
54  import com.sun.xml.internal.xsom.XSDeclaration;
55  import com.sun.xml.internal.xsom.XSSchemaSet;
56  import com.sun.xml.internal.xsom.XSSimpleType;
57  
58  /**
59   * Global binding customization. The code is highly temporary.
60   *
61   * <p>
62   * One of the information contained in a global customization
63   * is the default binding for properties. This object contains a
64   * BIProperty object to keep this information.
65   *
66   * @author
67   *  Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com)
68   */
69  @XmlRootElement(name="globalBindings")
70  public final class BIGlobalBinding extends AbstractDeclarationImpl {
71  
72  
73      /**
74       * Gets the name converter that will govern the XML->Java
75       * name conversion process for this compilation.
76       *
77       * <p>
78       * The "underscoreBinding" customization will determine
79       * the exact object returned from this method. The rest of XJC
80       * should just use the NameConverter interface.
81       *
82       * <p>
83       * Always non-null.
84       */
85      @XmlTransient
86      public NameConverter nameConverter = NameConverter.standard;
87  
88      // JAXB will use this property to set nameConverter
89      @XmlAttribute
90      void setUnderscoreBinding( UnderscoreBinding ub ) {
91          nameConverter = ub.nc;
92      }
93  
94      UnderscoreBinding getUnderscoreBinding() {
95          throw new IllegalStateException();  // no need for this
96      }
97  
98      public JDefinedClass getSuperClass() {
99          if(superClass==null)    return null;
100         return superClass.getClazz(ClassType.CLASS);
101     }
102 
103     public JDefinedClass getSuperInterface() {
104         if(superInterface==null)    return null;
105         return superInterface.getClazz(ClassType.INTERFACE);
106     }
107 
108     public BIProperty getDefaultProperty() {
109         return defaultProperty;
110     }
111 
112     public boolean isJavaNamingConventionEnabled() {
113         return isJavaNamingConventionEnabled;
114     }
115 
116     public BISerializable getSerializable() {
117         return serializable;
118     }
119 
120     public boolean isGenerateElementClass() {
121         return generateElementClass;
122     }
123 
124     public boolean isGenerateMixedExtensions() {
125         return generateMixedExtensions;
126     }
127 
128     public boolean isChoiceContentPropertyEnabled() {
129         return choiceContentProperty;
130     }
131 
132     public int getDefaultEnumMemberSizeCap() {
133         return defaultEnumMemberSizeCap;
134     }
135 
136     public boolean isSimpleMode() {
137         return simpleMode!=null;
138     }
139 
140     public boolean isRestrictionFreshType() {
141         return treatRestrictionLikeNewType !=null;
142     }
143 
144     public EnumMemberMode getEnumMemberMode() {
145         return generateEnumMemberName;
146     }
147 
148     public boolean isSimpleTypeSubstitution() {
149         return simpleTypeSubstitution;
150     }
151 
152     public ImplStructureStrategy getCodeGenerationStrategy() {
153         return codeGenerationStrategy;
154     }
155 
156     public LocalScoping getFlattenClasses() {
157         return flattenClasses;
158     }
159 
160     /**
161      * Performs error check
162      */
163     public void errorCheck() {
164         ErrorReceiver er = Ring.get(ErrorReceiver.class);
165         for (QName n : enumBaseTypes) {
166             XSSchemaSet xs = Ring.get(XSSchemaSet.class);
167             XSSimpleType st = xs.getSimpleType(n.getNamespaceURI(), n.getLocalPart());
168             if(st==null) {
169                 er.error(loc,Messages.ERR_UNDEFINED_SIMPLE_TYPE.format(n));
170                 continue;
171             }
172 
173             if(!SimpleTypeBuilder.canBeMappedToTypeSafeEnum(st)) {
174                 er.error(loc,Messages.ERR_CANNOT_BE_BOUND_TO_SIMPLETYPE.format(n));
175                 continue;
176             }
177         }
178     }
179 
180     private static enum UnderscoreBinding {
181         @XmlEnumValue("asWordSeparator")
182         WORD_SEPARATOR(NameConverter.standard),
183         @XmlEnumValue("asCharInWord")
184         CHAR_IN_WORD(NameConverter.jaxrpcCompatible);
185 
186         final NameConverter nc;
187 
188         UnderscoreBinding(NameConverter nc) {
189             this.nc = nc;
190         }
191     }
192 
193     /**
194      * Returns true if the "isJavaNamingConventionEnabled" option is turned on.
195      *
196      * In this mode, the compiler is expected to apply XML-to-Java name
197      * conversion algorithm even to names given by customizations.
198      *
199      * This method is intended to be called by other BIXXX classes.
200      * The effect of this switch should be hidden inside this package.
201      * IOW, the reader.xmlschema package shouldn't be aware of this switch.
202      */
203     @XmlAttribute(name="enableJavaNamingConventions")
204     /*package*/ boolean isJavaNamingConventionEnabled = true;
205 
206     /**
207      * True to generate classes for every simple type.
208      */
209     @XmlAttribute(name="mapSimpleTypeDef")
210     boolean simpleTypeSubstitution = false;
211 
212     /**
213      * Gets the default defaultProperty customization.
214      */
215     @XmlTransient
216     private BIProperty defaultProperty;
217 
218     /*
219         Three properties used to construct a default property
220     */
221     @XmlAttribute
222     private boolean fixedAttributeAsConstantProperty = false;
223     @XmlAttribute
224     private CollectionTypeAttribute collectionType = new CollectionTypeAttribute();
225     @XmlAttribute
226     void setGenerateIsSetMethod(boolean b) {
227         optionalProperty = b ? OptionalPropertyMode.ISSET : OptionalPropertyMode.WRAPPER;
228     }
229 
230 
231     /**
232      * Returns true if the compiler needs to generate type-safe enum
233      * member names when enumeration values cannot be used as constant names.
234      */
235     @XmlAttribute(name="typesafeEnumMemberName")
236     EnumMemberMode generateEnumMemberName = EnumMemberMode.SKIP;
237 
238     /**
239      * The code generation strategy.
240      */
241     @XmlAttribute(name="generateValueClass")
242     ImplStructureStrategy codeGenerationStrategy = BEAN_ONLY;
243 
244     /**
245      * Set of datatype names. For a type-safe enum class
246      * to be generated, the underlying XML datatype must be derived from
247      * one of the types in this set.
248      */
249     // default value is set in the post-init action
250     @XmlAttribute(name="typesafeEnumBase")
251     private Set<QName> enumBaseTypes;
252 
253     /**
254      * Returns {@link BISerializable} if the extension is specified,
255      * or null otherwise.
256      */
257     @XmlElement
258     private BISerializable serializable = null;
259 
260     /**
261      * If &lt;xjc:superClass> extension is specified,
262      * returns the specified root class. Otherwise null.
263      */
264     @XmlElement(namespace=Const.XJC_EXTENSION_URI)
265     ClassNameBean superClass = null;
266 
267     /**
268      * If &lt;xjc:superInterface> extension is specified,
269      * returns the specified root class. Otherwise null.
270      */
271     @XmlElement(namespace=Const.XJC_EXTENSION_URI)
272     ClassNameBean superInterface = null;
273 
274     /**
275      * Generate the simpler optimized code, but not necessarily
276      * conforming to the spec.
277      */
278     @XmlElement(name="simple",namespace=Const.XJC_EXTENSION_URI)
279     String simpleMode = null;
280 
281     /**
282      * Handles complex type restriction as if it were a new type.
283      */
284     @XmlElement(name="treatRestrictionLikeNewType",namespace=Const.XJC_EXTENSION_URI)
285     String treatRestrictionLikeNewType = null;
286 
287     /**
288      * True to generate a class for elements by default.
289      */
290     @XmlAttribute
291     boolean generateElementClass = false;
292 
293     @XmlAttribute
294     boolean generateMixedExtensions = false;
295 
296     @XmlElement(namespace=Const.XJC_EXTENSION_URI)
297     Boolean generateElementProperty = null;
298 
299     @XmlAttribute(name="generateElementProperty")     // for JAXB unmarshaller
300     private void setGenerateElementPropertyStd(boolean value) {
301         generateElementProperty = value;
302     }
303 
304     @XmlAttribute
305     boolean choiceContentProperty = false;
306 
307     @XmlAttribute
308     OptionalPropertyMode optionalProperty = OptionalPropertyMode.WRAPPER;
309 
310     /**
311      * Default cap to the number of constants in the enum.
312      * We won't attempt to produce a type-safe enum by default
313      * if there are more enumeration facets than specified in this field.
314      */
315     @XmlAttribute(name="typesafeEnumMaxMembers")
316     int defaultEnumMemberSizeCap = 256;
317 
318     /**
319      * If true, interfaces/classes that are normally generated as a nested interface/class
320      * will be generated into the package, allowing the generated classes to be flat.
321      *
322      * See <a href="http://monaco.sfbay/detail.jsf?cr=4969415">Bug 4969415</a> for the motivation.
323      */
324     @XmlAttribute(name="localScoping")
325     LocalScoping flattenClasses = LocalScoping.NESTED;
326 
327     /**
328      * Globally-defined conversion customizations.
329      *
330      * @see #setGlobalConversions
331      */
332     @XmlTransient
333     private final Map<QName,BIConversion> globalConversions = new HashMap<QName, BIConversion>();
334 
335     // method for JAXB unmarshaller
336     @XmlElement(name="javaType")
337     private void setGlobalConversions(GlobalStandardConversion[] convs) {
338         for (GlobalStandardConversion u : convs) {
339             globalConversions.put(u.xmlType,u);
340         }
341     }
342 
343     @XmlElement(name="javaType",namespace=Const.XJC_EXTENSION_URI)
344     private void setGlobalConversions2(GlobalVendorConversion[] convs) {
345         for (GlobalVendorConversion u : convs) {
346             globalConversions.put(u.xmlType,u);
347         }
348     }
349 
350     //
351     // these customizations were valid in 1.0, but in 2.0 we don't
352     // use them. OTOH, we don't want to issue an error for them,
353     // so we just define a mapping and ignore the value.
354     //
355     @XmlElement(namespace=Const.XJC_EXTENSION_URI)
356     String noMarshaller = null;
357     @XmlElement(namespace=Const.XJC_EXTENSION_URI)
358     String noUnmarshaller = null;
359     @XmlElement(namespace=Const.XJC_EXTENSION_URI)
360     String noValidator = null;
361     @XmlElement(namespace=Const.XJC_EXTENSION_URI)
362     String noValidatingUnmarshaller = null;
363     @XmlElement(namespace=Const.XJC_EXTENSION_URI)
364     TypeSubstitutionElement typeSubstitution = null;
365 
366     /**
367      * Another 1.0 compatibility customization (but we accept it
368      * and treat it as {@link #serializable})
369      */
370     @XmlElement(name="serializable",namespace=Const.XJC_EXTENSION_URI)
371     void setXjcSerializable(BISerializable s) {
372         this.serializable = s;
373     }
374 
375 
376 
377     private static final class TypeSubstitutionElement {
378         @XmlAttribute
379         String type;
380     }
381 
382     public void onSetOwner() {
383         super.onSetOwner();
384         // if one is given by options, use that
385         NameConverter nc = Ring.get(Model.class).options.getNameConverter();
386         if(nc!=null)
387             nameConverter = nc;
388     }
389 
390     /**
391      * Creates a bind info object with the default values
392      */
393     public BIGlobalBinding() {
394     }
395 
396     public void setParent(BindInfo parent) {
397         super.setParent(parent);
398         // fill in the remaining default values
399         if(enumBaseTypes==null)
400             enumBaseTypes = Collections.singleton(new QName(WellKnownNamespace.XML_SCHEMA,"string"));
401 
402         this.defaultProperty = new BIProperty(getLocation(),null,null,null,
403                 collectionType, fixedAttributeAsConstantProperty, optionalProperty, generateElementProperty );
404         defaultProperty.setParent(parent); // don't forget to initialize the defaultProperty
405     }
406 
407     /**
408      * Moves global BIConversion to the right object.
409      */
410     public void dispatchGlobalConversions( XSSchemaSet schema ) {
411         // also set parent to the global conversions
412         for( Map.Entry<QName,BIConversion> e : globalConversions.entrySet() ) {
413 
414             QName name = e.getKey();
415             BIConversion conv = e.getValue();
416 
417             XSSimpleType st = schema.getSimpleType(name.getNamespaceURI(),name.getLocalPart());
418             if(st==null) {
419                 Ring.get(ErrorReceiver.class).error(
420                     getLocation(),
421                     Messages.ERR_UNDEFINED_SIMPLE_TYPE.format(name)
422                 );
423                 continue; // abort
424             }
425 
426             getBuilder().getOrCreateBindInfo(st).addDecl(conv);
427         }
428     }
429 
430 
431     /**
432      * Checks if the given XML Schema built-in type can be mapped to
433      * a type-safe enum class.
434      *
435      * @param typeName
436      */
437     public boolean canBeMappedToTypeSafeEnum( QName typeName ) {
438         return enumBaseTypes.contains(typeName);
439     }
440 
441     public boolean canBeMappedToTypeSafeEnum( String nsUri, String localName ) {
442         return canBeMappedToTypeSafeEnum(new QName(nsUri,localName));
443     }
444 
445     public boolean canBeMappedToTypeSafeEnum( XSDeclaration decl ) {
446         return canBeMappedToTypeSafeEnum( decl.getTargetNamespace(), decl.getName() );
447     }
448 
449 
450     public QName getName() { return NAME; }
451     public static final QName NAME = new QName(
452         Const.JAXB_NSURI, "globalBindings" );
453 
454 
455     /**
456      * Used to unmarshal
457      * <xmp>
458      * <[element] name="className" />
459      * </xmp>
460      */
461     static final class ClassNameBean {
462         @XmlAttribute(required=true)
463         String name;
464 
465         /**
466          * Computed from {@link #name} on demand.
467          */
468         @XmlTransient
469         JDefinedClass clazz;
470 
471         JDefinedClass getClazz(ClassType t) {
472             if (clazz != null) return clazz;
473             try {
474                 JCodeModel codeModel = Ring.get(JCodeModel.class);
475                 clazz = codeModel._class(name, t);
476                 clazz.hide();
477                 return clazz;
478             } catch (JClassAlreadyExistsException e) {
479                 return e.getExistingClass();
480             }
481         }
482     }
483 
484     static final class ClassNameAdapter extends ReadOnlyAdapter<ClassNameBean,String> {
485         public String unmarshal(ClassNameBean bean) throws Exception {
486             return bean.name;
487         }
488     }
489 
490     /**
491      * Global &lt;jaxb:javaType>.
492      */
493     static final class GlobalStandardConversion extends BIConversion.User {
494         @XmlAttribute
495         QName xmlType;
496 
497         @Override
498         public boolean equals(Object obj) {
499             if(obj instanceof GlobalStandardConversion) {
500                 return ((GlobalStandardConversion)obj).xmlType.equals(xmlType);
501     }
502 
503             return false;
504         }
505 
506         @Override
507         public int hashCode() {
508             int hash = 7;
509             hash = 73 * hash + (this.xmlType != null ? this.xmlType.hashCode() : 0);
510             return hash;
511         }
512     }
513 
514     /**
515      * Global &lt;xjc:javaType>.
516      */
517     static final class GlobalVendorConversion extends BIConversion.UserAdapter {
518         @XmlAttribute
519         QName xmlType;
520 
521         @Override
522         public boolean equals(Object obj) {
523             if(obj instanceof GlobalVendorConversion) {
524                 return ((GlobalVendorConversion)obj).xmlType.equals(xmlType);
525     }
526 
527             return false;
528         }
529 
530         @Override
531         public int hashCode() {
532             int hash = 7;
533             hash = 73 * hash + (this.xmlType != null ? this.xmlType.hashCode() : 0);
534             return hash;
535         }
536     }
537 
538     /* don't want to override equals to avoid overriding hashcode for this complex object, too */
539     public boolean isEqual(BIGlobalBinding b) {
540         boolean equal =
541             this.isJavaNamingConventionEnabled == b.isJavaNamingConventionEnabled &&
542             this.simpleTypeSubstitution == b.simpleTypeSubstitution &&
543             this.fixedAttributeAsConstantProperty == b.fixedAttributeAsConstantProperty &&
544             this.generateEnumMemberName == b.generateEnumMemberName &&
545             this.codeGenerationStrategy == b.codeGenerationStrategy &&
546             this.serializable == b.serializable &&
547             this.superClass == b.superClass &&
548             this.superInterface == b.superInterface &&
549             this.generateElementClass == b.generateElementClass &&
550             this.generateMixedExtensions == b.generateMixedExtensions &&
551             this.generateElementProperty == b.generateElementProperty &&
552             this.choiceContentProperty == b.choiceContentProperty &&
553             this.optionalProperty == b.optionalProperty &&
554             this.defaultEnumMemberSizeCap == b.defaultEnumMemberSizeCap &&
555             this.flattenClasses == b.flattenClasses;
556 
557         if (!equal) return false;
558 
559         return isEqual(this.nameConverter, b.nameConverter) &&
560                isEqual(this.noMarshaller, b.noMarshaller) &&
561                isEqual(this.noUnmarshaller, b.noUnmarshaller) &&
562                isEqual(this.noValidator, b.noValidator) &&
563                isEqual(this.noValidatingUnmarshaller, b.noValidatingUnmarshaller) &&
564                isEqual(this.typeSubstitution, b.typeSubstitution) &&
565                isEqual(this.simpleMode, b.simpleMode) &&
566                isEqual(this.enumBaseTypes, b.enumBaseTypes) &&
567                isEqual(this.treatRestrictionLikeNewType, b.treatRestrictionLikeNewType) &&
568                isEqual(this.globalConversions, b.globalConversions);
569     }
570 
571     private boolean isEqual(Object a, Object b) {
572         if (a != null) {
573             return a.equals(b);
574         }
575         return (b == null);
576     }
577 }