blob: e43c9fbe0be3a2ae30b214796f897e606866632e [file] [log] [blame]
/* 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 org.apache.xmlbeans.*;
import org.apache.xmlbeans.impl.common.QNameHelper;
import org.apache.xmlbeans.impl.regex.ParseException;
import org.apache.xmlbeans.impl.regex.RegularExpression;
import org.apache.xmlbeans.impl.values.XmlValueOutOfRangeException;
import org.apache.xmlbeans.impl.xb.xsdschema.*;
import org.apache.xmlbeans.impl.xb.xsdschema.SchemaDocument.Schema;
import javax.xml.namespace.QName;
import java.math.BigInteger;
import java.util.*;
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();
Schema schema = StscComplexTypeResolver.getSchema(parseSt);
// 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.",
XmlErrorCodes.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.",
XmlErrorCodes.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;
Object finalValue = null;
if (parseSt.isSetFinal()) {
finalValue = parseSt.getFinal();
}
// Inspect the finalDefault attribute on the schema
else if (schema != null && schema.isSetFinalDefault()) {
finalValue = schema.getFinalDefault();
}
if (finalValue != null) {
if (finalValue instanceof String) {
if ("#all".equals(finalValue)) {
finalRest = finalList = finalUnion = true;
}
} else if (finalValue instanceof List) {
@SuppressWarnings("unchecked")
List<String> lFinalValue = (List<String>) finalValue;
if (lFinalValue.contains("restriction")) {
finalRest = true;
}
if (lFinalValue.contains("list")) {
finalList = true;
}
if (lFinalValue.contains("union")) {
finalUnion = true;
}
}
}
sImpl.setSimpleFinal(finalRest, finalList, finalUnion);
List<SchemaType> 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(List<? extends SchemaType> typeList) {
return typeList.stream().map(SchemaType::getRef).toArray(SchemaType.Ref[]::new);
}
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.apache.xmlbeans.impl.xb.xsdschema.ListDocument.List parseList, List<SchemaType> 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()) {
state.error(XmlErrorCodes.SCHEMA_REDEFINE$EXTEND_OR_RESTRICT,
new Object[]{"list"}, parseList);
// recovery: oh well.
}
QName itemName = parseList.getItemType();
LocalSimpleType parseInner = parseList.getSimpleType();
if (itemName != null && parseInner != null) {
state.error(XmlErrorCodes.SCHEMA_SIMPLE_TYPE$LIST_HAS_BOTH_ITEM_OR_SIMPLE_TYPE, null, parseList);
// recovery: ignore the inner simple type.
parseInner = null;
}
SchemaTypeImpl itemImpl;
XmlObject errorLoc;
if (itemName != null) {
itemImpl = state.findGlobalType(itemName, sImpl.getChameleonNamespace(), sImpl.getTargetNamespace());
errorLoc = parseList.xgetItemType();
if (itemImpl == null) {
state.notFoundError(itemName, SchemaType.TYPE, parseList.xgetItemType(), true);
// recovery: treat it as a list of anySimpleType
itemImpl = BuiltinSchemaTypeSystem.ST_ANY_SIMPLE;
}
} else if (parseInner != null) {
itemImpl = StscTranslator.translateAnonymousSimpleType(parseInner,
sImpl.getTargetNamespace(), sImpl.getChameleonNamespace() != null,
sImpl.getElemFormDefault(), sImpl.getAttFormDefault(), anonTypes, sImpl);
errorLoc = parseInner;
} else {
state.error(XmlErrorCodes.SCHEMA_SIMPLE_TYPE$LIST_HAS_NEITHER_ITEM_OR_SIMPLE_TYPE, null, parseList);
// recovery: treat it as an extension of anySimpleType
resolveErrorSimpleType(sImpl);
return;
}
// Verify final restrictions
if (itemImpl.finalList()) {
state.error(XmlErrorCodes.SIMPLE_TYPE_PROPERTIES$LIST_FINAL, null, parseList);
}
// Recursion...
StscResolver.resolveType(itemImpl);
if (!itemImpl.isSimpleType()) {
state.error(XmlErrorCodes.SIMPLE_TYPE_RESTRICTION$LIST_ITEM_NOT_SIMPLE, null, errorLoc);
// recovery: treat the item type as anySimpleType
sImpl = BuiltinSchemaTypeSystem.ST_ANY_SIMPLE;
}
switch (itemImpl.getSimpleVariety()) {
case SchemaType.LIST:
state.error(XmlErrorCodes.SIMPLE_TYPE_RESTRICTION$LIST_ITEM_IS_LIST, null, errorLoc);
// recovery: treat the list as an anySimpleType
resolveErrorSimpleType(sImpl);
return;
case SchemaType.UNION:
if (itemImpl.isUnionOfLists()) {
state.error(XmlErrorCodes.SIMPLE_TYPE_RESTRICTION$LIST_ITEM_IS_UNION_OF_LIST, null, errorLoc);
resolveErrorSimpleType(sImpl);
return;
}
// fallthrough: nonlist unions are just like atomic items
case SchemaType.ATOMIC:
sImpl.setListItemTypeRef(itemImpl.getRef());
// Check that the item type is not a plan NOTATION
if (sImpl.getBuiltinTypeCode() == SchemaType.BTC_NOTATION) {
state.recover(XmlErrorCodes.DATATYPE_ENUM_NOTATION, null, errorLoc);
}
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<SchemaType> 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()) {
state.error(XmlErrorCodes.SCHEMA_REDEFINE$EXTEND_OR_RESTRICT,
new Object[]{"union"}, parseUnion);
// recovery: oh well.
}
@SuppressWarnings("unchecked")
List<QName> memberTypes = (List<QName>) parseUnion.getMemberTypes();
SimpleType[] simpleTypes = parseUnion.getSimpleTypeArray();
List<SchemaTypeImpl> memberImplList = new ArrayList<>();
if (simpleTypes.length == 0 && (memberTypes == null || memberTypes.size() == 0)) {
state.error(XmlErrorCodes.SCHEMA_SIMPLE_TYPE$UNION_HAS_MEMBER_TYPES_OR_SIMPLE_TYPES, null, parseUnion);
// recovery: oh well, zero member types is fine.
}
if (memberTypes != null) {
for (QName mName : memberTypes) {
SchemaTypeImpl memberImpl = state.findGlobalType(mName, sImpl.getChameleonNamespace(), sImpl.getTargetNamespace());
if (memberImpl == null)
// recovery: skip member
{
state.notFoundError(mName, SchemaType.TYPE, parseUnion.xgetMemberTypes(), true);
} else {
memberImplList.add(memberImpl);
}
}
}
for (int i = 0; i < simpleTypes.length; i++) {
// BUGBUG: see if non<simpleType> children can leak through
SchemaTypeImpl mImpl = StscTranslator.translateAnonymousSimpleType(simpleTypes[i],
sImpl.getTargetNamespace(), sImpl.getChameleonNamespace() != null,
sImpl.getElemFormDefault(), sImpl.getAttFormDefault(), anonTypes, sImpl);
memberImplList.add(mImpl);
mImpl.setAnonymousUnionMemberOrdinal(i + 1);
}
// Recurse and resolve all member types
for (Iterator<SchemaTypeImpl> mImpls = memberImplList.iterator(); mImpls.hasNext(); ) {
SchemaTypeImpl mImpl = mImpls.next();
if (!StscResolver.resolveType(mImpl)) {
// KHK: review
String memberName = "";
XmlObject errorLoc;
if (Objects.equals(mImpl.getOuterType(), sImpl)) {
errorLoc = mImpl.getParseObject();
} else {
memberName = QNameHelper.pretty(mImpl.getName()) + " ";
errorLoc = parseUnion.xgetMemberTypes();
}
state.error(XmlErrorCodes.SCHEMA_SIMPLE_TYPE$CYCLIC_UNION, new Object[]{memberName}, errorLoc);
// recovery: ignore the errant union member
mImpls.remove();
}
}
// Now verify members
boolean isUnionOfLists = false;
for (Iterator<SchemaTypeImpl> mImpls = memberImplList.iterator(); mImpls.hasNext(); ) {
SchemaTypeImpl mImpl = mImpls.next();
if (!mImpl.isSimpleType()) {
// KHK: review
String memberName = "";
XmlObject errorLoc;
if (mImpl.getOuterType() != null && mImpl.getOuterType().equals(sImpl)) {
errorLoc = mImpl.getParseObject();
} else {
memberName = QNameHelper.pretty(mImpl.getName()) + " ";
errorLoc = parseUnion.xgetMemberTypes();
}
state.error(XmlErrorCodes.SIMPLE_TYPE_RESTRICTION$UNION_MEMBER_NOT_SIMPLE, new Object[]{memberName}, errorLoc);
// 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 (SchemaTypeImpl schemaType : memberImplList) {
if (schemaType.finalUnion()) {
state.error(XmlErrorCodes.SIMPLE_TYPE_PROPERTIES$UNION_FINAL, null, 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<SchemaType> anonTypes) {
QName baseName = parseRestr.getBase();
SimpleType parseInner = parseRestr.getSimpleType();
StscState state = StscState.get();
if (baseName != null && parseInner != null) {
state.error(XmlErrorCodes.SCHEMA_SIMPLE_TYPE$RESTRICTION_HAS_BOTH_BASE_OR_SIMPLE_TYPE, null, 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);
if (baseImpl != null && !baseImpl.getName().equals(sImpl.getName())) {
state.error(XmlErrorCodes.SCHEMA_REDEFINE$SAME_TYPE,
new Object[]{"<simpleType>",
QNameHelper.pretty(baseName),
QNameHelper.pretty(sImpl.getName())
},
parseRestr);
}
} else {
baseImpl = state.findGlobalType(baseName, sImpl.getChameleonNamespace(), sImpl.getTargetNamespace());
}
if (baseImpl == null) {
state.notFoundError(baseName, SchemaType.TYPE, parseRestr.xgetBase(), true);
// recovery: treat it as an extension of anySimpleType
baseImpl = BuiltinSchemaTypeSystem.ST_ANY_SIMPLE;
}
} else if (parseInner != null) {
if (sImpl.isRedefinition()) {
StscState.get().error(XmlErrorCodes.SCHEMA_REDEFINE$EXTEND_OR_RESTRICT,
new Object[]{"<simpleType>"}, parseInner);
// recovery: oh well.
}
baseImpl = StscTranslator.translateAnonymousSimpleType(parseInner,
sImpl.getTargetNamespace(), sImpl.getChameleonNamespace() != null,
sImpl.getElemFormDefault(), sImpl.getAttFormDefault(), anonTypes, sImpl);
} else {
state.error(XmlErrorCodes.SCHEMA_SIMPLE_TYPE$RESTRICTION_HAS_NEITHER_BASE_OR_SIMPLE_TYPE, null, 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(XmlErrorCodes.SIMPLE_TYPE_PROPERTIES$RESTRICTION_FINAL, null, parseRestr);
}
sImpl.setBaseTypeRef(baseImpl.getRef());
sImpl.setBaseDepth(baseImpl.getBaseDepth() + 1);
sImpl.setDerivationType(SchemaType.DT_RESTRICTION);
if (!baseImpl.isSimpleType()) {
state.error(XmlErrorCodes.SIMPLE_TYPE_RESTRICTION$ATOMIC_NOT_SIMPLE, null, 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;
}
// KHK: s4s
StscState.get().error("Unrecognized whitespace value \"" + textval + "\"", XmlErrorCodes.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<XmlAnySimpleType> enumeratedValues = null;
List<RegularExpression> patterns = null;
if (restriction != null) {
XmlCursor cur = restriction.newCursor();
for (boolean more = cur.toFirstChild(); more; more = cur.toNextSibling()) {
QName facetQName = cur.getName();
String facetName = facetQName.getLocalPart();
int code = translateFacetCode(facetQName);
if (code == -1) {
continue;
}
Facet facet = (Facet) cur.getObject();
if (!facetAppliesToType(code, baseImpl)) {
state.error(XmlErrorCodes.FACETS_APPLICABLE,
new Object[]{facetName, QNameHelper.pretty(baseImpl.getName())}, facet);
continue;
} else if (baseImpl.getSimpleVariety() == SchemaType.ATOMIC &&
baseImpl.getPrimitiveType().getBuiltinTypeCode() == SchemaType.BTC_NOTATION
&& (code == SchemaType.FACET_LENGTH || code == SchemaType.FACET_MIN_LENGTH ||
code == SchemaType.FACET_MAX_LENGTH)) {
state.warning(XmlErrorCodes.FACETS_DEPRECATED_NOTATION,
new Object[]{facetName, QNameHelper.pretty(baseImpl.getName())}, facet);
}
if (seenFacet[code] && !isMultipleFacet(code)) {
state.error(XmlErrorCodes.DATATYPE_SINGLE_FACET_VALUE, null, 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(XmlErrorCodes.DATATYPE_LENGTH, null, facet);
// continue;
// }
XmlInteger len = StscTranslator.buildNnInteger(facet.getValue());
if (len == null) {
state.error("Must be a nonnegative integer", XmlErrorCodes.FACET_VALUE_MALFORMED, facet);
continue;
}
if (fixedFacets[code] && !myFacets[code].valueEquals(len)) {
state.error(XmlErrorCodes.FACET_FIXED, new Object[]{facetName}, facet);
continue;
}
if (myFacets[SchemaType.FACET_MIN_LENGTH] != null) {
// An error for 'length' and 'minLength' to be specified at the same time
// except if the base type had the same value for 'minLength' also
XmlAnySimpleType baseMinLength = baseImpl.getFacet(SchemaType.FACET_MIN_LENGTH);
if (!(baseMinLength != null &&
baseMinLength.valueEquals(myFacets[SchemaType.FACET_MIN_LENGTH]) &&
baseMinLength.compareValue(len) <= 0)) {
state.error(XmlErrorCodes.DATATYPE_LENGTH, null, facet);
continue;
}
}
if (myFacets[SchemaType.FACET_MAX_LENGTH] != null) {
// An error for 'length' and 'maxLength' to be specified at the same time
// except if the base type had the same value for 'maxLength' also
XmlAnySimpleType baseMaxLength = baseImpl.getFacet(SchemaType.FACET_MAX_LENGTH);
if (!(baseMaxLength != null &&
baseMaxLength.valueEquals(myFacets[SchemaType.FACET_MAX_LENGTH]) &&
baseMaxLength.compareValue(len) >= 0)) {
state.error(XmlErrorCodes.DATATYPE_LENGTH, null, facet);
continue;
}
}
myFacets[code] = len;
break;
case SchemaType.FACET_MIN_LENGTH:
case SchemaType.FACET_MAX_LENGTH:
XmlInteger mlen = StscTranslator.buildNnInteger(facet.getValue());
if (mlen == null) {
state.error("Must be a nonnegative integer", XmlErrorCodes.FACET_VALUE_MALFORMED, facet);
continue;
}
if (fixedFacets[code] && !myFacets[code].valueEquals(mlen)) {
state.error(XmlErrorCodes.FACET_FIXED, new Object[]{facetName}, facet);
continue;
}
if (myFacets[SchemaType.FACET_LENGTH] != null) {
// It's an error for 'length' and 'minLength'/'maxLength' to be
// specified at the same time, except for the case when
// the base type had the same value for 'minLength'/'maxLength'
// and the two values are consistent
XmlAnySimpleType baseMinMaxLength = baseImpl.getFacet(code);
if (!(baseMinMaxLength != null &&
baseMinMaxLength.valueEquals(mlen) &&
(code == SchemaType.FACET_MIN_LENGTH ?
baseMinMaxLength.compareTo(myFacets[SchemaType.FACET_LENGTH]) <= 0 :
baseMinMaxLength.compareTo(myFacets[SchemaType.FACET_LENGTH]) >= 0))) {
state.error(XmlErrorCodes.DATATYPE_LENGTH, null, facet);
continue;
}
}
if (myFacets[SchemaType.FACET_MAX_LENGTH] != null) {
if (mlen.compareValue(myFacets[SchemaType.FACET_MAX_LENGTH]) > 0) {
state.error(XmlErrorCodes.DATATYPE_MAX_LENGTH_RESTRICTION, null, facet);
continue;
}
}
if (myFacets[SchemaType.FACET_MIN_LENGTH] != null) {
if (mlen.compareValue(myFacets[SchemaType.FACET_MIN_LENGTH]) < 0) {
state.error(XmlErrorCodes.DATATYPE_MIN_LENGTH_RESTRICTION, null, facet);
continue;
}
}
myFacets[code] = mlen;
break;
case SchemaType.FACET_TOTAL_DIGITS:
XmlPositiveInteger dig = StscTranslator.buildPosInteger(facet.getValue());
if (dig == null) {
state.error("Must be a positive integer", XmlErrorCodes.FACET_VALUE_MALFORMED, facet);
break;
}
if (fixedFacets[code] && !myFacets[code].valueEquals(dig)) {
state.error(XmlErrorCodes.FACET_FIXED, new Object[]{facetName}, facet);
continue;
}
if (myFacets[SchemaType.FACET_TOTAL_DIGITS] != null) {
if (dig.compareValue(myFacets[SchemaType.FACET_TOTAL_DIGITS]) > 0) {
state.error(XmlErrorCodes.DATATYPE_TOTAL_DIGITS_RESTRICTION, null, facet);
}
}
myFacets[code] = dig;
break;
case SchemaType.FACET_FRACTION_DIGITS:
XmlNonNegativeInteger fdig = StscTranslator.buildNnInteger(facet.getValue());
if (fdig == null) {
state.error("Must be a nonnegative integer", XmlErrorCodes.FACET_VALUE_MALFORMED, facet);
break;
}
if (fixedFacets[code] && !myFacets[code].valueEquals(fdig)) {
state.error(XmlErrorCodes.FACET_FIXED, new Object[]{facetName}, facet);
continue;
}
if (myFacets[SchemaType.FACET_FRACTION_DIGITS] != null) {
if (fdig.compareValue(myFacets[SchemaType.FACET_FRACTION_DIGITS]) > 0) {
state.error(XmlErrorCodes.DATATYPE_FRACTION_DIGITS_RESTRICTION, null, facet);
}
}
if (myFacets[SchemaType.FACET_TOTAL_DIGITS] != null) {
if (fdig.compareValue(myFacets[SchemaType.FACET_TOTAL_DIGITS]) > 0) {
state.error(XmlErrorCodes.DATATYPE_FRACTION_DIGITS_LE_TOTAL_DIGITS, null, facet);
}
}
myFacets[code] = fdig;
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", XmlErrorCodes.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(), true);
} catch (XmlValueOutOfRangeException e) {
// note: this guarantees that the limit is a valid number in the
// base data type!!
switch (code) {
case SchemaType.FACET_MIN_EXCLUSIVE:
state.error(XmlErrorCodes.DATATYPE_MIN_EXCLUSIVE_RESTRICTION,
new Object[]{e.getMessage()}, facet);
break;
case SchemaType.FACET_MIN_INCLUSIVE:
state.error(XmlErrorCodes.DATATYPE_MIN_INCLUSIVE_RESTRICTION,
new Object[]{e.getMessage()}, facet);
break;
case SchemaType.FACET_MAX_INCLUSIVE:
state.error(XmlErrorCodes.DATATYPE_MAX_INCLUSIVE_RESTRICTION,
new Object[]{e.getMessage()}, facet);
break;
case SchemaType.FACET_MAX_EXCLUSIVE:
state.error(XmlErrorCodes.DATATYPE_MAX_EXCLUSIVE_RESTRICTION,
new Object[]{e.getMessage()}, facet);
break;
}
// 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(XmlErrorCodes.FACET_FIXED, new Object[]{facetName}, facet);
continue;
}
if (myFacets[code] != null) {
SchemaType limitSType = limit.schemaType();
if (limitSType != null && !limitSType.isSimpleType() &&
limitSType.getContentType() == SchemaType.SIMPLE_CONTENT) {
// in the case of complex types with simple content that has facets
// we need to compare values based on the content type
limit = baseImpl.getContentBasedOnType().newValue(facet.getValue());
}
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"),
XmlErrorCodes.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(XmlErrorCodes.DATATYPE_WHITESPACE_RESTRICTION, null, facet);
continue;
}
myFacets[code] = StscState.build_wsstring(wsr).get();
break;
case SchemaType.FACET_ENUMERATION:
XmlAnySimpleType enumval;
try {
enumval = baseImpl.newValue(facet.getValue(), true);
// enumval.set(facet.getValue());
// ((XmlObjectBase)enumval).setImmutable();
} catch (XmlValueOutOfRangeException e) {
state.error(XmlErrorCodes.DATATYPE_ENUM_RESTRICTION, new Object[]{facet.getValue().getStringValue(), e.getMessage()}, facet);
continue;
}
if (enumeratedValues == null) {
enumeratedValues = new ArrayList<>();
}
enumeratedValues.add(enumval);
break;
case SchemaType.FACET_PATTERN:
RegularExpression p;
try {
p = new RegularExpression(facet.getValue().getStringValue(), "X");
} catch (ParseException e) {
state.error(XmlErrorCodes.PATTERN_REGEX, new Object[]{facet.getValue().getStringValue(), e.getMessage()}, 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(enumeratedValues.toArray(new XmlAnySimpleType[0])));
SchemaType beType = sImpl;
if (sImpl.isRedefinition()) {
beType = sImpl.getBaseType().getBaseEnumType();
if (beType == null || sImpl.getBaseType() == beType) {
beType = sImpl;
}
} else if (sImpl.getBaseType().getBaseEnumType() != null) {
beType = sImpl.getBaseType().getBaseEnumType();
}
sImpl.setBaseEnumTypeRef(beType.getRef());
} else {
sImpl.copyEnumerationValues(baseImpl);
}
// store the pattern list
RegularExpression[] patternArray = patterns != null ? patterns.toArray(EMPTY_REGEX_ARRAY) : EMPTY_REGEX_ARRAY;
sImpl.setPatternFacet((patternArray.length > 0 || baseImpl.hasPatternFacet()));
sImpl.setPatterns(patternArray);
// Check that, if the base type is NOTATION, there is an enumeration facet
// http://www.w3.org/TR/xmlschema-2/#NOTATION
if (baseImpl.getBuiltinTypeCode() == SchemaType.BTC_NOTATION) {
if (sImpl.getEnumerationValues() == null) {
state.recover(XmlErrorCodes.DATATYPE_ENUM_NOTATION, null, restriction);
}
}
}
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 RegularExpression[] EMPTY_REGEX_ARRAY = new 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 ignored) {
}
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 (SchemaType mType : mTypes) {
if (mType.ordered() != SchemaType.UNORDERED) {
ordered = SchemaType.PARTIAL_ORDER;
}
if (!mType.isBounded()) {
isBounded = false;
}
if (!mType.isFinite()) {
isFinite = false;
}
if (!mType.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 final CodeForNameEntry[] facetCodes = {
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<QName, Integer> facetCodeMap = buildFacetCodeMap();
private static Map<QName, Integer> buildFacetCodeMap() {
Map<QName, Integer> result = new HashMap<>();
for (CodeForNameEntry facetCode : facetCodes) {
result.put(facetCode.name, facetCode.code);
}
return result;
}
private static int translateFacetCode(QName name) {
return facetCodeMap.getOrDefault(name, -1);
}
}