src/share/jaxws_classes/com/sun/tools/internal/xjc/reader/xmlschema/BGMBuilder.java

changeset 286
f50545b5e2f1
child 368
0989ad8c0860
equal deleted inserted replaced
284:88b85470e72c 286:f50545b5e2f1
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.ArrayList;
29 import java.util.HashMap;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.Set;
33
34 import javax.xml.namespace.QName;
35 import javax.xml.transform.Transformer;
36 import javax.xml.transform.TransformerConfigurationException;
37 import javax.xml.transform.TransformerFactory;
38
39 import com.sun.codemodel.internal.JCodeModel;
40 import com.sun.codemodel.internal.fmt.JTextFile;
41 import com.sun.istack.internal.NotNull;
42 import com.sun.istack.internal.Nullable;
43 import com.sun.tools.internal.xjc.ErrorReceiver;
44 import com.sun.tools.internal.xjc.Options;
45 import com.sun.tools.internal.xjc.Plugin;
46 import com.sun.tools.internal.xjc.generator.bean.field.FieldRendererFactory;
47 import com.sun.tools.internal.xjc.model.CClassInfoParent;
48 import com.sun.tools.internal.xjc.model.Model;
49 import com.sun.tools.internal.xjc.reader.ModelChecker;
50 import com.sun.tools.internal.xjc.reader.Ring;
51 import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIDeclaration;
52 import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIDom;
53 import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIGlobalBinding;
54 import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BISchemaBinding;
55 import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BISerializable;
56 import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BindInfo;
57 import com.sun.tools.internal.xjc.util.CodeModelClassFactory;
58 import com.sun.tools.internal.xjc.util.ErrorReceiverFilter;
59 import com.sun.xml.internal.bind.api.impl.NameConverter;
60 import com.sun.xml.internal.xsom.XSAnnotation;
61 import com.sun.xml.internal.xsom.XSAttributeUse;
62 import com.sun.xml.internal.xsom.XSComponent;
63 import com.sun.xml.internal.xsom.XSDeclaration;
64 import com.sun.xml.internal.xsom.XSParticle;
65 import com.sun.xml.internal.xsom.XSSchema;
66 import com.sun.xml.internal.xsom.XSSchemaSet;
67 import com.sun.xml.internal.xsom.XSSimpleType;
68 import com.sun.xml.internal.xsom.XSTerm;
69 import com.sun.xml.internal.xsom.XSType;
70 import com.sun.xml.internal.xsom.XSWildcard;
71 import com.sun.xml.internal.xsom.util.XSFinder;
72
73 import org.xml.sax.Locator;
74
75 /**
76 * Root of the XML Schema binder.
77 *
78 * <div><img src="doc-files/binding_chart.png"/></div>
79 *
80 * @author Kohsuke Kawaguchi
81 */
82 public class BGMBuilder extends BindingComponent {
83
84 /**
85 * Entry point.
86 */
87 public static Model build( XSSchemaSet _schemas, JCodeModel codeModel,
88 ErrorReceiver _errorReceiver, Options opts ) {
89 // set up a ring
90 final Ring old = Ring.begin();
91 try {
92 ErrorReceiverFilter ef = new ErrorReceiverFilter(_errorReceiver);
93
94 Ring.add(XSSchemaSet.class,_schemas);
95 Ring.add(codeModel);
96 Model model = new Model(opts, codeModel, null/*set later*/, opts.classNameAllocator, _schemas);
97 Ring.add(model);
98 Ring.add(ErrorReceiver.class,ef);
99 Ring.add(CodeModelClassFactory.class,new CodeModelClassFactory(ef));
100
101 BGMBuilder builder = new BGMBuilder(opts.defaultPackage,opts.defaultPackage2,
102 opts.isExtensionMode(),opts.getFieldRendererFactory(), opts.activePlugins);
103 builder._build();
104
105 if(ef.hadError()) return null;
106 else return model;
107 } finally {
108 Ring.end(old);
109 }
110 }
111
112
113 /**
114 * True if the compiler is running in the extension mode
115 * (as opposed to the strict conformance mode.)
116 */
117 public final boolean inExtensionMode;
118
119 /**
120 * If this is non-null, this package name takes over
121 * all the schema customizations.
122 */
123 public final String defaultPackage1;
124
125 /**
126 * If this is non-null, this package name will be
127 * used when no customization is specified.
128 */
129 public final String defaultPackage2;
130
131 private final BindGreen green = Ring.get(BindGreen.class);
132 private final BindPurple purple = Ring.get(BindPurple.class);
133
134 public final Model model = Ring.get(Model.class);
135
136 public final FieldRendererFactory fieldRendererFactory;
137
138 /**
139 * Lazily computed {@link RefererFinder}.
140 *
141 * @see #getReferer
142 */
143 private RefererFinder refFinder;
144
145 private List<Plugin> activePlugins;
146
147 protected BGMBuilder(String defaultPackage1, String defaultPackage2,
148 boolean _inExtensionMode, FieldRendererFactory fieldRendererFactory,
149 List<Plugin> activePlugins) {
150 this.inExtensionMode = _inExtensionMode;
151 this.defaultPackage1 = defaultPackage1;
152 this.defaultPackage2 = defaultPackage2;
153 this.fieldRendererFactory = fieldRendererFactory;
154 this.activePlugins = activePlugins;
155 promoteGlobalBindings();
156 }
157
158 private void _build() {
159 // do the binding
160 buildContents();
161 getClassSelector().executeTasks();
162
163 // additional error check
164 // Reports unused customizations to the user as errors.
165 Ring.get(UnusedCustomizationChecker.class).run();
166
167 Ring.get(ModelChecker.class).check();
168
169 for( Plugin ma : activePlugins )
170 ma.postProcessModel(model, Ring.get(ErrorReceiver.class));
171
172 }
173
174
175 /** List up all the global bindings. */
176 private void promoteGlobalBindings() {
177 // promote any global bindings in the schema
178 XSSchemaSet schemas = Ring.get(XSSchemaSet.class);
179
180 for( XSSchema s : schemas.getSchemas() ) {
181 BindInfo bi = getBindInfo(s);
182
183 // collect all global customizations
184 model.getCustomizations().addAll(bi.toCustomizationList());
185
186 BIGlobalBinding gb = bi.get(BIGlobalBinding.class);
187 if(gb==null)
188 continue;
189
190 gb.markAsAcknowledged();
191
192 if(globalBinding==null) {
193 globalBinding = gb;
194 } else {
195 if (!globalBinding.isEqual(gb)) { // see Issue 687 - this may happen with syntactically imported documents
196 // acknowledge this customization and report an error
197 // otherwise the user will see "customization is attached to a wrong place" error,
198 // which is incorrect
199 getErrorReporter().error( gb.getLocation(),
200 Messages.ERR_MULTIPLE_GLOBAL_BINDINGS);
201 getErrorReporter().error( globalBinding.getLocation(),
202 Messages.ERR_MULTIPLE_GLOBAL_BINDINGS_OTHER);
203 }
204 }
205 }
206
207 if( globalBinding==null ) {
208 // no global customization is present.
209 // use the default one
210 globalBinding = new BIGlobalBinding();
211 BindInfo big = new BindInfo();
212 big.addDecl(globalBinding);
213 big.setOwner(this,null);
214 }
215
216 // code generation mode
217 model.strategy = globalBinding.getCodeGenerationStrategy();
218 model.rootClass = globalBinding.getSuperClass();
219 model.rootInterface = globalBinding.getSuperInterface();
220
221 particleBinder = globalBinding.isSimpleMode() ? new ExpressionParticleBinder() : new DefaultParticleBinder();
222
223 // check XJC extensions and realize them
224 BISerializable serial = globalBinding.getSerializable();
225 if(serial!=null) {
226 model.serializable = true;
227 model.serialVersionUID = serial.uid;
228 }
229
230 // obtain the name conversion mode
231 if(globalBinding.nameConverter!=null)
232 model.setNameConverter(globalBinding.nameConverter);
233
234 // attach global conversions to the appropriate simple types
235 globalBinding.dispatchGlobalConversions(schemas);
236
237 globalBinding.errorCheck();
238 }
239
240 /**
241 * Global bindings.
242 *
243 * The empty global binding is set as the default, so that
244 * there will be no need to test if the value is null.
245 */
246 private BIGlobalBinding globalBinding;
247
248 /**
249 * Gets the global bindings.
250 */
251 public @NotNull BIGlobalBinding getGlobalBinding() { return globalBinding; }
252
253
254 private ParticleBinder particleBinder;
255
256 /**
257 * Gets the particle binder for this binding.
258 */
259 public @NotNull ParticleBinder getParticleBinder() { return particleBinder; }
260
261
262 /**
263 * Name converter that implements "XML->Java name conversion"
264 * as specified in the spec.
265 *
266 * This object abstracts the detail that we use different name
267 * conversion depending on the customization.
268 *
269 * <p>
270 * This object should be used to perform any name conversion
271 * needs, instead of the JJavaName class in CodeModel.
272 */
273 public NameConverter getNameConverter() { return model.getNameConverter(); }
274
275 /** Fill-in the contents of each classes. */
276 private void buildContents() {
277 ClassSelector cs = getClassSelector();
278 SimpleTypeBuilder stb = Ring.get(SimpleTypeBuilder.class);
279
280 for( XSSchema s : Ring.get(XSSchemaSet.class).getSchemas() ) {
281 BISchemaBinding sb = getBindInfo(s).get(BISchemaBinding.class);
282
283 if(sb!=null && !sb.map) {
284 sb.markAsAcknowledged();
285 continue; // no mapping for this package
286 }
287
288 getClassSelector().pushClassScope( new CClassInfoParent.Package(
289 getClassSelector().getPackage(s.getTargetNamespace())) );
290
291 checkMultipleSchemaBindings(s);
292 processPackageJavadoc(s);
293 populate(s.getAttGroupDecls(),s);
294 populate(s.getAttributeDecls(),s);
295 populate(s.getElementDecls(),s);
296 populate(s.getModelGroupDecls(),s);
297
298 // fill in typeUses
299 for (XSType t : s.getTypes().values()) {
300 stb.refererStack.push(t);
301 model.typeUses().put( getName(t), cs.bindToType(t,s) );
302 stb.refererStack.pop();
303 }
304
305 getClassSelector().popClassScope();
306 }
307 }
308
309 /** Reports an error if there are more than one jaxb:schemaBindings customization. */
310 private void checkMultipleSchemaBindings( XSSchema schema ) {
311 ArrayList<Locator> locations = new ArrayList<Locator>();
312
313 BindInfo bi = getBindInfo(schema);
314 for( BIDeclaration bid : bi ) {
315 if( bid.getName()==BISchemaBinding.NAME )
316 locations.add( bid.getLocation() );
317 }
318 if(locations.size()<=1) return; // OK
319
320 // error
321 getErrorReporter().error( locations.get(0),
322 Messages.ERR_MULTIPLE_SCHEMA_BINDINGS,
323 schema.getTargetNamespace() );
324 for( int i=1; i<locations.size(); i++ )
325 getErrorReporter().error( (Locator)locations.get(i),
326 Messages.ERR_MULTIPLE_SCHEMA_BINDINGS_LOCATION);
327 }
328
329 /**
330 * Calls {@link ClassSelector} for each item in the iterator
331 * to populate class items if there is any.
332 */
333 private void populate( Map<String,? extends XSComponent> col, XSSchema schema ) {
334 ClassSelector cs = getClassSelector();
335 for( XSComponent sc : col.values() )
336 cs.bindToType(sc,schema);
337 }
338
339 /**
340 * Generates <code>package.html</code> if the customization
341 * says so.
342 */
343 private void processPackageJavadoc( XSSchema s ) {
344 // look for the schema-wide customization
345 BISchemaBinding cust = getBindInfo(s).get(BISchemaBinding.class);
346 if(cust==null) return; // not present
347
348 cust.markAsAcknowledged();
349 if( cust.getJavadoc()==null ) return; // no javadoc customization
350
351 // produce a HTML file
352 JTextFile html = new JTextFile("package.html");
353 html.setContents(cust.getJavadoc());
354 getClassSelector().getPackage(s.getTargetNamespace()).addResourceFile(html);
355 }
356
357
358
359
360
361
362 /**
363 * Gets or creates the BindInfo object associated to a schema component.
364 *
365 * @return
366 * Always return a non-null valid BindInfo object.
367 * Even if no declaration was specified, this method creates
368 * a new BindInfo so that new decls can be added.
369 */
370 public BindInfo getOrCreateBindInfo( XSComponent schemaComponent ) {
371
372 BindInfo bi = _getBindInfoReadOnly(schemaComponent);
373 if(bi!=null) return bi;
374
375 // XSOM is read-only, so we cannot add new annotations.
376 // for components that didn't have annotations,
377 // we maintain an external map.
378 bi = new BindInfo();
379 bi.setOwner(this,schemaComponent);
380 externalBindInfos.put(schemaComponent,bi);
381 return bi;
382 }
383
384
385 /**
386 * Used as a constant instance to represent the empty {@link BindInfo}.
387 */
388 private final BindInfo emptyBindInfo = new BindInfo();
389
390 /**
391 * Gets the BindInfo object associated to a schema component.
392 *
393 * @return
394 * always return a valid {@link BindInfo} object. If none
395 * is specified for the given component, a dummy empty BindInfo
396 * will be returned.
397 */
398 public BindInfo getBindInfo( XSComponent schemaComponent ) {
399 BindInfo bi = _getBindInfoReadOnly(schemaComponent);
400 if(bi!=null) return bi;
401 else return emptyBindInfo;
402 }
403
404 /**
405 * Gets the BindInfo object associated to a schema component.
406 *
407 * @return
408 * null if no bind info is associated to this schema component.
409 */
410 private BindInfo _getBindInfoReadOnly( XSComponent schemaComponent ) {
411
412 BindInfo bi = externalBindInfos.get(schemaComponent);
413 if(bi!=null) return bi;
414
415 XSAnnotation annon = schemaComponent.getAnnotation();
416 if(annon!=null) {
417 bi = (BindInfo)annon.getAnnotation();
418 if(bi!=null) {
419 if(bi.getOwner()==null)
420 bi.setOwner(this,schemaComponent);
421 return bi;
422 }
423 }
424
425 return null;
426 }
427
428 /**
429 * A map that stores binding declarations augmented by XJC.
430 */
431 private final Map<XSComponent,BindInfo> externalBindInfos = new HashMap<XSComponent,BindInfo>();
432
433 /**
434 * Gets the {@link BIDom} object that applies to the given particle.
435 */
436 protected final BIDom getLocalDomCustomization( XSParticle p ) {
437 if (p == null) {
438 return null;
439 }
440 BIDom dom = getBindInfo(p).get(BIDom.class);
441 if(dom!=null) return dom;
442
443 // if not, the term might have one.
444 dom = getBindInfo(p.getTerm()).get(BIDom.class);
445 if(dom!=null) return dom;
446
447 XSTerm t = p.getTerm();
448 // type could also have one, in case of the dom customization
449 if(t.isElementDecl())
450 return getBindInfo(t.asElementDecl().getType()).get(BIDom.class);
451 // similarly the model group in a model group definition may have one.
452 if(t.isModelGroupDecl())
453 return getBindInfo(t.asModelGroupDecl().getModelGroup()).get(BIDom.class);
454
455 return null;
456 }
457
458 /**
459 * Returns true if the component should be processed by purple.
460 */
461 private final XSFinder toPurple = new XSFinder() {
462 @Override
463 public Boolean attributeUse(XSAttributeUse use) {
464 // attribute use always maps to a property
465 return true;
466 }
467
468 @Override
469 public Boolean simpleType(XSSimpleType xsSimpleType) {
470 // simple type always maps to a type, hence we should take purple
471 return true;
472 }
473
474 @Override
475 public Boolean wildcard(XSWildcard xsWildcard) {
476 // attribute wildcards always maps to a property.
477 // element wildcards should have been processed with particle binders
478 return true;
479 }
480 };
481 /**
482 * If the component maps to a property, forwards to purple, otherwise to green.
483 *
484 * If the component is mapped to a type, this method needs to return true.
485 * See the chart at the class javadoc.
486 */
487 public void ying( XSComponent sc, @Nullable XSComponent referer ) {
488 if(sc.apply(toPurple)==true || getClassSelector().bindToType(sc,referer)!=null)
489 sc.visit(purple);
490 else
491 sc.visit(green);
492 }
493
494 private Transformer identityTransformer;
495
496 /**
497 * Gets the shared instance of the identity transformer.
498 */
499 public Transformer getIdentityTransformer() {
500 try {
501 if(identityTransformer==null)
502 identityTransformer = TransformerFactory.newInstance().newTransformer();
503 return identityTransformer;
504 } catch (TransformerConfigurationException e) {
505 throw new Error(e); // impossible
506 }
507 }
508
509 /**
510 * Find all types that refer to the given complex type.
511 */
512 public Set<XSComponent> getReferer(XSType c) {
513 if(refFinder==null) {
514 refFinder = new RefererFinder();
515 refFinder.schemaSet(Ring.get(XSSchemaSet.class));
516 }
517 return refFinder.getReferer(c);
518 }
519
520 /**
521 * Returns the QName of the declaration.
522 * @return null
523 * if the declaration is anonymous.
524 */
525 public static QName getName(XSDeclaration decl) {
526 String local = decl.getName();
527 if(local==null) return null;
528 return new QName(decl.getTargetNamespace(),local);
529 }
530
531 /**
532 * Derives a name from a schema component.
533 *
534 * This method handles prefix/suffix modification and
535 * XML-to-Java name conversion.
536 *
537 * @param name
538 * The base name. This should be things like element names
539 * or type names.
540 * @param comp
541 * The component from which the base name was taken.
542 * Used to determine how names are modified.
543 */
544 public String deriveName( String name, XSComponent comp ) {
545 XSSchema owner = comp.getOwnerSchema();
546
547 name = getNameConverter().toClassName(name);
548
549 if( owner!=null ) {
550 BISchemaBinding sb = getBindInfo(owner).get(BISchemaBinding.class);
551
552 if(sb!=null) name = sb.mangleClassName(name,comp);
553 }
554
555 return name;
556 }
557
558 public boolean isGenerateMixedExtensions() {
559 if (globalBinding != null) {
560 return globalBinding.isGenerateMixedExtensions();
561 }
562 return false;
563 }
564
565 }

mercurial