src/share/jaxws_classes/com/sun/tools/internal/xjc/reader/xmlschema/SimpleTypeBuilder.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.io.StringWriter;
29 import java.math.BigInteger;
30 import java.text.ParseException;
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 import java.util.Collections;
34 import java.util.HashMap;
35 import java.util.HashSet;
36 import java.util.List;
37 import java.util.Map;
38 import java.util.Set;
39 import java.util.Stack;
40
41 import javax.activation.MimeTypeParseException;
42
43 import com.sun.codemodel.internal.JJavaName;
44 import com.sun.codemodel.internal.util.JavadocEscapeWriter;
45 import com.sun.tools.internal.xjc.ErrorReceiver;
46 import com.sun.tools.internal.xjc.model.CBuiltinLeafInfo;
47 import com.sun.tools.internal.xjc.model.CClassInfo;
48 import com.sun.tools.internal.xjc.model.CClassInfoParent;
49 import com.sun.tools.internal.xjc.model.CClassRef;
50 import com.sun.tools.internal.xjc.model.CEnumConstant;
51 import com.sun.tools.internal.xjc.model.CEnumLeafInfo;
52 import com.sun.tools.internal.xjc.model.CNonElement;
53 import com.sun.tools.internal.xjc.model.Model;
54 import com.sun.tools.internal.xjc.model.TypeUse;
55 import com.sun.tools.internal.xjc.model.TypeUseFactory;
56 import com.sun.tools.internal.xjc.reader.Const;
57 import com.sun.tools.internal.xjc.reader.Ring;
58 import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIConversion;
59 import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIEnum;
60 import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIEnumMember;
61 import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIProperty;
62 import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BindInfo;
63 import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.EnumMemberMode;
64 import com.sun.tools.internal.xjc.util.MimeTypeRange;
65 import com.sun.xml.internal.bind.DatatypeConverterImpl;
66 import com.sun.xml.internal.bind.v2.WellKnownNamespace;
67 import static com.sun.xml.internal.bind.v2.WellKnownNamespace.XML_MIME_URI;
68 import com.sun.xml.internal.bind.v2.runtime.SwaRefAdapter;
69 import com.sun.xml.internal.xsom.XSAttributeDecl;
70 import com.sun.xml.internal.xsom.XSComplexType;
71 import com.sun.xml.internal.xsom.XSComponent;
72 import com.sun.xml.internal.xsom.XSElementDecl;
73 import com.sun.xml.internal.xsom.XSFacet;
74 import com.sun.xml.internal.xsom.XSListSimpleType;
75 import com.sun.xml.internal.xsom.XSRestrictionSimpleType;
76 import com.sun.xml.internal.xsom.XSSimpleType;
77 import com.sun.xml.internal.xsom.XSUnionSimpleType;
78 import com.sun.xml.internal.xsom.XSVariety;
79 import com.sun.xml.internal.xsom.impl.util.SchemaWriter;
80 import com.sun.xml.internal.xsom.visitor.XSSimpleTypeFunction;
81 import com.sun.xml.internal.xsom.visitor.XSVisitor;
82
83 import org.xml.sax.Locator;
84
85 /**
86 * Builds {@link TypeUse} from simple types.
87 *
88 * <p>
89 * This code consists of two main portions. The {@link #compose(XSSimpleType)} method
90 * and {@link #composer} forms an outer cycle, which gradually ascends the type
91 * inheritance chain until it finds the suitable binding. When it does this
92 * {@link #initiatingType} is set to the type which started binding, so that we can refer
93 * to the actual constraint facets and such that are applicable on the type.
94 *
95 * <p>
96 * For each intermediate type in the chain, the {@link #find(XSSimpleType)} method
97 * is used to find the binding on that type, sine the outer loop is doing the ascending,
98 * this method only sees if the current type has some binding available.
99 *
100 * <p>
101 * There is at least one ugly code that you need to aware of
102 * when you are modifying the code. See the documentation
103 * about <a href="package.html#stref_cust">
104 * "simple type customization at the point of reference."</a>
105 *
106 *
107 * @author
108 * Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com)
109 */
110 public final class SimpleTypeBuilder extends BindingComponent {
111
112 protected final BGMBuilder builder = Ring.get(BGMBuilder.class);
113
114 private final Model model = Ring.get(Model.class);
115
116 /**
117 * The component that is refering to the simple type
118 * which we are building. This is ugly but necessary
119 * to support the customization of simple types at
120 * its point of reference. See my comment at the header
121 * of this class for details.
122 *
123 * UGLY: Implemented as a Stack of XSComponent to fix a bug
124 */
125 public final Stack<XSComponent> refererStack = new Stack<XSComponent>();
126
127 /**
128 * Records what xmime:expectedContentTypes annotations we honored and processed,
129 * so that we can later check if the user had these annotations in the places
130 * where we didn't anticipate them.
131 */
132 private final Set<XSComponent> acknowledgedXmimeContentTypes = new HashSet<XSComponent>();
133
134 /**
135 * The type that was originally passed to this {@link SimpleTypeBuilder#build(XSSimpleType)}.
136 * Never null.
137 */
138 private XSSimpleType initiatingType;
139
140 /** {@link TypeUse}s for the built-in types. Read-only. */
141 public static final Map<String,TypeUse> builtinConversions = new HashMap<String,TypeUse>();
142
143
144 /**
145 * Entry point from outside. Builds a BGM type expression
146 * from a simple type schema component.
147 *
148 * @param type
149 * the simple type to be bound.
150 */
151 public TypeUse build( XSSimpleType type ) {
152 XSSimpleType oldi = initiatingType;
153 this.initiatingType = type;
154
155 TypeUse e = checkRefererCustomization(type);
156 if(e==null)
157 e = compose(type);
158
159 initiatingType = oldi;
160
161 return e;
162 }
163
164 /**
165 * A version of the {@link #build(XSSimpleType)} method
166 * used to bind the definition of a class generated from
167 * the given simple type.
168 */
169 public TypeUse buildDef( XSSimpleType type ) {
170 XSSimpleType oldi = initiatingType;
171 this.initiatingType = type;
172
173 TypeUse e = type.apply(composer);
174
175 initiatingType = oldi;
176
177 return e;
178 }
179
180
181 /**
182 * Returns a javaType customization specified to the referer, if present.
183 * @return can be null.
184 */
185 private BIConversion getRefererCustomization() {
186 BindInfo info = builder.getBindInfo(getReferer());
187 BIProperty prop = info.get(BIProperty.class);
188 if(prop==null) return null;
189 return prop.getConv();
190 }
191
192 public XSComponent getReferer() {
193 return refererStack.peek();
194 }
195
196 /**
197 * Checks if the referer has a conversion customization or not.
198 * If it does, use it to bind this simple type. Otherwise
199 * return null;
200 */
201 private TypeUse checkRefererCustomization( XSSimpleType type ) {
202
203 // assertion check. referer must be set properly
204 // before the build method is called.
205 // since the handling of the simple type point-of-reference
206 // customization is very error prone, it deserves a strict
207 // assertion check.
208 // UGLY CODE WARNING
209 XSComponent top = getReferer();
210
211 if( top instanceof XSElementDecl ) {
212 // if the parent is element type, its content type must be us.
213 XSElementDecl eref = (XSElementDecl)top;
214 assert eref.getType()==type;
215
216 // for elements, you can't use <property>,
217 // so we allow javaType to appear directly.
218 BindInfo info = builder.getBindInfo(top);
219 BIConversion conv = info.get(BIConversion.class);
220 if(conv!=null) {
221 conv.markAsAcknowledged();
222 // the conversion is given.
223 return conv.getTypeUse(type);
224 }
225 detectJavaTypeCustomization();
226 } else
227 if( top instanceof XSAttributeDecl ) {
228 XSAttributeDecl aref = (XSAttributeDecl)top;
229 assert aref.getType()==type;
230 detectJavaTypeCustomization();
231 } else
232 if( top instanceof XSComplexType ) {
233 XSComplexType tref = (XSComplexType)top;
234 assert tref.getBaseType()==type || tref.getContentType()==type;
235 detectJavaTypeCustomization();
236 } else
237 if( top == type ) {
238 // this means the simple type is built by itself and
239 // not because it's referenced by something.
240 } else
241 // unexpected referer type.
242 assert false;
243
244 // now we are certain that the referer is OK.
245 // see if it has a conversion customization.
246 BIConversion conv = getRefererCustomization();
247 if(conv!=null) {
248 conv.markAsAcknowledged();
249 // the conversion is given.
250 return conv.getTypeUse(type);
251 } else
252 // not found
253 return null;
254 }
255
256 /**
257 * Detect "javaType" customizations placed directly on simple types, rather
258 * than being enclosed by "property" and "baseType" customizations (see
259 * sec 6.8.1 of the spec).
260 *
261 * Report an error if any exist.
262 */
263 private void detectJavaTypeCustomization() {
264 BindInfo info = builder.getBindInfo(getReferer());
265 BIConversion conv = info.get(BIConversion.class);
266
267 if( conv != null ) {
268 // ack this conversion to prevent further error messages
269 conv.markAsAcknowledged();
270
271 // report the error
272 getErrorReporter().error( conv.getLocation(),
273 Messages.ERR_UNNESTED_JAVATYPE_CUSTOMIZATION_ON_SIMPLETYPE );
274 }
275 }
276
277 /**
278 * Recursively decend the type inheritance chain to find a binding.
279 */
280 TypeUse compose( XSSimpleType t ) {
281 TypeUse e = find(t);
282 if(e!=null) return e;
283 return t.apply(composer);
284 }
285
286 public final XSSimpleTypeFunction<TypeUse> composer = new XSSimpleTypeFunction<TypeUse>() {
287
288 public TypeUse listSimpleType(XSListSimpleType type) {
289 // bind item type individually and then compose them into a list
290 // facets on the list shouldn't be taken account when binding item types,
291 // so weed to call build(), not compose().
292 XSSimpleType itemType = type.getItemType();
293 refererStack.push(itemType);
294 TypeUse tu = TypeUseFactory.makeCollection(build(type.getItemType()));
295 refererStack.pop();
296 return tu;
297 }
298
299 public TypeUse unionSimpleType(XSUnionSimpleType type) {
300 boolean isCollection = false;
301 for( int i=0; i<type.getMemberSize(); i++ )
302 if(type.getMember(i).getVariety()==XSVariety.LIST || type.getMember(i).getVariety()==XSVariety.UNION) {
303 isCollection = true;
304 break;
305 }
306
307 TypeUse r = CBuiltinLeafInfo.STRING;
308 if(isCollection)
309 r = TypeUseFactory.makeCollection(r);
310 return r;
311 }
312
313 public TypeUse restrictionSimpleType(XSRestrictionSimpleType type) {
314 // just process the base type.
315 return compose(type.getSimpleBaseType());
316 }
317 };
318
319
320 /**
321 * Checks if there's any binding available on the given type.
322 *
323 * @return
324 * null if not (which causes the {@link #compose(XSSimpleType)} method
325 * to do ascending.
326 */
327 private TypeUse find( XSSimpleType type ) {
328 TypeUse r;
329 boolean noAutoEnum = false;
330
331 // check for user specified conversion
332 BindInfo info = builder.getBindInfo(type);
333 BIConversion conv = info.get(BIConversion.class);
334
335 if( conv!=null ) {
336 // a conversion was found
337 conv.markAsAcknowledged();
338 return conv.getTypeUse(type);
339 }
340
341 // look for enum customization, which is another user specified conversion
342 BIEnum en = info.get(BIEnum.class);
343 if( en!=null ) {
344 en.markAsAcknowledged();
345
346 if(!en.isMapped()) {
347 noAutoEnum = true;
348 } else {
349 // if an enum customization is specified, make sure
350 // the type is OK
351 if( !canBeMappedToTypeSafeEnum(type) ) {
352 getErrorReporter().error( en.getLocation(),
353 Messages.ERR_CANNOT_BE_TYPE_SAFE_ENUM );
354 getErrorReporter().error( type.getLocator(),
355 Messages.ERR_CANNOT_BE_TYPE_SAFE_ENUM_LOCATION );
356 // recover by ignoring this customization
357 return null;
358 }
359
360 // reference?
361 if(en.ref!=null) {
362 if(!JJavaName.isFullyQualifiedClassName(en.ref)) {
363 Ring.get(ErrorReceiver.class).error( en.getLocation(),
364 Messages.format(Messages.ERR_INCORRECT_CLASS_NAME, en.ref) );
365 // recover by ignoring @ref
366 return null;
367 }
368
369 return new CClassRef(model, type, en, info.toCustomizationList() );
370 }
371
372 // list and union cannot be mapped to a type-safe enum,
373 // so in this stage we can safely cast it to XSRestrictionSimpleType
374 return bindToTypeSafeEnum( (XSRestrictionSimpleType)type,
375 en.className, en.javadoc, en.members,
376 getEnumMemberMode().getModeWithEnum(),
377 en.getLocation() );
378 }
379 }
380
381
382 // if the type is built in, look for the default binding
383 if(type.getTargetNamespace().equals(WellKnownNamespace.XML_SCHEMA)) {
384 String name = type.getName();
385 if(name!=null) {
386 r = lookupBuiltin(name);
387 if(r!=null)
388 return r;
389 }
390 }
391
392 // also check for swaRef
393 if(type.getTargetNamespace().equals(WellKnownNamespace.SWA_URI)) {
394 String name = type.getName();
395 if(name!=null && name.equals("swaRef"))
396 return CBuiltinLeafInfo.STRING.makeAdapted(SwaRefAdapter.class,false);
397 }
398
399
400 // see if this type should be mapped to a type-safe enumeration by default.
401 // if so, built a EnumXDucer from it and return it.
402 if(type.isRestriction() && !noAutoEnum) {
403 XSRestrictionSimpleType rst = type.asRestriction();
404 if(shouldBeMappedToTypeSafeEnumByDefault(rst)) {
405 r = bindToTypeSafeEnum(rst,null,null, Collections.<String, BIEnumMember>emptyMap(),
406 getEnumMemberMode(),null);
407 if(r!=null)
408 return r;
409 }
410 }
411
412 return (CNonElement)getClassSelector()._bindToClass(type,null,false);
413 }
414
415 private static Set<XSRestrictionSimpleType> reportedEnumMemberSizeWarnings;
416
417 /**
418 * Returns true if a type-safe enum should be created from
419 * the given simple type by default without an explicit &lt;jaxb:enum> customization.
420 */
421 private boolean shouldBeMappedToTypeSafeEnumByDefault( XSRestrictionSimpleType type ) {
422
423 // if not, there will be a problem wrt the class name of this type safe enum type.
424 if( type.isLocal() ) return false;
425
426 // if redefined, we should map the new definition, not the old one.
427 if( type.getRedefinedBy()!=null ) return false;
428
429 List<XSFacet> facets = type.getDeclaredFacets(XSFacet.FACET_ENUMERATION);
430 if( facets.isEmpty() )
431 // if the type itself doesn't have the enumeration facet,
432 // it won't be mapped to a type-safe enum.
433 return false;
434
435 if(facets.size() > builder.getGlobalBinding().getDefaultEnumMemberSizeCap()) {
436 // if there are too many facets, it's not very useful
437 // produce warning when simple type is not mapped to enum
438 // see issue https://jaxb.dev.java.net/issues/show_bug.cgi?id=711
439
440 if(reportedEnumMemberSizeWarnings == null)
441 reportedEnumMemberSizeWarnings = new HashSet<XSRestrictionSimpleType>();
442
443 if(!reportedEnumMemberSizeWarnings.contains(type)) {
444 getErrorReporter().warning(type.getLocator(), Messages.WARN_ENUM_MEMBER_SIZE_CAP,
445 type.getName(), facets.size(), builder.getGlobalBinding().getDefaultEnumMemberSizeCap());
446
447 reportedEnumMemberSizeWarnings.add(type);
448 }
449
450 return false;
451 }
452
453 if( !canBeMappedToTypeSafeEnum(type) )
454 // we simply can't map this to an enumeration
455 return false;
456
457 // check for collisions among constant names. if a collision will happen,
458 // don't try to bind it to an enum.
459
460 // return true only when this type is derived from one of the "enum base type".
461 for( XSSimpleType t = type; t!=null; t=t.getSimpleBaseType() )
462 if( t.isGlobal() && builder.getGlobalBinding().canBeMappedToTypeSafeEnum(t) )
463 return true;
464
465 return false;
466 }
467
468
469 private static final Set<String> builtinTypeSafeEnumCapableTypes;
470
471 static {
472 Set<String> s = new HashSet<String>();
473
474 // see a bullet of 6.5.1 of the spec.
475 String[] typeNames = new String[] {
476 "string", "boolean", "float", "decimal", "double", "anyURI"
477 };
478 s.addAll(Arrays.asList(typeNames));
479
480 builtinTypeSafeEnumCapableTypes = Collections.unmodifiableSet(s);
481 }
482
483
484 /**
485 * Returns true if the given simple type can be mapped to a
486 * type-safe enum class.
487 *
488 * <p>
489 * JAXB spec places a restrictrion as to what type can be
490 * mapped to a type-safe enum. This method enforces this
491 * constraint.
492 */
493 public static boolean canBeMappedToTypeSafeEnum( XSSimpleType type ) {
494 do {
495 if( WellKnownNamespace.XML_SCHEMA.equals(type.getTargetNamespace()) ) {
496 // type must be derived from one of these types
497 String localName = type.getName();
498 if( localName!=null ) {
499 if( localName.equals("anySimpleType") )
500 return false; // catch all case
501 if( localName.equals("ID") || localName.equals("IDREF") )
502 return false; // not ID/IDREF
503
504 // other allowed list
505 if( builtinTypeSafeEnumCapableTypes.contains(localName) )
506 return true;
507 }
508 }
509
510 type = type.getSimpleBaseType();
511 } while( type!=null );
512
513 return false;
514 }
515
516
517
518 /**
519 * Builds a type-safe enum conversion from a simple type
520 * with enumeration facets.
521 *
522 * @param className
523 * The class name of the type-safe enum. Or null to
524 * create a default name.
525 * @param javadoc
526 * Additional javadoc that will be added at the beginning of the
527 * class, or null if none is necessary.
528 * @param members
529 * A map from enumeration values (as String) to BIEnumMember objects.
530 * if some of the value names need to be overrided.
531 * Cannot be null, but the map may not contain entries
532 * for all enumeration values.
533 * @param loc
534 * The source location where the above customizations are
535 * specified, or null if none is available.
536 */
537 private TypeUse bindToTypeSafeEnum( XSRestrictionSimpleType type,
538 String className, String javadoc, Map<String,BIEnumMember> members,
539 EnumMemberMode mode, Locator loc ) {
540
541 if( loc==null ) // use the location of the simple type as the default
542 loc = type.getLocator();
543
544 if( className==null ) {
545 // infer the class name. For this to be possible,
546 // the simple type must be a global one.
547 if( !type.isGlobal() ) {
548 getErrorReporter().error( loc, Messages.ERR_NO_ENUM_NAME_AVAILABLE );
549 // recover by returning a meaningless conversion
550 return CBuiltinLeafInfo.STRING;
551 }
552 className = type.getName();
553 }
554
555 // we apply name conversion in any case
556 className = builder.deriveName(className,type);
557
558 {// compute Javadoc
559 StringWriter out = new StringWriter();
560 SchemaWriter sw = new SchemaWriter(new JavadocEscapeWriter(out));
561 type.visit((XSVisitor)sw);
562
563 if(javadoc!=null) javadoc += "\n\n";
564 else javadoc = "";
565
566 javadoc += Messages.format( Messages.JAVADOC_HEADING, type.getName() )
567 +"\n<p>\n<pre>\n"+out.getBuffer()+"</pre>";
568
569 }
570
571 // build base type
572 refererStack.push(type.getSimpleBaseType());
573 TypeUse use = build(type.getSimpleBaseType());
574 refererStack.pop();
575
576 if(use.isCollection())
577 return null; // can't bind a list to enum constant
578
579 CNonElement baseDt = use.getInfo(); // for now just ignore that case
580
581 if(baseDt instanceof CClassInfo)
582 return null; // can't bind to an enum if the base is a class, since we don't have the value constrctor
583
584 // if the member names collide, re-generate numbered constant names.
585 XSFacet[] errorRef = new XSFacet[1];
586 List<CEnumConstant> memberList = buildCEnumConstants(type, false, members, errorRef);
587 if(memberList==null || checkMemberNameCollision(memberList)!=null) {
588 switch(mode) {
589 case SKIP:
590 // abort
591 return null;
592 case ERROR:
593 // error
594 if(memberList==null) {
595 getErrorReporter().error( errorRef[0].getLocator(),
596 Messages.ERR_CANNOT_GENERATE_ENUM_NAME,
597 errorRef[0].getValue() );
598 } else {
599 CEnumConstant[] collision = checkMemberNameCollision(memberList);
600 getErrorReporter().error( collision[0].getLocator(),
601 Messages.ERR_ENUM_MEMBER_NAME_COLLISION,
602 collision[0].getName() );
603 getErrorReporter().error( collision[1].getLocator(),
604 Messages.ERR_ENUM_MEMBER_NAME_COLLISION_RELATED );
605 }
606 return null; // recover from error
607 case GENERATE:
608 // generate
609 memberList = buildCEnumConstants(type,true,members,null);
610 break;
611 }
612 }
613 if(memberList.isEmpty()) {
614 getErrorReporter().error( loc, Messages.ERR_NO_ENUM_FACET );
615 return null;
616 }
617
618 // use the name of the simple type as the name of the class.
619 CClassInfoParent scope;
620 if(type.isGlobal())
621 scope = new CClassInfoParent.Package(getClassSelector().getPackage(type.getTargetNamespace()));
622 else
623 scope = getClassSelector().getClassScope();
624 CEnumLeafInfo xducer = new CEnumLeafInfo( model, BGMBuilder.getName(type), scope,
625 className, baseDt, memberList, type,
626 builder.getBindInfo(type).toCustomizationList(), loc );
627 xducer.javadoc = javadoc;
628
629 BIConversion conv = new BIConversion.Static( type.getLocator(),xducer);
630 conv.markAsAcknowledged();
631
632 // attach this new conversion object to this simple type
633 // so that successive look up will use the same object.
634 builder.getOrCreateBindInfo(type).addDecl(conv);
635
636 return conv.getTypeUse(type);
637 }
638
639 /**
640 *
641 * @param errorRef
642 * if constant names couldn't be generated, return a reference to that enum facet.
643 * @return
644 * null if unable to generate names for some of the constants.
645 */
646 private List<CEnumConstant> buildCEnumConstants(XSRestrictionSimpleType type, boolean needsToGenerateMemberName, Map<String, BIEnumMember> members, XSFacet[] errorRef) {
647 List<CEnumConstant> memberList = new ArrayList<CEnumConstant>();
648 int idx=1;
649 Set<String> enums = new HashSet<String>(); // to avoid duplicates. See issue #366
650
651 for( XSFacet facet : type.getDeclaredFacets(XSFacet.FACET_ENUMERATION)) {
652 String name=null;
653 String mdoc=builder.getBindInfo(facet).getDocumentation();
654
655 if(!enums.add(facet.getValue().value))
656 continue; // ignore the 2nd occasion
657
658 if( needsToGenerateMemberName ) {
659 // generate names for all member names.
660 // this will even override names specified by the user. that's crazy.
661 name = "VALUE_"+(idx++);
662 } else {
663 String facetValue = facet.getValue().value;
664 BIEnumMember mem = members.get(facetValue);
665 if( mem==null )
666 // look at the one attached to the facet object
667 mem = builder.getBindInfo(facet).get(BIEnumMember.class);
668
669 if (mem!=null) {
670 name = mem.name;
671 if (mdoc != null) {
672 mdoc = mem.javadoc;
673 }
674 }
675
676 if(name==null) {
677 StringBuilder sb = new StringBuilder();
678 for( int i=0; i<facetValue.length(); i++) {
679 char ch = facetValue.charAt(i);
680 if(Character.isJavaIdentifierPart(ch))
681 sb.append(ch);
682 else
683 sb.append('_');
684 }
685 name = model.getNameConverter().toConstantName(sb.toString());
686 }
687 }
688
689 if(!JJavaName.isJavaIdentifier(name)) {
690 if(errorRef!=null) errorRef[0] = facet;
691 return null; // unable to generate a name
692 }
693
694 memberList.add(new CEnumConstant(name,mdoc,facet.getValue().value,facet,builder.getBindInfo(facet).toCustomizationList(),facet.getLocator()));
695 }
696 return memberList;
697 }
698
699 /**
700 * Returns non-null if {@link CEnumConstant}s have name collisions among them.
701 *
702 * @return
703 * if there's a collision, return two {@link CEnumConstant}s that collided.
704 * otherwise return null.
705 */
706 private CEnumConstant[] checkMemberNameCollision( List<CEnumConstant> memberList ) {
707 Map<String,CEnumConstant> names = new HashMap<String,CEnumConstant>();
708 for (CEnumConstant c : memberList) {
709 CEnumConstant old = names.put(c.getName(),c);
710 if(old!=null)
711 // collision detected
712 return new CEnumConstant[]{old,c};
713 }
714 return null;
715 }
716
717
718
719 private EnumMemberMode getEnumMemberMode() {
720 return builder.getGlobalBinding().getEnumMemberMode();
721 }
722
723 private TypeUse lookupBuiltin( String typeLocalName ) {
724 if(typeLocalName.equals("integer") || typeLocalName.equals("long")) {
725 /*
726 attempt an optimization so that we can
727 improve the binding for types like this:
728
729 <simpleType>
730 <restriciton baseType="integer">
731 <maxInclusive value="100" />
732 </
733 </
734
735 ... to int, not BigInteger.
736 */
737
738 BigInteger xe = readFacet(XSFacet.FACET_MAXEXCLUSIVE,-1);
739 BigInteger xi = readFacet(XSFacet.FACET_MAXINCLUSIVE,0);
740 BigInteger max = min(xe,xi); // most restrictive one takes precedence
741
742 if(max!=null) {
743 BigInteger ne = readFacet(XSFacet.FACET_MINEXCLUSIVE,+1);
744 BigInteger ni = readFacet(XSFacet.FACET_MININCLUSIVE,0);
745 BigInteger min = max(ne,ni);
746
747 if(min!=null) {
748 if(min.compareTo(INT_MIN )>=0 && max.compareTo(INT_MAX )<=0)
749 typeLocalName = "int";
750 else
751 if(min.compareTo(LONG_MIN)>=0 && max.compareTo(LONG_MAX)<=0)
752 typeLocalName = "long";
753 }
754 }
755 } else
756 if(typeLocalName.equals("boolean") && isRestrictedTo0And1()) {
757 // this is seen in the SOAP schema and too common to ignore
758 return CBuiltinLeafInfo.BOOLEAN_ZERO_OR_ONE;
759 } else
760 if(typeLocalName.equals("base64Binary")) {
761 return lookupBinaryTypeBinding();
762 } else
763 if(typeLocalName.equals("anySimpleType")) {
764 if(getReferer() instanceof XSAttributeDecl || getReferer() instanceof XSSimpleType)
765 return CBuiltinLeafInfo.STRING;
766 else
767 return CBuiltinLeafInfo.ANYTYPE;
768 }
769 return builtinConversions.get(typeLocalName);
770 }
771
772 /**
773 * Decides the way xs:base64Binary binds.
774 *
775 * This method checks the expected media type.
776 */
777 private TypeUse lookupBinaryTypeBinding() {
778 XSComponent referer = getReferer();
779 String emt = referer.getForeignAttribute(XML_MIME_URI, Const.EXPECTED_CONTENT_TYPES);
780 if(emt!=null) {
781 acknowledgedXmimeContentTypes.add(referer);
782 try {
783 // see http://www.xml.com/lpt/a/2004/07/21/dive.html
784 List<MimeTypeRange> types = MimeTypeRange.parseRanges(emt);
785 MimeTypeRange mt = MimeTypeRange.merge(types);
786
787 // see spec table I-1 in appendix I section 2.1.1 for bindings
788 if(mt.majorType.equalsIgnoreCase("image"))
789 return CBuiltinLeafInfo.IMAGE.makeMimeTyped(mt.toMimeType());
790
791 if(( mt.majorType.equalsIgnoreCase("application") || mt.majorType.equalsIgnoreCase("text"))
792 && isXml(mt.subType))
793 return CBuiltinLeafInfo.XML_SOURCE.makeMimeTyped(mt.toMimeType());
794
795 if((mt.majorType.equalsIgnoreCase("text") && (mt.subType.equalsIgnoreCase("plain")) )) {
796 return CBuiltinLeafInfo.STRING.makeMimeTyped(mt.toMimeType());
797 }
798
799 return CBuiltinLeafInfo.DATA_HANDLER.makeMimeTyped(mt.toMimeType());
800 } catch (ParseException e) {
801 getErrorReporter().error( referer.getLocator(),
802 Messages.format(Messages.ERR_ILLEGAL_EXPECTED_MIME_TYPE,emt, e.getMessage()) );
803 // recover by using the default
804 } catch (MimeTypeParseException e) {
805 getErrorReporter().error( referer.getLocator(),
806 Messages.format(Messages.ERR_ILLEGAL_EXPECTED_MIME_TYPE,emt, e.getMessage()) );
807 }
808 }
809 // default
810 return CBuiltinLeafInfo.BASE64_BYTE_ARRAY;
811 }
812
813 public boolean isAcknowledgedXmimeContentTypes(XSComponent c) {
814 return acknowledgedXmimeContentTypes.contains(c);
815 }
816
817 /**
818 * Returns true if the specified sub-type is an XML type.
819 */
820 private boolean isXml(String subType) {
821 return subType.equals("xml") || subType.endsWith("+xml");
822 }
823
824 /**
825 * Returns true if the {@link #initiatingType} is restricted
826 * to '0' and '1'. This logic is not complete, but it at least
827 * finds the such definition in SOAP @mustUnderstand.
828 */
829 private boolean isRestrictedTo0And1() {
830 XSFacet pattern = initiatingType.getFacet(XSFacet.FACET_PATTERN);
831 if(pattern!=null) {
832 String v = pattern.getValue().value;
833 if(v.equals("0|1") || v.equals("1|0") || v.equals("\\d"))
834 return true;
835 }
836 XSFacet enumf = initiatingType.getFacet(XSFacet.FACET_ENUMERATION);
837 if(enumf!=null) {
838 String v = enumf.getValue().value;
839 if(v.equals("0") || v.equals("1"))
840 return true;
841 }
842 return false;
843 }
844
845 private BigInteger readFacet(String facetName,int offset) {
846 XSFacet me = initiatingType.getFacet(facetName);
847 if(me==null)
848 return null;
849 BigInteger bi = DatatypeConverterImpl._parseInteger(me.getValue().value);
850 if(offset!=0)
851 bi = bi.add(BigInteger.valueOf(offset));
852 return bi;
853 }
854
855 private BigInteger min(BigInteger a, BigInteger b) {
856 if(a==null) return b;
857 if(b==null) return a;
858 return a.min(b);
859 }
860
861 private BigInteger max(BigInteger a, BigInteger b) {
862 if(a==null) return b;
863 if(b==null) return a;
864 return a.max(b);
865 }
866
867 private static final BigInteger LONG_MIN = BigInteger.valueOf(Long.MIN_VALUE);
868 private static final BigInteger LONG_MAX = BigInteger.valueOf(Long.MAX_VALUE);
869 private static final BigInteger INT_MIN = BigInteger.valueOf(Integer.MIN_VALUE);
870 private static final BigInteger INT_MAX = BigInteger.valueOf(Integer.MAX_VALUE);
871
872 static {
873 // list of datatypes which have built-in conversions.
874 // note that although xs:token and xs:normalizedString are not
875 // specified in the spec, they need to be here because they
876 // have different whitespace normalization semantics.
877 Map<String,TypeUse> m = builtinConversions;
878
879 // TODO: this is so dumb
880 m.put("string", CBuiltinLeafInfo.STRING);
881 m.put("anyURI", CBuiltinLeafInfo.STRING);
882 m.put("boolean", CBuiltinLeafInfo.BOOLEAN);
883 // we'll also look at the expected media type, so don't just add this to the map
884 // m.put("base64Binary", CBuiltinLeafInfo.BASE64_BYTE_ARRAY);
885 m.put("hexBinary", CBuiltinLeafInfo.HEXBIN_BYTE_ARRAY);
886 m.put("float", CBuiltinLeafInfo.FLOAT);
887 m.put("decimal", CBuiltinLeafInfo.BIG_DECIMAL);
888 m.put("integer", CBuiltinLeafInfo.BIG_INTEGER);
889 m.put("long", CBuiltinLeafInfo.LONG);
890 m.put("unsignedInt", CBuiltinLeafInfo.LONG);
891 m.put("int", CBuiltinLeafInfo.INT);
892 m.put("unsignedShort", CBuiltinLeafInfo.INT);
893 m.put("short", CBuiltinLeafInfo.SHORT);
894 m.put("unsignedByte", CBuiltinLeafInfo.SHORT);
895 m.put("byte", CBuiltinLeafInfo.BYTE);
896 m.put("double", CBuiltinLeafInfo.DOUBLE);
897 m.put("QName", CBuiltinLeafInfo.QNAME);
898 m.put("NOTATION", CBuiltinLeafInfo.QNAME);
899 m.put("dateTime", CBuiltinLeafInfo.CALENDAR);
900 m.put("date", CBuiltinLeafInfo.CALENDAR);
901 m.put("time", CBuiltinLeafInfo.CALENDAR);
902 m.put("gYearMonth", CBuiltinLeafInfo.CALENDAR);
903 m.put("gYear", CBuiltinLeafInfo.CALENDAR);
904 m.put("gMonthDay", CBuiltinLeafInfo.CALENDAR);
905 m.put("gDay", CBuiltinLeafInfo.CALENDAR);
906 m.put("gMonth", CBuiltinLeafInfo.CALENDAR);
907 m.put("duration", CBuiltinLeafInfo.DURATION);
908 m.put("token", CBuiltinLeafInfo.TOKEN);
909 m.put("normalizedString",CBuiltinLeafInfo.NORMALIZED_STRING);
910 m.put("ID", CBuiltinLeafInfo.ID);
911 m.put("IDREF", CBuiltinLeafInfo.IDREF);
912 // TODO: handling dateTime, time, and date type
913 // String[] names = {
914 // "date", "dateTime", "time", "hexBinary" };
915 }
916 }

mercurial