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  
28  import java.util.LinkedHashSet;
29  import java.util.Set;
30  
31  import javax.activation.MimeType;
32  import javax.xml.namespace.QName;
33  
34  import com.sun.tools.internal.xjc.model.CAdapter;
35  import com.sun.tools.internal.xjc.model.CClass;
36  import com.sun.tools.internal.xjc.model.CClassInfo;
37  import com.sun.tools.internal.xjc.model.CCustomizations;
38  import com.sun.tools.internal.xjc.model.CElement;
39  import com.sun.tools.internal.xjc.model.CElementInfo;
40  import com.sun.tools.internal.xjc.model.CElementPropertyInfo;
41  import com.sun.tools.internal.xjc.model.CReferencePropertyInfo;
42  import com.sun.tools.internal.xjc.model.CTypeRef;
43  import com.sun.tools.internal.xjc.model.Model;
44  import com.sun.tools.internal.xjc.model.Multiplicity;
45  import com.sun.tools.internal.xjc.model.TypeUse;
46  import com.sun.tools.internal.xjc.reader.RawTypeSet;
47  import com.sun.tools.internal.xjc.reader.Ring;
48  import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIDom;
49  import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIGlobalBinding;
50  import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIXSubstitutable;
51  import com.sun.xml.internal.bind.v2.model.core.ID;
52  import com.sun.xml.internal.bind.v2.model.core.WildcardMode;
53  import com.sun.xml.internal.xsom.XSElementDecl;
54  import com.sun.xml.internal.xsom.XSModelGroup;
55  import com.sun.xml.internal.xsom.XSModelGroupDecl;
56  import com.sun.xml.internal.xsom.XSParticle;
57  import com.sun.xml.internal.xsom.XSWildcard;
58  import com.sun.xml.internal.xsom.visitor.XSTermVisitor;
59  
60  /**
61   * Builds {@link RawTypeSet} for XML Schema.
62   *
63   * @author Kohsuke Kawaguchi
64   */
65  public class RawTypeSetBuilder implements XSTermVisitor {
66      /**
67       * @param optional
68       *      if this whole property is optional due to the
69       *      occurrence constraints on ancestors, set this to true.
70       *      this will prevent the primitive types to be generated.
71       */
72      public static RawTypeSet build( XSParticle p, boolean optional ) {
73          RawTypeSetBuilder rtsb = new RawTypeSetBuilder();
74          rtsb.particle(p);
75          Multiplicity mul = MultiplicityCounter.theInstance.particle(p);
76  
77          if(optional)
78              mul = mul.makeOptional();
79  
80          return new RawTypeSet(rtsb.refs,mul);
81      }
82  
83      /**
84       * To avoid declaring the same element twice for a content model like
85       * (A,A), we keep track of element names here while we are building up
86       * this instance.
87       */
88      private final Set<QName> elementNames = new LinkedHashSet<QName>();
89  
90      private final Set<RawTypeSet.Ref> refs = new LinkedHashSet<RawTypeSet.Ref>();
91  
92      protected final BGMBuilder builder = Ring.get(BGMBuilder.class);
93  
94      public RawTypeSetBuilder() {}
95  
96  
97      /**
98       * Gets the {@link RawTypeSet.Ref}s that were built.
99       */
100     public Set<RawTypeSet.Ref> getRefs() {
101         return refs;
102     }
103 
104     /**
105      * Build up {@link #refs} and compute the total multiplicity of this {@link RawTypeSet.Ref} set.
106      */
107     private void particle( XSParticle p ) {
108         // if the DOM customization is present, bind it like a wildcard
109         BIDom dom = builder.getLocalDomCustomization(p);
110         if(dom!=null) {
111             dom.markAsAcknowledged();
112             refs.add(new WildcardRef(WildcardMode.SKIP));
113         } else {
114             p.getTerm().visit(this);
115         }
116     }
117 
118     public void wildcard(XSWildcard wc) {
119         refs.add(new WildcardRef(wc));
120     }
121 
122     public void modelGroupDecl(XSModelGroupDecl decl) {
123         modelGroup(decl.getModelGroup());
124     }
125 
126     public void modelGroup(XSModelGroup group) {
127         for( XSParticle p : group.getChildren())
128             particle(p);
129     }
130 
131     public void elementDecl(XSElementDecl decl) {
132 
133         QName n = BGMBuilder.getName(decl);
134         if(elementNames.add(n)) {
135             CElement elementBean = Ring.get(ClassSelector.class).bindToType(decl,null);
136             if(elementBean==null)
137                 refs.add(new XmlTypeRef(decl));
138             else {
139                 // yikes!
140                 if(elementBean instanceof CClass)
141                     refs.add(new CClassRef(decl,(CClass)elementBean));
142                 else
143                     refs.add(new CElementInfoRef(decl,(CElementInfo)elementBean));
144             }
145         }
146     }
147 
148     /**
149      * Reference to a wildcard.
150      */
151     public static final class WildcardRef extends RawTypeSet.Ref {
152         private final WildcardMode mode;
153 
154         WildcardRef(XSWildcard wildcard) {
155             this.mode = getMode(wildcard);
156         }
157         WildcardRef(WildcardMode mode) {
158             this.mode = mode;
159         }
160 
161         private static WildcardMode getMode(XSWildcard wildcard) {
162             switch(wildcard.getMode()) {
163             case XSWildcard.LAX:
164                 return WildcardMode.LAX;
165             case XSWildcard.STRTICT:
166                 return WildcardMode.STRICT;
167             case XSWildcard.SKIP:
168                 return WildcardMode.SKIP;
169             default:
170                 throw new IllegalStateException();
171             }
172         }
173 
174         protected CTypeRef toTypeRef(CElementPropertyInfo ep) {
175             // we don't allow a mapping to typeRef if the wildcard is present
176             throw new IllegalStateException();
177         }
178 
179         protected void toElementRef(CReferencePropertyInfo prop) {
180             prop.setWildcard(mode);
181         }
182 
183         protected RawTypeSet.Mode canBeType(RawTypeSet parent) {
184             return RawTypeSet.Mode.MUST_BE_REFERENCE;
185         }
186 
187         protected boolean isListOfValues() {
188             return false;
189         }
190 
191         protected ID id() {
192             return ID.NONE;
193         }
194     }
195 
196     /**
197      * Reference to a class that maps from an element.
198      */
199     public static final class CClassRef extends RawTypeSet.Ref {
200         public final CClass target;
201         public final XSElementDecl decl;
202 
203         CClassRef(XSElementDecl decl, CClass target) {
204             this.decl = decl;
205             this.target = target;
206         }
207 
208         protected CTypeRef toTypeRef(CElementPropertyInfo ep) {
209             return new CTypeRef(target,decl);
210         }
211 
212         protected void toElementRef(CReferencePropertyInfo prop) {
213             prop.getElements().add(target);
214         }
215 
216         protected RawTypeSet.Mode canBeType(RawTypeSet parent) {
217             // if element substitution can occur, no way it can be mapped to a list of types
218             if(decl.getSubstitutables().size()>1)
219                 return RawTypeSet.Mode.MUST_BE_REFERENCE;
220 
221             return RawTypeSet.Mode.SHOULD_BE_TYPEREF;
222         }
223 
224         protected boolean isListOfValues() {
225             return false;
226         }
227 
228         protected ID id() {
229             return ID.NONE;
230         }
231     }
232 
233     /**
234      * Reference to a class that maps from an element.
235      */
236     public final class CElementInfoRef extends RawTypeSet.Ref {
237         public final CElementInfo target;
238         public final XSElementDecl decl;
239 
240         CElementInfoRef(XSElementDecl decl, CElementInfo target) {
241             this.decl = decl;
242             this.target = target;
243         }
244 
245         protected CTypeRef toTypeRef(CElementPropertyInfo ep) {
246             assert !target.isCollection();
247             CAdapter a = target.getProperty().getAdapter();
248             if(a!=null && ep!=null) ep.setAdapter(a);
249 
250             return new CTypeRef(target.getContentType(),decl);
251         }
252 
253         protected void toElementRef(CReferencePropertyInfo prop) {
254             prop.getElements().add(target);
255         }
256 
257         protected RawTypeSet.Mode canBeType(RawTypeSet parent) {
258             // if element substitution can occur, no way it can be mapped to a list of types
259             if(decl.getSubstitutables().size()>1)
260                 return RawTypeSet.Mode.MUST_BE_REFERENCE;
261             // BIXSubstitutable also simulates this effect. Useful for separate compilation
262             BIXSubstitutable subst = builder.getBindInfo(decl).get(BIXSubstitutable.class);
263             if(subst!=null) {
264                 subst.markAsAcknowledged();
265                 return RawTypeSet.Mode.MUST_BE_REFERENCE;
266             }
267 
268             // we have no place to put an adater if this thing maps to a type
269             CElementPropertyInfo p = target.getProperty();
270             // if we have an adapter or IDness, which requires special
271             // annotation, and there's more than one element,
272             // we have no place to put the special annotation, so we need JAXBElement.
273             if((parent.refs.size()>1 || !parent.mul.isAtMostOnce()) && p.id()!=ID.NONE)
274                 return RawTypeSet.Mode.MUST_BE_REFERENCE;
275             if(parent.refs.size() > 1 && p.getAdapter() != null)
276                 return RawTypeSet.Mode.MUST_BE_REFERENCE;
277 
278             if(target.hasClass())
279                 // if the CElementInfo was explicitly bound to a class (which happen if and only if
280                 // the user requested so, then map that to reference property so that the user sees a class
281                 return RawTypeSet.Mode.CAN_BE_TYPEREF;
282             else
283                 return RawTypeSet.Mode.SHOULD_BE_TYPEREF;
284         }
285 
286         protected boolean isListOfValues() {
287             return target.getProperty().isValueList();
288         }
289 
290         protected ID id() {
291             return target.getProperty().id();
292         }
293 
294         @Override
295         protected MimeType getExpectedMimeType() {
296             return target.getProperty().getExpectedMimeType();
297         }
298     }
299 
300     /**
301      * References to a type. Could be global or local.
302      */
303     public static final class XmlTypeRef extends RawTypeSet.Ref {
304         private final XSElementDecl decl;
305         private final TypeUse target;
306 
307         public XmlTypeRef(XSElementDecl decl) {
308             this.decl = decl;
309             SimpleTypeBuilder stb = Ring.get(SimpleTypeBuilder.class);
310             stb.refererStack.push(decl);
311             TypeUse r = Ring.get(ClassSelector.class).bindToType(decl.getType(),decl);
312             stb.refererStack.pop();
313             target = r;
314         }
315 
316         protected CTypeRef toTypeRef(CElementPropertyInfo ep) {
317             if(ep!=null && target.getAdapterUse()!=null)
318                 ep.setAdapter(target.getAdapterUse());
319             return new CTypeRef(target.getInfo(),decl);
320         }
321 
322         /**
323          * The whole type set can be later bound to a reference property,
324          * in which case we need to generate additional code to wrap this
325          * type reference into an element class.
326          *
327          * This method generates such an element class and returns it.
328          */
329         protected void toElementRef(CReferencePropertyInfo prop) {
330             CClassInfo scope = Ring.get(ClassSelector.class).getCurrentBean();
331             Model model = Ring.get(Model.class);
332 
333             CCustomizations custs = Ring.get(BGMBuilder.class).getBindInfo(decl).toCustomizationList();
334 
335             if(target instanceof CClassInfo && Ring.get(BIGlobalBinding.class).isSimpleMode()) {
336                 CClassInfo bean = new CClassInfo(model,scope,
337                                 model.getNameConverter().toClassName(decl.getName()),
338                                 decl.getLocator(), null, BGMBuilder.getName(decl), decl,
339                                 custs);
340                 bean.setBaseClass((CClassInfo)target);
341                 prop.getElements().add(bean);
342             } else {
343                 CElementInfo e = new CElementInfo(model,BGMBuilder.getName(decl),scope,target,
344                         decl.getDefaultValue(), decl, custs, decl.getLocator());
345                 prop.getElements().add(e);
346             }
347         }
348 
349         protected RawTypeSet.Mode canBeType(RawTypeSet parent) {
350             // if we have an adapter or IDness, which requires special
351             // annotation, and there's more than one element,
352             // we have no place to put the special annotation, so we need JAXBElement.
353             if((parent.refs.size()>1 || !parent.mul.isAtMostOnce()) && target.idUse()!=ID.NONE)
354                 return RawTypeSet.Mode.MUST_BE_REFERENCE;
355             if(parent.refs.size() > 1 && target.getAdapterUse() != null)
356                 return RawTypeSet.Mode.MUST_BE_REFERENCE;
357 
358             // nillable and optional at the same time. needs an element wrapper to distinguish those
359             // two states. But this is not a hard requirement.
360             if(decl.isNillable() && parent.mul.isOptional())
361                 return RawTypeSet.Mode.CAN_BE_TYPEREF;
362 
363             return RawTypeSet.Mode.SHOULD_BE_TYPEREF;
364         }
365 
366         protected boolean isListOfValues() {
367             return target.isCollection();
368         }
369 
370         protected ID id() {
371             return target.idUse();
372         }
373 
374         @Override
375         protected MimeType getExpectedMimeType() {
376             return target.getExpectedMimeType();
377         }
378     }
379 }