in src/main/java/org/apache/xmlbeans/impl/schema/StscSimpleTypeResolver.java [576:898]
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) {
try (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);
}
}
}