package com.thaiopensource.datatype.xsd; import com.thaiopensource.datatype.xsd.regex.RegexSyntaxException; import com.thaiopensource.util.Localizer; import org.relaxng.datatype.Datatype; import org.relaxng.datatype.DatatypeBuilder; import org.relaxng.datatype.DatatypeException; import org.relaxng.datatype.ValidationContext; class DatatypeBuilderImpl implements DatatypeBuilder { static final Localizer localizer = new Localizer(DatatypeBuilderImpl.class); private DatatypeBase base; private final DatatypeLibraryImpl library; DatatypeBuilderImpl(DatatypeLibraryImpl library, DatatypeBase base) throws DatatypeException { this.library = library; this.base = base; } public void addParameter(String name, String value, ValidationContext context) throws DatatypeException { if (name.equals("pattern")) addPatternParam(value); else if (name.equals("minInclusive")) addMinInclusiveParam(value, context); else if (name.equals("maxInclusive")) addMaxInclusiveParam(value, context); else if (name.equals("minExclusive")) addMinExclusiveParam(value, context); else if (name.equals("maxExclusive")) addMaxExclusiveParam(value, context); else if (name.equals("length")) addLengthParam(value); else if (name.equals("minLength")) addMinLengthParam(value); else if (name.equals("maxLength")) addMaxLengthParam(value); else if (name.equals("fractionDigits")) addScaleParam(value); else if (name.equals("totalDigits")) addPrecisionParam(value); else if (name.equals("enumeration")) error("enumeration_param"); else if (name.equals("whiteSpace")) error("whiteSpace_param"); else error("unrecognized_param", name); } private void addPatternParam(String value) throws DatatypeException { try { base = new PatternRestrictDatatype(base, library.getRegexEngine().compile(value), value); } catch (RegexSyntaxException e) { int pos = e.getPosition(); if (pos == RegexSyntaxException.UNKNOWN_POSITION) pos = DatatypeException.UNKNOWN; error("invalid_regex", e.getMessage(), pos); } } private void addMinInclusiveParam(String value, ValidationContext context) throws DatatypeException { base = new MinInclusiveRestrictDatatype(base, getLimit(value, context), value); } private void addMaxInclusiveParam(String value, ValidationContext context) throws DatatypeException { base = new MaxInclusiveRestrictDatatype(base, getLimit(value, context), value); } private void addMinExclusiveParam(String value, ValidationContext context) throws DatatypeException { base = new MinExclusiveRestrictDatatype(base, getLimit(value, context), value); } private void addMaxExclusiveParam(String value, ValidationContext context) throws DatatypeException { base = new MaxExclusiveRestrictDatatype(base, getLimit(value, context), value); } private Object getLimit(String str, ValidationContext context) throws DatatypeException { if (base.getOrderRelation() == null) error("not_ordered"); str = base.normalizeWhiteSpace(str); try { base.checkLexicallyAllows(str); return base.getValue(str, context); } catch (DatatypeException e) { throw new DatatypeException(localizer.message("invalid_limit", str, e.getMessage())); } } private void addLengthParam(String value) throws DatatypeException { base = new LengthRestrictDatatype(base, getLength(value)); } private void addMinLengthParam(String value) throws DatatypeException { base = new MinLengthRestrictDatatype(base, getLength(value)); } private void addMaxLengthParam(String value) throws DatatypeException { base = new MaxLengthRestrictDatatype(base, getLength(value)); } private int getLength(String str) throws DatatypeException { if (base.getMeasure() == null) error("no_length"); int len = convertNonNegativeInteger(str); if (len < 0) error("length_not_non_negative_integer"); return len; } private void addScaleParam(String str) throws DatatypeException { if (!(base.getPrimitive() instanceof DecimalDatatype)) error("scale_not_derived_from_decimal"); int scale = convertNonNegativeInteger(str); if (scale < 0) error("scale_not_non_negative_integer"); base = new ScaleRestrictDatatype(base, scale); } private void addPrecisionParam(String str) throws DatatypeException { if (!(base.getPrimitive() instanceof DecimalDatatype)) error("precision_not_derived_from_decimal"); int scale = convertNonNegativeInteger(str); if (scale <= 0) error("precision_not_positive_integer"); base = new PrecisionRestrictDatatype(base, scale); } public Datatype createDatatype() { return base; } private static void error(String key) throws DatatypeException { throw new DatatypeException(localizer.message(key)); } private static void error(String key, String arg) throws DatatypeException { throw new DatatypeException(localizer.message(key, arg)); } private static void error(String key, String arg, int pos) throws DatatypeException { throw new DatatypeException(pos, localizer.message(key, arg)); } // Return -1 for anything that is not a nonNegativeInteger // Return Integer.MAX_VALUE for values that are too big private int convertNonNegativeInteger(String str) { str = str.trim(); DecimalDatatype decimal = new DecimalDatatype(); if (!decimal.lexicallyAllows(str)) return -1; // Canonicalize the value str = decimal.getValue(str, null).toString(); // Reject negative and fractional numbers if (str.charAt(0) == '-' || str.indexOf('.') >= 0) return -1; try { return Integer.parseInt(str); } catch (NumberFormatException e) { // Map out of range integers to MAX_VALUE return Integer.MAX_VALUE; } } }