| /* Copyright 2004 The Apache Software Foundation |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.xmlbeans.impl.schema; |
| |
| import java.util.*; |
| import java.util.List; |
| import java.math.BigInteger; |
| |
| import javax.xml.namespace.QName; |
| import org.apache.xmlbeans.impl.values.XmlValueOutOfRangeException; |
| import org.apache.xmlbeans.impl.regex.RegularExpression; |
| import org.apache.xmlbeans.impl.common.QNameHelper; |
| import org.apache.xmlbeans.impl.common.XmlErrorContext; |
| import org.apache.xmlbeans.XmlObject; |
| import org.apache.xmlbeans.XmlInteger; |
| import org.apache.xmlbeans.SchemaType; |
| import org.apache.xmlbeans.XmlCursor; |
| import org.apache.xmlbeans.XmlAnySimpleType; |
| import org.apache.xmlbeans.SimpleValue; |
| import org.apache.xmlbeans.XmlByte; |
| import org.apache.xmlbeans.XmlShort; |
| import org.apache.xmlbeans.XmlUnsignedByte; |
| import org.w3.x2001.xmlSchema.*; |
| |
| public class StscSimpleTypeResolver |
| { |
| |
| /************************************************************************** |
| * SIMPLE TYPE RESOLUTION HERE |
| * |
| * Simple types can be declared as lists, unions, or restrictions. |
| * These three cases are treated separately in resolveListType, |
| * resolveUnionType, and resolveSimpleRestrictionType. |
| * |
| * The intricate work with facets is done in the restriction case, |
| * using method called resolveFacets (the union and list cases have |
| * trivial facet rules). Then all simple types call resolveProperties |
| * in the end to have their "fundamental facets" resolved. |
| */ |
| |
| public static void resolveSimpleType(SchemaTypeImpl sImpl) |
| { |
| SimpleType parseSt = (SimpleType)sImpl.getParseObject(); |
| |
| assert sImpl.isSimpleType(); |
| |
| // Verify: have list, union, or restriction, but not more than one |
| int count = |
| (parseSt.isSetList() ? 1 : 0) + |
| (parseSt.isSetUnion() ? 1 : 0) + |
| (parseSt.isSetRestriction() ? 1 : 0); |
| if (count > 1) |
| { |
| StscState.get().error( |
| "A simple type must define either a list, a union, or a restriction: more than one found.", |
| XmlErrorContext.MALFORMED_SIMPLE_TYPE_DEFN, |
| parseSt); |
| // recovery: treat it as the first of list, union, restr |
| } |
| else if (count < 1) |
| { |
| StscState.get().error("A simple type must define either a list, a union, or a restriction: none was found.", |
| XmlErrorContext.MALFORMED_SIMPLE_TYPE_DEFN, |
| parseSt); |
| // recovery: treat it as restriction of anySimpleType |
| resolveErrorSimpleType(sImpl); |
| return; |
| } |
| |
| // Set final flags |
| boolean finalRest = false; |
| boolean finalList = false; |
| boolean finalUnion = false; |
| |
| if (parseSt.isSetFinal()) |
| { |
| String value = parseSt.getFinal(); |
| if (value.equals("#all")) |
| finalRest = finalList = finalUnion = true; |
| else if (value.equals("restriction")) |
| finalRest = true; |
| else if (value.equals("list")) |
| finalList = true; |
| else if (value.equals("union")) |
| finalUnion = true; |
| } |
| sImpl.setSimpleFinal(finalRest, finalList, finalUnion); |
| |
| List anonTypes = new ArrayList(); |
| |
| if (parseSt.getList() != null) |
| resolveListType(sImpl, parseSt.getList(), anonTypes); |
| else if (parseSt.getUnion() != null) |
| resolveUnionType(sImpl, parseSt.getUnion(), anonTypes); |
| else if (parseSt.getRestriction() != null) |
| resolveSimpleRestrictionType(sImpl, parseSt.getRestriction(), anonTypes); |
| |
| sImpl.setAnonymousTypeRefs(makeRefArray(anonTypes)); |
| } |
| |
| private static SchemaType.Ref[] makeRefArray(Collection typeList) |
| { |
| SchemaType.Ref[] result = new SchemaType.Ref[typeList.size()]; |
| int j = 0; |
| for (Iterator i = typeList.iterator(); i.hasNext(); j++) |
| result[j] = ((SchemaType)i.next()).getRef(); |
| return result; |
| } |
| |
| static void resolveErrorSimpleType(SchemaTypeImpl sImpl) |
| { |
| sImpl.setSimpleTypeVariety(SchemaType.ATOMIC); |
| sImpl.setBaseTypeRef(BuiltinSchemaTypeSystem.ST_ANY_SIMPLE.getRef()); |
| sImpl.setBaseDepth(BuiltinSchemaTypeSystem.ST_ANY_SIMPLE.getBaseDepth() + 1); |
| sImpl.setPrimitiveTypeRef(BuiltinSchemaTypeSystem.ST_ANY_SIMPLE.getRef()); |
| } |
| |
| static void resolveListType(SchemaTypeImpl sImpl, org.w3.x2001.xmlSchema.ListDocument.List parseList, List anonTypes) |
| { |
| StscState state = StscState.get(); |
| |
| sImpl.setSimpleTypeVariety(SchemaType.LIST); |
| sImpl.setBaseTypeRef(BuiltinSchemaTypeSystem.ST_ANY_SIMPLE.getRef()); |
| sImpl.setBaseDepth(BuiltinSchemaTypeSystem.ST_ANY_SIMPLE.getBaseDepth() + 1); |
| sImpl.setDerivationType(SchemaType.DT_RESTRICTION); |
| |
| if (sImpl.isRedefinition()) |
| { |
| StscState.get().error("A type redefinition must restrict the original definition of the type.", XmlErrorContext.GENERIC_ERROR, parseList); |
| // recovery: oh well. |
| } |
| |
| QName itemName = parseList.getItemType(); |
| LocalSimpleType parseInner = parseList.getSimpleType(); |
| |
| if (itemName != null && parseInner != null) |
| { |
| state.error("List type definitions provide either an itemType attribute " + |
| "or contain a nested simpleType: both were found.", |
| XmlErrorContext.REDUNDANT_NESTED_TYPE, |
| parseList); |
| // recovery: ignore the inner simple type. |
| parseInner = null; |
| } |
| |
| SchemaTypeImpl itemImpl; |
| XmlObject errorLoc; |
| |
| if (itemName != null) |
| { |
| itemImpl = state.findGlobalType(itemName, sImpl.getChameleonNamespace()); |
| errorLoc = parseList.xgetItemType(); |
| if (itemImpl == null) |
| { |
| state.notFoundError(itemName, XmlErrorContext.TYPE_NOT_FOUND, parseList.xgetItemType()); |
| // recovery: treat it as a list of anySimpleType |
| itemImpl = BuiltinSchemaTypeSystem.ST_ANY_SIMPLE; |
| } |
| } |
| else if (parseInner != null) |
| { |
| itemImpl = new SchemaTypeImpl(state.sts()); |
| errorLoc = parseInner; |
| itemImpl.setSimpleType(true); |
| itemImpl.setParseContext(parseInner, sImpl.getTargetNamespace(), sImpl.getChameleonNamespace() != null, false); |
| itemImpl.setOuterSchemaTypeRef(sImpl.getRef()); |
| SchemaAnnotationImpl ann = SchemaAnnotationImpl.getAnnotation(state.sts(), parseInner); |
| itemImpl.setAnnotation(ann); |
| anonTypes.add(itemImpl); |
| } |
| else |
| { |
| state.error("List type definitions provide either an itemType attribute " + |
| "or contain a nested simpleType: neither was found.", |
| XmlErrorContext.LIST_MISSING_ITEM, |
| parseList); |
| // recovery: treat it as an extension of anySimpleType |
| resolveErrorSimpleType(sImpl); |
| return; |
| } |
| |
| // Verify final restrictions |
| if (itemImpl.finalList()) |
| state.error("Cannot derive by list a final type.", XmlErrorContext.CANNOT_DERIVE_FINAL, parseList); |
| |
| // Recursion... |
| StscResolver.resolveType(itemImpl); |
| |
| if (!itemImpl.isSimpleType()) |
| { |
| state.error("Item type for this list type is not simple", |
| XmlErrorContext.LIST_ITEM_NOT_SIMPLE, errorLoc); |
| // recovery: treat the item type as anySimpleType |
| sImpl = BuiltinSchemaTypeSystem.ST_ANY_SIMPLE; |
| } |
| |
| switch (itemImpl.getSimpleVariety()) |
| { |
| case SchemaType.LIST: |
| state.error("This item type is another list type; lists of lists are not allowed.", XmlErrorContext.LIST_OF_LIST, errorLoc); |
| // recovery: treat the list as an anySimpleType |
| resolveErrorSimpleType(sImpl); |
| return; |
| case SchemaType.UNION: |
| if (itemImpl.isUnionOfLists()) |
| { |
| state.error("This item type is a union containing a list; lists of lists are not allowed.", XmlErrorContext.LIST_OF_LIST, errorLoc); |
| resolveErrorSimpleType(sImpl); |
| return; |
| } |
| // fallthrough: nonlist unions are just like atomic items |
| case SchemaType.ATOMIC: |
| sImpl.setListItemTypeRef(itemImpl.getRef()); |
| break; |
| default: |
| assert(false); |
| sImpl.setListItemTypeRef(BuiltinSchemaTypeSystem.ST_ANY_SIMPLE.getRef()); |
| } |
| |
| // now deal with facets |
| sImpl.setBasicFacets(StscState.FACETS_LIST, StscState.FIXED_FACETS_LIST); |
| sImpl.setWhiteSpaceRule( SchemaType.WS_COLLAPSE ); |
| |
| // now compute our intrinsic properties |
| resolveFundamentalFacets(sImpl); |
| } |
| |
| |
| |
| |
| static void resolveUnionType(SchemaTypeImpl sImpl, UnionDocument.Union parseUnion, List anonTypes) |
| { |
| sImpl.setSimpleTypeVariety(SchemaType.UNION); |
| sImpl.setBaseTypeRef(BuiltinSchemaTypeSystem.ST_ANY_SIMPLE.getRef()); |
| sImpl.setBaseDepth(BuiltinSchemaTypeSystem.ST_ANY_SIMPLE.getBaseDepth() + 1); |
| sImpl.setDerivationType(SchemaType.DT_RESTRICTION); |
| |
| StscState state = StscState.get(); |
| |
| if (sImpl.isRedefinition()) |
| { |
| StscState.get().error("A type redefinition must restrict the original definition of the type.", XmlErrorContext.GENERIC_ERROR, parseUnion); |
| // recovery: oh well. |
| } |
| |
| List memberTypes = parseUnion.getMemberTypes(); |
| SimpleType[] simpleTypes = parseUnion.getSimpleTypeArray(); |
| |
| List memberImplList = new ArrayList(); |
| |
| if (simpleTypes.length == 0 && (memberTypes == null || memberTypes.size() == 0)) |
| { |
| state.error("A union type must specify at least one member type", XmlErrorContext.UNION_MEMBER_NOT_SIMPLE, parseUnion); |
| // recovery: oh well, zero member types is fine. |
| } |
| |
| if (memberTypes != null) |
| { |
| for (Iterator mNames = memberTypes.iterator(); mNames.hasNext(); ) |
| { |
| QName mName = (QName)mNames.next(); |
| SchemaTypeImpl memberImpl = state.findGlobalType(mName, sImpl.getChameleonNamespace()); |
| if (memberImpl == null) |
| // recovery: skip member |
| state.notFoundError(mName, XmlErrorContext.TYPE_NOT_FOUND, parseUnion.xgetMemberTypes()); |
| else |
| memberImplList.add(memberImpl); |
| } |
| } |
| |
| for (int i = 0; i < simpleTypes.length; i++) |
| { |
| // BUGBUG: see if non<simpleType> children can leak through |
| SchemaTypeImpl mImpl = new SchemaTypeImpl(state.sts()); |
| mImpl.setSimpleType(true); |
| mImpl.setParseContext(simpleTypes[i], sImpl.getTargetNamespace(), sImpl.getChameleonNamespace() != null, false); |
| memberImplList.add(mImpl); |
| mImpl.setOuterSchemaTypeRef(sImpl.getRef()); |
| mImpl.setAnonymousUnionMemberOrdinal(i + 1); |
| SchemaAnnotationImpl ann = SchemaAnnotationImpl.getAnnotation(state.sts(), simpleTypes[i]); |
| mImpl.setAnnotation(ann); |
| anonTypes.add(mImpl); |
| } |
| |
| // Recurse and resolve all member types |
| for (Iterator mImpls = memberImplList.iterator(); mImpls.hasNext(); ) |
| { |
| SchemaTypeImpl mImpl = (SchemaTypeImpl)mImpls.next(); |
| if (!StscResolver.resolveType(mImpl)) |
| { |
| if (mImpl.getOuterType().equals(sImpl)) |
| state.error("Member has a cyclic dependency on the containing union", XmlErrorContext.CYCLIC_DEPENDENCY, mImpl.getParseObject()); |
| else |
| state.error("Member " + QNameHelper.pretty(mImpl.getName()) + " has a cyclic dependency on the union", XmlErrorContext.CYCLIC_DEPENDENCY, parseUnion.xgetMemberTypes()); |
| |
| // recovery: ignore the errant union member |
| mImpls.remove(); |
| continue; |
| } |
| } |
| |
| // Now verify members |
| boolean isUnionOfLists = false; |
| |
| for (Iterator mImpls = memberImplList.iterator(); mImpls.hasNext(); ) |
| { |
| SchemaTypeImpl mImpl = (SchemaTypeImpl)mImpls.next(); |
| |
| if (!mImpl.isSimpleType()) |
| { |
| if (mImpl.getOuterType() != null && mImpl.getOuterType().equals(sImpl)) |
| state.error("Member is not simple", XmlErrorContext.UNION_MEMBER_NOT_SIMPLE, mImpl.getParseObject()); |
| else |
| state.error("Member " + QNameHelper.pretty(mImpl.getName()) + " is not simple", XmlErrorContext.UNION_MEMBER_NOT_SIMPLE, parseUnion.xgetMemberTypes()); |
| |
| // recovery: ignore the errant union member |
| mImpls.remove(); |
| continue; |
| } |
| |
| if (mImpl.getSimpleVariety() == SchemaType.LIST || |
| mImpl.getSimpleVariety() == SchemaType.UNION && mImpl.isUnionOfLists()) |
| isUnionOfLists = true; |
| } |
| |
| // Verify any final restrictions |
| for (int i = 0 ; i < memberImplList.size() ; i++) |
| { |
| SchemaTypeImpl mImpl = (SchemaTypeImpl)memberImplList.get(i); |
| if (mImpl.finalUnion()) |
| state.error("Cannot derive by union a final type.", XmlErrorContext.CANNOT_DERIVE_FINAL, parseUnion); |
| } |
| |
| sImpl.setUnionOfLists(isUnionOfLists); |
| |
| sImpl.setUnionMemberTypeRefs(makeRefArray(memberImplList)); |
| |
| // now deal with facets |
| sImpl.setBasicFacets(StscState.FACETS_UNION, StscState.FIXED_FACETS_UNION); |
| |
| // now compute our intrinsic properties |
| resolveFundamentalFacets(sImpl); |
| } |
| |
| static void resolveSimpleRestrictionType(SchemaTypeImpl sImpl, RestrictionDocument.Restriction parseRestr, List anonTypes) |
| { |
| QName baseName = parseRestr.getBase(); |
| SimpleType parseInner = parseRestr.getSimpleType(); |
| StscState state = StscState.get(); |
| |
| if (baseName != null && parseInner != null) |
| { |
| state.error("Simple type restrictions must name a base type " + |
| "or contain a nested simple type: both were found.", |
| XmlErrorContext.RESTRICTION_REDUNDANT_BASE, |
| parseRestr); |
| // recovery: ignore the inner simple type. |
| parseInner = null; |
| } |
| |
| SchemaTypeImpl baseImpl; |
| |
| if (baseName != null) |
| { |
| if (sImpl.isRedefinition()) |
| { |
| baseImpl = state.findRedefinedGlobalType(parseRestr.getBase(), sImpl.getChameleonNamespace(), sImpl.getName()); |
| if (baseImpl != null && !baseImpl.getName().equals(sImpl.getName())) |
| state.error("A type redefinition must restrict the original type definition", XmlErrorContext.GENERIC_ERROR, parseRestr); |
| } |
| else |
| { |
| baseImpl = state.findGlobalType(baseName, sImpl.getChameleonNamespace()); |
| } |
| if (baseImpl == null) |
| { |
| state.notFoundError(baseName, XmlErrorContext.TYPE_NOT_FOUND, parseRestr.xgetBase()); |
| // recovery: treat it as an extension of anySimpleType |
| baseImpl = BuiltinSchemaTypeSystem.ST_ANY_SIMPLE; |
| } |
| } |
| else if (parseInner != null) |
| { |
| if (sImpl.isRedefinition()) |
| { |
| StscState.get().error("A type redefinition must restrict the original definition of the type.", XmlErrorContext.GENERIC_ERROR, parseInner); |
| // recovery: oh well. |
| } |
| |
| baseImpl = new SchemaTypeImpl(state.sts()); |
| baseImpl.setSimpleType(true); |
| baseImpl.setParseContext(parseInner, sImpl.getTargetNamespace(), sImpl.getChameleonNamespace() != null, false); |
| // baseImpl.setSkippedAnonymousType(true); |
| baseImpl.setOuterSchemaTypeRef(sImpl.getRef()); |
| SchemaAnnotationImpl ann = SchemaAnnotationImpl.getAnnotation(state.sts(), parseInner); |
| baseImpl.setAnnotation(ann); |
| anonTypes.add(baseImpl); |
| } |
| else |
| { |
| state.error("Simple type restrictions must name a base type " + |
| "or contain a nested simple type: neither were found.", |
| XmlErrorContext.RESTRICTION_MISSING_BASE, |
| parseRestr); |
| // recovery: treat it as an extension of anySimpleType |
| baseImpl = BuiltinSchemaTypeSystem.ST_ANY_SIMPLE; |
| } |
| |
| // Recursion! |
| if (!StscResolver.resolveType(baseImpl)) |
| { |
| // cyclic dependency recovery: treat it as an extension of anySimpleType |
| baseImpl = BuiltinSchemaTypeSystem.ST_ANY_SIMPLE; |
| } |
| |
| if (baseImpl.finalRestriction()) |
| state.error("Cannot restrict a final type", XmlErrorContext.CANNOT_DERIVE_FINAL, parseRestr); |
| |
| sImpl.setBaseTypeRef(baseImpl.getRef()); |
| sImpl.setBaseDepth(baseImpl.getBaseDepth() + 1); |
| sImpl.setDerivationType(SchemaType.DT_RESTRICTION); |
| |
| if (!baseImpl.isSimpleType()) |
| { |
| state.error("Base type for this simple type restriction is not simple", |
| XmlErrorContext.SIMPLE_RESTRICTION_NOT_SIMPLE, |
| parseRestr.xgetBase()); |
| // recovery: treat it as a restriction of anySimpleType |
| resolveErrorSimpleType(sImpl); |
| return; |
| } |
| |
| sImpl.setSimpleTypeVariety(baseImpl.getSimpleVariety()); |
| |
| // copy variety-specific properties |
| switch (baseImpl.getSimpleVariety()) |
| { |
| case SchemaType.ATOMIC: |
| sImpl.setPrimitiveTypeRef(baseImpl.getPrimitiveType().getRef()); |
| break; |
| case SchemaType.UNION: |
| sImpl.setUnionOfLists(baseImpl.isUnionOfLists()); |
| sImpl.setUnionMemberTypeRefs(makeRefArray(Arrays.asList(baseImpl.getUnionMemberTypes()))); |
| break; |
| case SchemaType.LIST: |
| sImpl.setListItemTypeRef(baseImpl.getListItemType().getRef()); |
| break; |
| } |
| |
| // deal with facets |
| resolveFacets(sImpl, parseRestr, baseImpl); |
| |
| // now compute our intrinsic properties |
| resolveFundamentalFacets(sImpl); |
| } |
| |
| static int translateWhitespaceCode(XmlAnySimpleType value) |
| { |
| // BUGBUG: add whitespace rule to textvalue. |
| String textval = value.getStringValue(); |
| |
| if (textval.equals("collapse")) |
| return SchemaType.WS_COLLAPSE; |
| |
| if (textval.equals("preserve")) |
| return SchemaType.WS_PRESERVE; |
| |
| if (textval.equals("replace")) |
| return SchemaType.WS_REPLACE; |
| |
| StscState.get().error("Unrecognized whitespace value \"" + textval + "\"", XmlErrorContext.FACET_VALUE_MALFORMED, value); |
| return SchemaType.WS_UNSPECIFIED; |
| } |
| |
| static boolean isMultipleFacet(int facetcode) |
| { |
| return (facetcode == SchemaType.FACET_ENUMERATION || |
| facetcode == SchemaType.FACET_PATTERN); |
| } |
| |
| static boolean facetAppliesToType(int facetCode, SchemaTypeImpl baseImpl) |
| { |
| switch (baseImpl.getSimpleVariety()) |
| { |
| case SchemaType.LIST: |
| switch (facetCode) |
| { |
| case SchemaType.FACET_LENGTH: |
| case SchemaType.FACET_MIN_LENGTH: |
| case SchemaType.FACET_MAX_LENGTH: |
| case SchemaType.FACET_ENUMERATION: |
| case SchemaType.FACET_PATTERN: |
| case SchemaType.FACET_WHITE_SPACE: |
| return true; |
| } |
| return false; |
| |
| case SchemaType.UNION: |
| switch (facetCode) |
| { |
| case SchemaType.FACET_ENUMERATION: |
| case SchemaType.FACET_PATTERN: |
| return true; |
| } |
| return false; |
| } |
| |
| // the atomic case |
| |
| switch (baseImpl.getPrimitiveType().getBuiltinTypeCode()) |
| { |
| case SchemaType.BTC_ANY_SIMPLE: |
| return false; |
| |
| case SchemaType.BTC_BOOLEAN: |
| switch (facetCode) |
| { |
| case SchemaType.FACET_PATTERN: |
| case SchemaType.FACET_WHITE_SPACE: |
| return true; |
| } |
| return false; |
| |
| case SchemaType.BTC_FLOAT: |
| case SchemaType.BTC_DOUBLE: |
| case SchemaType.BTC_DURATION: |
| case SchemaType.BTC_DATE_TIME: |
| case SchemaType.BTC_TIME: |
| case SchemaType.BTC_DATE: |
| case SchemaType.BTC_G_YEAR_MONTH: |
| case SchemaType.BTC_G_YEAR: |
| case SchemaType.BTC_G_MONTH_DAY: |
| case SchemaType.BTC_G_DAY: |
| case SchemaType.BTC_G_MONTH: |
| switch (facetCode) |
| { |
| case SchemaType.FACET_MIN_EXCLUSIVE: |
| case SchemaType.FACET_MIN_INCLUSIVE: |
| case SchemaType.FACET_MAX_INCLUSIVE: |
| case SchemaType.FACET_MAX_EXCLUSIVE: |
| case SchemaType.FACET_ENUMERATION: |
| case SchemaType.FACET_PATTERN: |
| case SchemaType.FACET_WHITE_SPACE: |
| return true; |
| } |
| return false; |
| |
| case SchemaType.BTC_DECIMAL: |
| switch (facetCode) |
| { |
| case SchemaType.FACET_MIN_EXCLUSIVE: |
| case SchemaType.FACET_MIN_INCLUSIVE: |
| case SchemaType.FACET_MAX_INCLUSIVE: |
| case SchemaType.FACET_MAX_EXCLUSIVE: |
| case SchemaType.FACET_TOTAL_DIGITS: |
| case SchemaType.FACET_FRACTION_DIGITS: |
| case SchemaType.FACET_ENUMERATION: |
| case SchemaType.FACET_PATTERN: |
| case SchemaType.FACET_WHITE_SPACE: |
| return true; |
| } |
| return false; |
| |
| case SchemaType.BTC_BASE_64_BINARY: |
| case SchemaType.BTC_HEX_BINARY: |
| case SchemaType.BTC_ANY_URI: |
| case SchemaType.BTC_QNAME: |
| case SchemaType.BTC_NOTATION: |
| case SchemaType.BTC_STRING: |
| switch (facetCode) |
| { |
| case SchemaType.FACET_LENGTH: |
| case SchemaType.FACET_MIN_LENGTH: |
| case SchemaType.FACET_MAX_LENGTH: |
| case SchemaType.FACET_ENUMERATION: |
| case SchemaType.FACET_PATTERN: |
| case SchemaType.FACET_WHITE_SPACE: |
| return true; |
| } |
| return false; |
| default: |
| assert(false); |
| return false; |
| } |
| } |
| |
| private static int other_similar_limit(int facetcode) |
| { |
| switch (facetcode) |
| { |
| case SchemaType.FACET_MIN_EXCLUSIVE: |
| return SchemaType.FACET_MIN_INCLUSIVE; |
| case SchemaType.FACET_MIN_INCLUSIVE: |
| return SchemaType.FACET_MIN_EXCLUSIVE; |
| case SchemaType.FACET_MAX_INCLUSIVE: |
| return SchemaType.FACET_MAX_EXCLUSIVE; |
| case SchemaType.FACET_MAX_EXCLUSIVE: |
| return SchemaType.FACET_MAX_INCLUSIVE; |
| default: |
| assert(false); |
| throw new IllegalStateException(); |
| } |
| } |
| |
| static void resolveFacets(SchemaTypeImpl sImpl, XmlObject restriction, SchemaTypeImpl baseImpl) |
| { |
| StscState state = StscState.get(); |
| |
| boolean[] seenFacet = new boolean[SchemaType.LAST_FACET + 1]; |
| XmlAnySimpleType[] myFacets = baseImpl.getBasicFacets(); // makes a copy |
| boolean[] fixedFacets = baseImpl.getFixedFacets(); |
| int wsr = SchemaType.WS_UNSPECIFIED; |
| List enumeratedValues = null; |
| List patterns = null; |
| |
| if (restriction != null) |
| { |
| XmlCursor cur = restriction.newCursor(); |
| for (boolean more = cur.toFirstChild(); more; more = cur.toNextSibling()) |
| { |
| int code = translateFacetCode(cur.getName()); |
| if (code == -1) |
| continue; |
| |
| Facet facet = (Facet)cur.getObject(); |
| |
| if (!facetAppliesToType(code, baseImpl)) |
| { |
| state.error("The facet " + facet.newCursor().getName().getLocalPart() + " does not apply to the base type " + baseImpl, XmlErrorContext.FACET_DOES_NOT_APPLY, facet); |
| continue; |
| } |
| if (seenFacet[code] && !isMultipleFacet(code)) |
| { |
| state.error("Facet specified multiple times", XmlErrorContext.FACET_DUPLICATED, facet); |
| continue; |
| } |
| seenFacet[code] = true; |
| |
| switch (code) |
| { |
| case SchemaType.FACET_LENGTH: |
| if (myFacets[SchemaType.FACET_MIN_LENGTH] != null || |
| myFacets[SchemaType.FACET_MAX_LENGTH] != null) |
| { |
| state.error("Cannot specify length in addition to minLength or maxLength", XmlErrorContext.FACET_DUPLICATED, facet); |
| continue; |
| } |
| XmlInteger len = StscTranslator.buildNnInteger(facet.getValue()); |
| if (len == null) |
| { |
| state.error("Must be a nonnegative integer", XmlErrorContext.FACET_VALUE_MALFORMED, facet); |
| continue; |
| } |
| if (fixedFacets[code] && !myFacets[code].valueEquals(len)) |
| { |
| state.error("This facet is fixed and cannot be overridden", XmlErrorContext.FACET_FIXED, facet); |
| continue; |
| } |
| myFacets[code] = len; |
| break; |
| |
| case SchemaType.FACET_MIN_LENGTH: |
| case SchemaType.FACET_MAX_LENGTH: |
| if (myFacets[SchemaType.FACET_LENGTH] != null) |
| { |
| state.error("Cannot specify minLength or maxLength in addition to length", XmlErrorContext.FACET_DUPLICATED, facet); |
| continue; |
| } |
| XmlInteger mlen = StscTranslator.buildNnInteger(facet.getValue()); |
| if (mlen == null) |
| { |
| state.error("Must be a nonnegative integer", XmlErrorContext.FACET_VALUE_MALFORMED, facet); |
| continue; |
| } |
| if (fixedFacets[code] && !myFacets[code].valueEquals(mlen)) |
| { |
| state.error("This facet is fixed and cannot be overridden", XmlErrorContext.FACET_FIXED, facet); |
| continue; |
| } |
| if (myFacets[SchemaType.FACET_MAX_LENGTH] != null) |
| { |
| if (mlen.compareValue(myFacets[SchemaType.FACET_MAX_LENGTH]) > 0) |
| { |
| state.error("Larger than prior maxLength", XmlErrorContext.FACET_VALUE_MALFORMED, facet); |
| continue; |
| } |
| } |
| if (myFacets[SchemaType.FACET_MIN_LENGTH] != null) |
| { |
| if (mlen.compareValue(myFacets[SchemaType.FACET_MIN_LENGTH]) < 0) |
| { |
| state.error("Smaller than prior minLength", XmlErrorContext.FACET_VALUE_MALFORMED, facet); |
| continue; |
| } |
| } |
| myFacets[code] = mlen; |
| break; |
| |
| case SchemaType.FACET_TOTAL_DIGITS: |
| case SchemaType.FACET_FRACTION_DIGITS: |
| boolean istotaldig = (code == SchemaType.FACET_TOTAL_DIGITS); |
| XmlInteger dig = StscTranslator.buildNnInteger(facet.getValue()); |
| if (dig == null) |
| { |
| state.error("Must be a nonnegative integer", XmlErrorContext.FACET_VALUE_MALFORMED, facet); |
| break; |
| } |
| if (fixedFacets[code] && !myFacets[code].valueEquals(dig)) |
| { |
| state.error("This facet is fixed and cannot be overridden", XmlErrorContext.FACET_FIXED, facet); |
| continue; |
| } |
| if (myFacets[SchemaType.FACET_TOTAL_DIGITS] != null) |
| { |
| if (dig.compareValue(myFacets[SchemaType.FACET_TOTAL_DIGITS]) > 0) |
| state.error("Larger than prior totalDigits", XmlErrorContext.FACET_VALUE_MALFORMED, facet); |
| } |
| if (!istotaldig && myFacets[SchemaType.FACET_FRACTION_DIGITS] != null) |
| { |
| if (dig.compareValue(myFacets[SchemaType.FACET_FRACTION_DIGITS]) > 0) |
| state.error("Larger than prior fractionDigits", XmlErrorContext.FACET_VALUE_MALFORMED, facet); |
| } |
| myFacets[code] = dig; |
| break; |
| |
| case SchemaType.FACET_MIN_EXCLUSIVE: |
| case SchemaType.FACET_MIN_INCLUSIVE: |
| case SchemaType.FACET_MAX_INCLUSIVE: |
| case SchemaType.FACET_MAX_EXCLUSIVE: |
| |
| if (seenFacet[other_similar_limit(code)]) |
| { |
| state.error("Cannot define both inclusive and exclusive limit in the same restriciton", XmlErrorContext.FACET_DUPLICATED, facet); |
| continue; |
| } |
| boolean ismin = (code == SchemaType.FACET_MIN_EXCLUSIVE || code == SchemaType.FACET_MIN_INCLUSIVE); |
| boolean isexclusive = (code == SchemaType.FACET_MIN_EXCLUSIVE || code == SchemaType.FACET_MAX_EXCLUSIVE); |
| |
| XmlAnySimpleType limit; |
| try |
| { |
| limit = baseImpl.newValue(facet.getValue()); |
| } |
| catch (XmlValueOutOfRangeException e) |
| { |
| // note: this guarantees that the limit is a valid number in the |
| // base data type!! |
| state.error("Must be valid value in base type", XmlErrorContext.FACET_VALUE_MALFORMED, facet); |
| |
| // BUGBUG: if there are actual schemas that redefine min/maxExclusive, |
| // they will need this rule relaxed for them!! |
| continue; |
| } |
| if (fixedFacets[code] && !myFacets[code].valueEquals(limit)) |
| { |
| state.error("This facet is fixed and cannot be overridden", XmlErrorContext.FACET_FIXED, facet); |
| continue; |
| } |
| if (myFacets[code] != null) |
| { |
| int comparison = limit.compareValue(myFacets[code]); |
| if (comparison == 2 || comparison == (ismin ? -1 : 1)) |
| { |
| state.error(ismin ? |
| (isexclusive ? |
| "Must be greater than or equal to previous minExclusive" : |
| "Must be greater than or equal to previous minInclusive") : |
| (isexclusive ? |
| "Must be less than or equal to previous maxExclusive" : |
| "Must be less than or equal to previous maxInclusive"), |
| XmlErrorContext.FACET_VALUE_MALFORMED, facet); |
| continue; |
| } |
| } |
| myFacets[code] = limit; |
| myFacets[other_similar_limit(code)] = null; |
| break; |
| |
| case SchemaType.FACET_WHITE_SPACE: |
| wsr = translateWhitespaceCode(facet.getValue()); |
| if (baseImpl.getWhiteSpaceRule() > wsr) |
| { |
| wsr = SchemaType.WS_UNSPECIFIED; |
| state.error("Cannot apply this whitespace facet over the previous one", XmlErrorContext.FACET_VALUE_MALFORMED, facet); |
| continue; |
| } |
| myFacets[code] = StscState.build_wsstring(wsr).get(); |
| break; |
| |
| case SchemaType.FACET_ENUMERATION: |
| XmlObject enumval; |
| try |
| { |
| enumval = baseImpl.newValue(facet.getValue()); |
| // enumval.set(facet.getValue()); |
| // ((XmlObjectBase)enumval).setImmutable(); |
| } |
| catch (XmlValueOutOfRangeException e) |
| { |
| state.error("Enumerated value invalid in base type", XmlErrorContext.FACET_VALUE_MALFORMED, facet); |
| continue; |
| } |
| if (enumeratedValues == null) |
| enumeratedValues = new ArrayList(); |
| enumeratedValues.add(enumval); |
| break; |
| |
| case SchemaType.FACET_PATTERN: |
| org.apache.xmlbeans.impl.regex.RegularExpression p; |
| try { p = new org.apache.xmlbeans.impl.regex.RegularExpression(facet.getValue().getStringValue(), "X"); } |
| catch (org.apache.xmlbeans.impl.regex.ParseException e) |
| { |
| state.error("Malformed regular expression", XmlErrorContext.FACET_VALUE_MALFORMED, facet); |
| continue; |
| } |
| if (patterns == null) |
| patterns = new ArrayList(); |
| patterns.add(p); |
| break; |
| } |
| |
| if (facet.getFixed()) |
| fixedFacets[code] = true; |
| } |
| } |
| |
| // Store the array of basic facets |
| |
| sImpl.setBasicFacets(makeValueRefArray(myFacets), fixedFacets); |
| |
| // Update the numeric whitespace rule |
| if (wsr == SchemaType.WS_UNSPECIFIED) |
| wsr = baseImpl.getWhiteSpaceRule(); |
| sImpl.setWhiteSpaceRule(wsr); |
| |
| // store away the enumerated values |
| if (enumeratedValues != null) |
| { |
| sImpl.setEnumerationValues(makeValueRefArray((XmlAnySimpleType[]) |
| enumeratedValues.toArray(new XmlAnySimpleType[enumeratedValues.size()]))); |
| |
| SchemaType beType = sImpl; |
| if (sImpl.getBaseType().getBaseEnumType() != null) |
| beType = sImpl.getBaseType().getBaseEnumType(); |
| sImpl.setBaseEnumTypeRef(beType.getRef()); |
| } |
| else |
| { |
| sImpl.copyEnumerationValues(baseImpl); |
| } |
| |
| // store the pattern list |
| org.apache.xmlbeans.impl.regex.RegularExpression[] patternArray; |
| if (patterns != null) |
| patternArray = (org.apache.xmlbeans.impl.regex.RegularExpression[])patterns.toArray(EMPTY_REGEX_ARRAY); |
| else |
| patternArray = EMPTY_REGEX_ARRAY; |
| sImpl.setPatternFacet((patternArray.length > 0 || baseImpl.hasPatternFacet())); |
| sImpl.setPatterns(patternArray); |
| } |
| |
| private static XmlValueRef[] makeValueRefArray(XmlAnySimpleType[] source) |
| { |
| XmlValueRef[] result = new XmlValueRef[source.length]; |
| for (int i = 0; i < result.length; i++) |
| result[i] = (source[i] == null ? null : new XmlValueRef(source[i])); |
| return result; |
| } |
| |
| private static final org.apache.xmlbeans.impl.regex.RegularExpression[] EMPTY_REGEX_ARRAY = new org.apache.xmlbeans.impl.regex.RegularExpression[0]; |
| |
| private static boolean isDiscreteType(SchemaTypeImpl sImpl) |
| { |
| if (sImpl.getFacet(SchemaType.FACET_FRACTION_DIGITS) != null) |
| return true; |
| // BUGBUG: spec is silent on enumerations; they're finite too. |
| switch (sImpl.getPrimitiveType().getBuiltinTypeCode()) |
| { |
| case SchemaType.BTC_DATE: |
| case SchemaType.BTC_G_YEAR_MONTH: |
| case SchemaType.BTC_G_YEAR: |
| case SchemaType.BTC_G_MONTH_DAY: |
| case SchemaType.BTC_G_DAY: |
| case SchemaType.BTC_G_MONTH: |
| case SchemaType.BTC_BOOLEAN: |
| return true; |
| } |
| return false; |
| } |
| |
| private static boolean isNumericPrimitive(SchemaType sImpl) |
| { |
| switch (sImpl.getBuiltinTypeCode()) |
| { |
| case SchemaType.BTC_DECIMAL: |
| case SchemaType.BTC_FLOAT: |
| case SchemaType.BTC_DOUBLE: |
| return true; |
| } |
| return false; |
| } |
| |
| private static int decimalSizeOfType(SchemaTypeImpl sImpl) |
| { |
| int size = mathematicalSizeOfType(sImpl); |
| |
| // byte and short are inconvenient, because setByte((byte)4) requires a cast. |
| // So use "int" unless you're really a xs:byte, xs:short, or an xs:unsignedByte |
| // (the last case is included for alignment with JAXB) |
| |
| if (size == SchemaType.SIZE_BYTE && !XmlByte.type.isAssignableFrom(sImpl)) |
| size = SchemaType.SIZE_SHORT; |
| if (size == SchemaType.SIZE_SHORT && !XmlShort.type.isAssignableFrom(sImpl) && !XmlUnsignedByte.type.isAssignableFrom(sImpl)) |
| size = SchemaType.SIZE_INT; |
| |
| return size; |
| } |
| |
| private static int mathematicalSizeOfType(SchemaTypeImpl sImpl) |
| { |
| if (sImpl.getPrimitiveType().getBuiltinTypeCode() != SchemaType.BTC_DECIMAL) |
| return SchemaType.NOT_DECIMAL; |
| |
| if (sImpl.getFacet(SchemaType.FACET_FRACTION_DIGITS) == null || |
| ((SimpleValue)sImpl.getFacet(SchemaType.FACET_FRACTION_DIGITS)).getBigIntegerValue().signum() != 0) |
| return SchemaType.SIZE_BIG_DECIMAL; |
| |
| BigInteger min = null; |
| BigInteger max = null; |
| |
| if (sImpl.getFacet(SchemaType.FACET_MIN_EXCLUSIVE) != null) |
| min = ((SimpleValue)sImpl.getFacet(SchemaType.FACET_MIN_EXCLUSIVE)).getBigIntegerValue(); // .add(BigInteger.ONE); |
| if (sImpl.getFacet(SchemaType.FACET_MIN_INCLUSIVE) != null) |
| min = ((SimpleValue)sImpl.getFacet(SchemaType.FACET_MIN_INCLUSIVE)).getBigIntegerValue(); |
| if (sImpl.getFacet(SchemaType.FACET_MAX_INCLUSIVE) != null) |
| max = ((SimpleValue)sImpl.getFacet(SchemaType.FACET_MAX_INCLUSIVE)).getBigIntegerValue(); |
| if (sImpl.getFacet(SchemaType.FACET_MAX_EXCLUSIVE) != null) |
| max = ((SimpleValue)sImpl.getFacet(SchemaType.FACET_MAX_EXCLUSIVE)).getBigIntegerValue(); // .subtract(BigInteger.ONE); |
| |
| if (sImpl.getFacet(SchemaType.FACET_TOTAL_DIGITS) != null) |
| { |
| BigInteger peg = null; |
| try |
| { |
| BigInteger totalDigits = ((SimpleValue)sImpl.getFacet(SchemaType.FACET_TOTAL_DIGITS)).getBigIntegerValue(); |
| |
| switch (totalDigits.intValue()) |
| { |
| case 0: case 1: case 2: |
| peg = BigInteger.valueOf(99L); // BYTE size |
| break; |
| case 3: case 4: |
| peg = BigInteger.valueOf(9999L); // SHORT size |
| break; |
| case 5: case 6: case 7: case 8: case 9: |
| peg = BigInteger.valueOf(999999999L); // INT size |
| break; |
| case 10: case 11: case 12: case 13: case 14: |
| case 15: case 16: case 17: case 18: |
| peg = BigInteger.valueOf(999999999999999999L); // LONG size |
| break; |
| } |
| } |
| catch (XmlValueOutOfRangeException e) {} |
| if (peg != null) |
| { |
| min = (min == null ? peg.negate() : min.max(peg.negate())); |
| max = (max == null ? peg : max.min(peg)); |
| } |
| } |
| |
| if (min != null && max != null) |
| { |
| // find the largest "absolute value" number that must be dealt with |
| if (min.signum() < 0) |
| min = min.negate().subtract(BigInteger.ONE); |
| if (max.signum() < 0) |
| max = max.negate().subtract(BigInteger.ONE); |
| |
| max = max.max(min); |
| if (max.compareTo(BigInteger.valueOf(Byte.MAX_VALUE)) <= 0) |
| return SchemaType.SIZE_BYTE; |
| if (max.compareTo(BigInteger.valueOf(Short.MAX_VALUE)) <= 0) |
| return SchemaType.SIZE_SHORT; |
| if (max.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) <= 0) |
| return SchemaType.SIZE_INT; |
| if (max.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) <= 0) |
| return SchemaType.SIZE_LONG; |
| } |
| |
| return SchemaType.SIZE_BIG_INTEGER; |
| } |
| |
| |
| static void resolveFundamentalFacets(SchemaTypeImpl sImpl) |
| { |
| // deal with, isOrdered, isBounded, isFinite, isNumeric |
| // also deal with |
| switch (sImpl.getSimpleVariety()) |
| { |
| case SchemaType.ATOMIC: |
| SchemaTypeImpl baseImpl = (SchemaTypeImpl)sImpl.getBaseType(); |
| sImpl.setOrdered(baseImpl.ordered()); |
| sImpl.setBounded( |
| (sImpl.getFacet(SchemaType.FACET_MIN_EXCLUSIVE) != null || |
| sImpl.getFacet(SchemaType.FACET_MIN_INCLUSIVE) != null) && |
| (sImpl.getFacet(SchemaType.FACET_MAX_INCLUSIVE) != null || |
| sImpl.getFacet(SchemaType.FACET_MAX_EXCLUSIVE) != null)); |
| sImpl.setFinite(baseImpl.isFinite() || |
| sImpl.isBounded() && isDiscreteType(sImpl)); |
| sImpl.setNumeric(baseImpl.isNumeric() || |
| isNumericPrimitive(sImpl.getPrimitiveType())); |
| sImpl.setDecimalSize(decimalSizeOfType(sImpl)); |
| break; |
| case SchemaType.UNION: |
| SchemaType[] mTypes = sImpl.getUnionMemberTypes(); |
| int ordered = SchemaType.UNORDERED; |
| boolean isBounded = true; |
| boolean isFinite = true; |
| boolean isNumeric = true; |
| // ordered if any is ordered, bounded if all are bounded. |
| for (int i = 0; i < mTypes.length; i++) |
| { |
| if (mTypes[i].ordered() != SchemaType.UNORDERED) |
| ordered = SchemaType.PARTIAL_ORDER; |
| if (!mTypes[i].isBounded()) |
| isBounded = false; |
| if (!mTypes[i].isFinite()) |
| isFinite = false; |
| if (!mTypes[i].isNumeric()) |
| isNumeric = false; |
| } |
| sImpl.setOrdered(ordered); |
| sImpl.setBounded(isBounded); |
| sImpl.setFinite(isFinite); |
| sImpl.setNumeric(isNumeric); |
| sImpl.setDecimalSize(SchemaType.NOT_DECIMAL); |
| break; |
| case SchemaType.LIST: |
| sImpl.setOrdered(SchemaType.UNORDERED); |
| // BUGBUG: the schema spec is wrong here: MIN_LENGTH is not needed, beause len >=0 |
| sImpl.setBounded(sImpl.getFacet(SchemaType.FACET_LENGTH) != null || |
| sImpl.getFacet(SchemaType.FACET_MAX_LENGTH) != null); |
| // BUGBUG: the schema spec is wrong here: finite cardinality requires item type is finite |
| sImpl.setFinite(sImpl.getListItemType().isFinite() && sImpl.isBounded()); |
| sImpl.setNumeric(false); |
| sImpl.setDecimalSize(SchemaType.NOT_DECIMAL); |
| break; |
| } |
| } |
| |
| private static class CodeForNameEntry |
| { |
| CodeForNameEntry(QName name, int code) |
| { this.name = name; this.code = code; } |
| public QName name; |
| public int code; |
| } |
| |
| private static CodeForNameEntry[] facetCodes = new CodeForNameEntry[] |
| { |
| new CodeForNameEntry(QNameHelper.forLNS("length", "http://www.w3.org/2001/XMLSchema"), SchemaType.FACET_LENGTH), |
| new CodeForNameEntry(QNameHelper.forLNS("minLength", "http://www.w3.org/2001/XMLSchema"), SchemaType.FACET_MIN_LENGTH), |
| new CodeForNameEntry(QNameHelper.forLNS("maxLength", "http://www.w3.org/2001/XMLSchema"), SchemaType.FACET_MAX_LENGTH), |
| new CodeForNameEntry(QNameHelper.forLNS("pattern", "http://www.w3.org/2001/XMLSchema"), SchemaType.FACET_PATTERN), |
| new CodeForNameEntry(QNameHelper.forLNS("enumeration", "http://www.w3.org/2001/XMLSchema"), SchemaType.FACET_ENUMERATION), |
| new CodeForNameEntry(QNameHelper.forLNS("whiteSpace", "http://www.w3.org/2001/XMLSchema"), SchemaType.FACET_WHITE_SPACE), |
| new CodeForNameEntry(QNameHelper.forLNS("maxInclusive", "http://www.w3.org/2001/XMLSchema"), SchemaType.FACET_MAX_INCLUSIVE), |
| new CodeForNameEntry(QNameHelper.forLNS("maxExclusive", "http://www.w3.org/2001/XMLSchema"), SchemaType.FACET_MAX_EXCLUSIVE), |
| new CodeForNameEntry(QNameHelper.forLNS("minInclusive", "http://www.w3.org/2001/XMLSchema"), SchemaType.FACET_MIN_INCLUSIVE), |
| new CodeForNameEntry(QNameHelper.forLNS("minExclusive", "http://www.w3.org/2001/XMLSchema"), SchemaType.FACET_MIN_EXCLUSIVE), |
| new CodeForNameEntry(QNameHelper.forLNS("totalDigits", "http://www.w3.org/2001/XMLSchema"), SchemaType.FACET_TOTAL_DIGITS), |
| new CodeForNameEntry(QNameHelper.forLNS("fractionDigits", "http://www.w3.org/2001/XMLSchema"), SchemaType.FACET_FRACTION_DIGITS), |
| }; |
| |
| private static final Map facetCodeMap = buildFacetCodeMap(); |
| |
| private static Map buildFacetCodeMap() |
| { |
| Map result = new HashMap(); |
| for (int i = 0; i < facetCodes.length; i++) |
| result.put(facetCodes[i].name, new Integer(facetCodes[i].code)); |
| return result; |
| } |
| |
| private static int translateFacetCode(QName name) |
| { |
| Integer result = ((Integer)facetCodeMap.get(name)); |
| if (result == null) |
| return -1; |
| return result.intValue(); |
| } |
| } |