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

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

mercurial