|
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.model; |
|
27 |
|
28 import java.util.ArrayList; |
|
29 import java.util.Collection; |
|
30 import java.util.HashSet; |
|
31 import java.util.Iterator; |
|
32 import java.util.List; |
|
33 import java.util.Set; |
|
34 |
|
35 import javax.xml.bind.annotation.XmlElement; |
|
36 import javax.xml.bind.annotation.XmlID; |
|
37 import javax.xml.bind.annotation.XmlIDREF; |
|
38 import javax.xml.bind.annotation.XmlRootElement; |
|
39 import javax.xml.namespace.QName; |
|
40 |
|
41 import com.sun.codemodel.internal.JClass; |
|
42 import com.sun.codemodel.internal.JCodeModel; |
|
43 import com.sun.codemodel.internal.JPackage; |
|
44 import com.sun.istack.internal.Nullable; |
|
45 import com.sun.tools.internal.xjc.Language; |
|
46 import com.sun.tools.internal.xjc.model.nav.NClass; |
|
47 import com.sun.tools.internal.xjc.model.nav.NType; |
|
48 import com.sun.tools.internal.xjc.outline.Aspect; |
|
49 import com.sun.tools.internal.xjc.outline.Outline; |
|
50 import com.sun.tools.internal.xjc.reader.Ring; |
|
51 import com.sun.tools.internal.xjc.reader.xmlschema.BGMBuilder; |
|
52 import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIFactoryMethod; |
|
53 import com.sun.xml.internal.bind.v2.model.core.ClassInfo; |
|
54 import com.sun.xml.internal.bind.v2.model.core.Element; |
|
55 import com.sun.xml.internal.xsom.XSComponent; |
|
56 |
|
57 import org.xml.sax.Locator; |
|
58 |
|
59 /** |
|
60 * Mutable {@link ClassInfo} represenatation. |
|
61 * |
|
62 * <p> |
|
63 * Schema parsers build these objects. |
|
64 * |
|
65 * @author Kohsuke Kawaguchi |
|
66 */ |
|
67 public final class CClassInfo extends AbstractCElement implements ClassInfo<NType,NClass>, CClassInfoParent, CClass, NClass { |
|
68 |
|
69 @XmlIDREF |
|
70 private CClass baseClass; |
|
71 |
|
72 /** |
|
73 * List of all subclasses, together with {@link #nextSibling}. |
|
74 * |
|
75 * If this class has no sub-class, this field is null. Otherwise, |
|
76 * this field points to a sub-class of this class. From there you can enumerate |
|
77 * all the sub-classes by using {@link #nextSibling}. |
|
78 */ |
|
79 private CClassInfo firstSubclass; |
|
80 |
|
81 /** |
|
82 * @see #firstSubclass |
|
83 */ |
|
84 private CClassInfo nextSibling = null; |
|
85 |
|
86 /** |
|
87 * @see #getTypeName() |
|
88 */ |
|
89 private final QName typeName; |
|
90 |
|
91 /** |
|
92 * Custom {@link #getSqueezedName() squeezed name}, if any. |
|
93 */ |
|
94 private /*almost final*/ @Nullable String squeezedName; |
|
95 |
|
96 /** |
|
97 * If this class also gets {@link XmlRootElement}, the class name. |
|
98 */ |
|
99 private final @Nullable QName elementName; |
|
100 |
|
101 private boolean isOrdered = true; |
|
102 |
|
103 private final List<CPropertyInfo> properties = new ArrayList<CPropertyInfo>(); |
|
104 |
|
105 /** |
|
106 * TODO: revisit this design. |
|
107 * we should at least do a basic encapsulation to avoid careless |
|
108 * mistakes. Maybe we should even differ the javadoc generation |
|
109 * by queueing runners. |
|
110 */ |
|
111 public String javadoc; |
|
112 |
|
113 @XmlIDREF |
|
114 private final CClassInfoParent parent; |
|
115 |
|
116 /** |
|
117 * short name. |
|
118 */ |
|
119 public final String shortName; |
|
120 |
|
121 /** |
|
122 * Optional user-specified implementation override class. |
|
123 */ |
|
124 private @Nullable String implClass; |
|
125 |
|
126 /** |
|
127 * The {@link Model} object to which this bean belongs. |
|
128 */ |
|
129 public final Model model; |
|
130 |
|
131 /** |
|
132 * @see #hasAttributeWildcard() |
|
133 */ |
|
134 private boolean hasAttributeWildcard; |
|
135 |
|
136 |
|
137 public CClassInfo(Model model,JPackage pkg, String shortName, Locator location, QName typeName, QName elementName, XSComponent source, CCustomizations customizations) { |
|
138 this(model,model.getPackage(pkg),shortName,location,typeName,elementName,source,customizations); |
|
139 } |
|
140 |
|
141 public CClassInfo(Model model,CClassInfoParent p, String shortName, Locator location, QName typeName, QName elementName, XSComponent source, CCustomizations customizations) { |
|
142 super(model,source,location,customizations); |
|
143 this.model = model; |
|
144 this.parent = p; |
|
145 this.shortName = model.allocator.assignClassName(parent,shortName); |
|
146 this.typeName = typeName; |
|
147 this.elementName = elementName; |
|
148 |
|
149 Language schemaLanguage = model.options.getSchemaLanguage(); |
|
150 if ((schemaLanguage != null) && |
|
151 (schemaLanguage.equals(Language.XMLSCHEMA) || schemaLanguage.equals(Language.WSDL))) { |
|
152 BIFactoryMethod factoryMethod = Ring.get(BGMBuilder.class).getBindInfo(source).get(BIFactoryMethod.class); |
|
153 if(factoryMethod!=null) { |
|
154 factoryMethod.markAsAcknowledged(); |
|
155 this.squeezedName = factoryMethod.name; |
|
156 } |
|
157 } |
|
158 |
|
159 model.add(this); |
|
160 } |
|
161 |
|
162 public CClassInfo(Model model,JCodeModel cm, String fullName, Locator location, QName typeName, QName elementName, XSComponent source, CCustomizations customizations) { |
|
163 super(model,source,location,customizations); |
|
164 this.model = model; |
|
165 int idx = fullName.indexOf('.'); |
|
166 if(idx<0) { |
|
167 this.parent = model.getPackage(cm.rootPackage()); |
|
168 this.shortName = model.allocator.assignClassName(parent,fullName); |
|
169 } else { |
|
170 this.parent = model.getPackage(cm._package(fullName.substring(0,idx))); |
|
171 this.shortName = model.allocator.assignClassName(parent,fullName.substring(idx+1)); |
|
172 } |
|
173 this.typeName = typeName; |
|
174 this.elementName = elementName; |
|
175 |
|
176 model.add(this); |
|
177 } |
|
178 |
|
179 public boolean hasAttributeWildcard() { |
|
180 return hasAttributeWildcard; |
|
181 } |
|
182 |
|
183 public void hasAttributeWildcard(boolean hasAttributeWildcard) { |
|
184 this.hasAttributeWildcard = hasAttributeWildcard; |
|
185 } |
|
186 |
|
187 public boolean hasSubClasses() { |
|
188 return firstSubclass!=null; |
|
189 } |
|
190 |
|
191 /** |
|
192 * Returns true if a new attribute wildcard property needs to be |
|
193 * declared on this class. |
|
194 */ |
|
195 public boolean declaresAttributeWildcard() { |
|
196 return hasAttributeWildcard && !inheritsAttributeWildcard(); |
|
197 } |
|
198 |
|
199 /** |
|
200 * Returns true if this class inherits a wildcard attribute property |
|
201 * from its ancestor classes. |
|
202 */ |
|
203 public boolean inheritsAttributeWildcard() { |
|
204 if (getRefBaseClass() != null) { |
|
205 CClassRef cref = (CClassRef)baseClass; |
|
206 if (cref.getSchemaComponent().getForeignAttributes().size() > 0) { |
|
207 return true; |
|
208 } |
|
209 } else { |
|
210 for( CClassInfo c=getBaseClass(); c!=null; c=c.getBaseClass() ) { |
|
211 if(c.hasAttributeWildcard) |
|
212 return true; |
|
213 } |
|
214 } |
|
215 return false; |
|
216 } |
|
217 |
|
218 |
|
219 public NClass getClazz() { |
|
220 return this; |
|
221 } |
|
222 |
|
223 public CClassInfo getScope() { |
|
224 return null; |
|
225 } |
|
226 |
|
227 @XmlID |
|
228 public String getName() { |
|
229 return fullName(); |
|
230 } |
|
231 |
|
232 /** |
|
233 * Returns the "squeezed name" of this bean token. |
|
234 * <p> |
|
235 * The squeezed name of a bean is the concatenation of |
|
236 * the names of its outer classes and itself. |
|
237 * <p> |
|
238 * Thus if the bean is "org.acme.foo.Bean", then the squeezed name is "Bean", |
|
239 * if the bean is "org.acme.foo.Outer1.Outer2.Bean", then "Outer1Outer2Bean". |
|
240 * <p> |
|
241 * This is used by the code generator |
|
242 */ |
|
243 @XmlElement |
|
244 public String getSqueezedName() { |
|
245 if (squeezedName != null) return squeezedName; |
|
246 return calcSqueezedName.onBean(this); |
|
247 } |
|
248 |
|
249 private static final CClassInfoParent.Visitor<String> calcSqueezedName = new Visitor<String>() { |
|
250 public String onBean(CClassInfo bean) { |
|
251 return bean.parent.accept(this)+bean.shortName; |
|
252 } |
|
253 |
|
254 public String onElement(CElementInfo element) { |
|
255 return element.parent.accept(this)+element.shortName(); |
|
256 } |
|
257 |
|
258 public String onPackage(JPackage pkg) { |
|
259 return ""; |
|
260 } |
|
261 }; |
|
262 |
|
263 /** |
|
264 * Returns a mutable list. |
|
265 */ |
|
266 public List<CPropertyInfo> getProperties() { |
|
267 return properties; |
|
268 } |
|
269 |
|
270 public boolean hasValueProperty() { |
|
271 throw new UnsupportedOperationException(); |
|
272 } |
|
273 |
|
274 /** |
|
275 * Gets a propery by name. |
|
276 */ |
|
277 public CPropertyInfo getProperty(String name) { |
|
278 // TODO: does this method need to be fast? |
|
279 for( CPropertyInfo p : properties ) |
|
280 if(p.getName(false).equals(name)) |
|
281 return p; |
|
282 return null; |
|
283 } |
|
284 |
|
285 public boolean hasProperties() { |
|
286 return !getProperties().isEmpty(); |
|
287 } |
|
288 |
|
289 public boolean isElement() { |
|
290 return elementName!=null; |
|
291 } |
|
292 |
|
293 /** |
|
294 * Guaranteed to return this. |
|
295 */ |
|
296 @Deprecated |
|
297 public CNonElement getInfo() { |
|
298 return this; |
|
299 } |
|
300 |
|
301 public Element<NType,NClass> asElement() { |
|
302 if(isElement()) |
|
303 return this; |
|
304 else |
|
305 return null; |
|
306 } |
|
307 |
|
308 public boolean isOrdered() { |
|
309 return isOrdered; |
|
310 } |
|
311 |
|
312 /** |
|
313 * @deprecated |
|
314 * if you are calling this method directly, you must be doing something wrong. |
|
315 */ |
|
316 public boolean isFinal() { |
|
317 return false; |
|
318 } |
|
319 |
|
320 public void setOrdered(boolean value) { |
|
321 isOrdered = value; |
|
322 } |
|
323 |
|
324 public QName getElementName() { |
|
325 return elementName; |
|
326 } |
|
327 |
|
328 public QName getTypeName() { |
|
329 return typeName; |
|
330 } |
|
331 |
|
332 public boolean isSimpleType() { |
|
333 throw new UnsupportedOperationException(); |
|
334 } |
|
335 |
|
336 /** |
|
337 * Returns the FQCN of this bean. |
|
338 */ |
|
339 public String fullName() { |
|
340 String r = parent.fullName(); |
|
341 if(r.length()==0) return shortName; |
|
342 else return r+'.'+shortName; |
|
343 } |
|
344 |
|
345 public CClassInfoParent parent() { |
|
346 return parent; |
|
347 } |
|
348 |
|
349 public void setUserSpecifiedImplClass(String implClass) { |
|
350 assert this.implClass==null; |
|
351 assert implClass!=null; |
|
352 this.implClass = implClass; |
|
353 } |
|
354 |
|
355 public String getUserSpecifiedImplClass() { |
|
356 return implClass; |
|
357 } |
|
358 |
|
359 |
|
360 /** |
|
361 * Adds a new property. |
|
362 */ |
|
363 public void addProperty(CPropertyInfo prop) { |
|
364 if(prop.ref().isEmpty()) |
|
365 // this property isn't contributing anything |
|
366 // this happens when you try to map an empty sequence to a property |
|
367 return; |
|
368 prop.setParent(this); |
|
369 properties.add(prop); |
|
370 } |
|
371 |
|
372 /** |
|
373 * This method accepts both {@link CClassInfo} (which means the base class |
|
374 * is also generated), or {@link CClassRef} (which means the base class is |
|
375 * already generated and simply referenced.) |
|
376 * |
|
377 * The latter is treated somewhat special --- from the rest of the model |
|
378 * this external base class is invisible. This modeling might need more |
|
379 * thoughts to get right. |
|
380 */ |
|
381 public void setBaseClass(CClass base) { |
|
382 assert baseClass==null; |
|
383 assert base!=null; |
|
384 baseClass = base; |
|
385 |
|
386 assert nextSibling==null; |
|
387 if (base instanceof CClassInfo) { |
|
388 CClassInfo realBase = (CClassInfo) base; |
|
389 this.nextSibling = realBase.firstSubclass; |
|
390 realBase.firstSubclass = this; |
|
391 } |
|
392 } |
|
393 |
|
394 /** |
|
395 * This inherited version returns null if this class extends from {@link CClassRef}. |
|
396 * |
|
397 * @see #getRefBaseClass() |
|
398 */ |
|
399 public CClassInfo getBaseClass() { |
|
400 if (baseClass instanceof CClassInfo) { |
|
401 return (CClassInfo) baseClass; |
|
402 } else { |
|
403 return null; |
|
404 } |
|
405 } |
|
406 |
|
407 public CClassRef getRefBaseClass() { |
|
408 if (baseClass instanceof CClassRef) { |
|
409 return (CClassRef) baseClass; |
|
410 } else { |
|
411 return null; |
|
412 } |
|
413 } |
|
414 |
|
415 /** |
|
416 * Enumerates all the sub-classes of this class. |
|
417 */ |
|
418 public Iterator<CClassInfo> listSubclasses() { |
|
419 return new Iterator<CClassInfo>() { |
|
420 CClassInfo cur = firstSubclass; |
|
421 public boolean hasNext() { |
|
422 return cur!=null; |
|
423 } |
|
424 |
|
425 public CClassInfo next() { |
|
426 CClassInfo r = cur; |
|
427 cur = cur.nextSibling; |
|
428 return r; |
|
429 } |
|
430 |
|
431 public void remove() { |
|
432 throw new UnsupportedOperationException(); |
|
433 } |
|
434 }; |
|
435 } |
|
436 |
|
437 public CClassInfo getSubstitutionHead() { |
|
438 CClassInfo c=getBaseClass(); |
|
439 while(c!=null && !c.isElement()) |
|
440 c=c.getBaseClass(); |
|
441 return c; |
|
442 } |
|
443 |
|
444 |
|
445 /** |
|
446 * Interfaces to be implemented. |
|
447 * Lazily constructed. |
|
448 */ |
|
449 private Set<JClass> _implements = null; |
|
450 |
|
451 public void _implements(JClass c) { |
|
452 if(_implements==null) |
|
453 _implements = new HashSet<JClass>(); |
|
454 _implements.add(c); |
|
455 } |
|
456 |
|
457 |
|
458 /** Constructor declarations. array of {@link Constructor}s. */ |
|
459 private final List<Constructor> constructors = new ArrayList<Constructor>(1); |
|
460 |
|
461 /** Creates a new constructor declaration and adds it. */ |
|
462 public void addConstructor( String... fieldNames ) { |
|
463 constructors.add(new Constructor(fieldNames)); |
|
464 } |
|
465 |
|
466 /** list all constructor declarations. */ |
|
467 public Collection<? extends Constructor> getConstructors() { |
|
468 return constructors; |
|
469 } |
|
470 |
|
471 public final <T> T accept(Visitor<T> visitor) { |
|
472 return visitor.onBean(this); |
|
473 } |
|
474 |
|
475 public JPackage getOwnerPackage() { |
|
476 return parent.getOwnerPackage(); |
|
477 } |
|
478 |
|
479 public final NClass getType() { |
|
480 return this; |
|
481 } |
|
482 |
|
483 public final JClass toType(Outline o, Aspect aspect) { |
|
484 switch(aspect) { |
|
485 case IMPLEMENTATION: |
|
486 return o.getClazz(this).implRef; |
|
487 case EXPOSED: |
|
488 return o.getClazz(this).ref; |
|
489 default: |
|
490 throw new IllegalStateException(); |
|
491 } |
|
492 } |
|
493 |
|
494 public boolean isBoxedType() { |
|
495 return false; |
|
496 } |
|
497 |
|
498 public String toString() { |
|
499 return fullName(); |
|
500 } |
|
501 } |