001 package com.thaiopensource.datatype.xsd;
002
003 import org.relaxng.datatype.Datatype;
004 import org.relaxng.datatype.DatatypeBuilder;
005 import org.relaxng.datatype.DatatypeException;
006 import org.relaxng.datatype.ValidationContext;
007
008 import com.thaiopensource.datatype.xsd.regex.RegexSyntaxException;
009 import com.thaiopensource.util.Localizer;
010
011 class DatatypeBuilderImpl implements DatatypeBuilder {
012 static final Localizer localizer = new Localizer(DatatypeBuilderImpl.class);
013
014 private DatatypeBase base;
015 private final DatatypeLibraryImpl library;
016
017 DatatypeBuilderImpl(DatatypeLibraryImpl library, DatatypeBase base) throws DatatypeException {
018 this.library = library;
019 this.base = base;
020 }
021
022 public void addParameter(String name,
023 String value,
024 ValidationContext context) throws DatatypeException {
025 if (name.equals("pattern"))
026 addPatternParam(value);
027 else if (name.equals("minInclusive"))
028 addMinInclusiveParam(value, context);
029 else if (name.equals("maxInclusive"))
030 addMaxInclusiveParam(value, context);
031 else if (name.equals("minExclusive"))
032 addMinExclusiveParam(value, context);
033 else if (name.equals("maxExclusive"))
034 addMaxExclusiveParam(value, context);
035 else if (name.equals("length"))
036 addLengthParam(value);
037 else if (name.equals("minLength"))
038 addMinLengthParam(value);
039 else if (name.equals("maxLength"))
040 addMaxLengthParam(value);
041 else if (name.equals("fractionDigits"))
042 addScaleParam(value);
043 else if (name.equals("totalDigits"))
044 addPrecisionParam(value);
045 else if (name.equals("enumeration"))
046 error("enumeration_param");
047 else if (name.equals("whiteSpace"))
048 error("whiteSpace_param");
049 else
050 error("unrecognized_param", name);
051 }
052
053 private void addPatternParam(String value) throws DatatypeException {
054 try {
055 base = new PatternRestrictDatatype(base,
056 library.getRegexEngine().compile(value));
057 }
058 catch (RegexSyntaxException e) {
059 int pos = e.getPosition();
060 if (pos == RegexSyntaxException.UNKNOWN_POSITION)
061 pos = DatatypeException.UNKNOWN;
062 error("invalid_regex", e.getMessage(), pos);
063 }
064 }
065
066 private void addMinInclusiveParam(String value, ValidationContext context)
067 throws DatatypeException {
068 base = new MinInclusiveRestrictDatatype(base,
069 getLimit(value, context));
070 }
071
072 private void addMaxInclusiveParam(String value, ValidationContext context)
073 throws DatatypeException {
074 base = new MaxInclusiveRestrictDatatype(base,
075 getLimit(value, context));
076 }
077
078 private void addMinExclusiveParam(String value, ValidationContext context)
079 throws DatatypeException {
080 base = new MinExclusiveRestrictDatatype(base,
081 getLimit(value, context));
082 }
083
084 private void addMaxExclusiveParam(String value, ValidationContext context)
085 throws DatatypeException {
086 base = new MaxExclusiveRestrictDatatype(base,
087 getLimit(value, context));
088 }
089
090 private Object getLimit(String str, ValidationContext context)
091 throws DatatypeException {
092 if (base.getOrderRelation() == null)
093 error("not_ordered");
094 Object value = base.createValue(str, context);
095 if (value == null)
096 error("invalid_limit", str);
097 return value;
098 }
099
100 private void addLengthParam(String value) throws DatatypeException {
101 base = new LengthRestrictDatatype(base, getLength(value));
102 }
103
104 private void addMinLengthParam(String value) throws DatatypeException {
105 base = new MinLengthRestrictDatatype(base, getLength(value));
106 }
107
108 private void addMaxLengthParam(String value) throws DatatypeException {
109 base = new MaxLengthRestrictDatatype(base, getLength(value));
110 }
111
112 private int getLength(String str) throws DatatypeException {
113 if (base.getMeasure() == null)
114 error("no_length");
115 int len = convertNonNegativeInteger(str);
116 if (len < 0)
117 error("value_not_non_negative_integer");
118 return len;
119 }
120
121 private void addScaleParam(String str) throws DatatypeException {
122 if (!(base.getPrimitive() instanceof DecimalDatatype))
123 error("scale_not_derived_from_decimal");
124 int scale = convertNonNegativeInteger(str);
125 if (scale < 0)
126 error("value_not_non_negative_integer");
127 base = new ScaleRestrictDatatype(base, scale);
128 }
129
130 private void addPrecisionParam(String str) throws DatatypeException {
131 if (!(base.getPrimitive() instanceof DecimalDatatype))
132 error("precision_not_derived_from_decimal");
133 int scale = convertNonNegativeInteger(str);
134 if (scale <= 0)
135 error("value_not_positive_integer");
136 base = new PrecisionRestrictDatatype(base, scale);
137 }
138
139 public Datatype createDatatype() {
140 return base;
141 }
142
143 private static void error(String key) throws DatatypeException {
144 throw new DatatypeException(localizer.message(key));
145 }
146
147 private static void error(String key, String arg) throws DatatypeException {
148 throw new DatatypeException(localizer.message(key, arg));
149 }
150
151 private static void error(String key, String arg, int pos) throws DatatypeException {
152 throw new DatatypeException(pos, localizer.message(key, arg));
153 }
154
155 // Return -1 for anything that is not a nonNegativeInteger
156 // Return Integer.MAX_VALUE for values that are too big
157
158 private int convertNonNegativeInteger(String str) {
159 str = str.trim();
160 DecimalDatatype decimal = new DecimalDatatype();
161 if (!decimal.lexicallyAllows(str))
162 return -1;
163 // Canonicalize the value
164 str = decimal.getValue(str, null).toString();
165 // Reject negative and fractional numbers
166 if (str.charAt(0) == '-' || str.indexOf('.') >= 0)
167 return -1;
168 try {
169 return Integer.parseInt(str);
170 }
171 catch (NumberFormatException e) {
172 // Map out of range integers to MAX_VALUE
173 return Integer.MAX_VALUE;
174 }
175 }
176 }