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    }