001    package com.thaiopensource.datatype.xsd;
002    
003    import java.util.Hashtable;
004    import java.util.Enumeration;
005    
006    import com.thaiopensource.util.Service;
007    import com.thaiopensource.datatype.xsd.regex.RegexEngine;
008    import com.thaiopensource.datatype.xsd.regex.RegexSyntaxException;
009    
010    import org.relaxng.datatype.DatatypeLibrary;
011    import org.relaxng.datatype.Datatype;
012    import org.relaxng.datatype.DatatypeException;
013    import org.relaxng.datatype.DatatypeBuilder;
014    
015    public class DatatypeLibraryImpl implements DatatypeLibrary {
016      private final Hashtable typeTable = new Hashtable();
017      private final RegexEngine regexEngine;
018    
019      static private final String LONG_MAX = "9223372036854775807";
020      static private final String LONG_MIN = "-9223372036854775808";
021      static private final String INT_MAX = "2147483647";
022      static private final String INT_MIN = "-2147483648";
023      static private final String SHORT_MAX = "32767";
024      static private final String SHORT_MIN = "-32768";
025      static private final String BYTE_MAX = "127";
026      static private final String BYTE_MIN = "-128";
027    
028      static private final String UNSIGNED_LONG_MAX = "18446744073709551615";
029      static private final String UNSIGNED_INT_MAX = "4294967295";
030      static private final String UNSIGNED_SHORT_MAX = "65535";
031      static private final String UNSIGNED_BYTE_MAX = "255";
032      // Follow RFC 3066 syntax.
033      static private final String LANGUAGE_PATTERN = "[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*";
034    
035      public DatatypeLibraryImpl() {
036        this.regexEngine = findRegexEngine();
037        typeTable.put("string", new StringDatatype());
038        typeTable.put("normalizedString", new CdataDatatype());
039        typeTable.put("token", new TokenDatatype());
040        typeTable.put("boolean", new BooleanDatatype());
041    
042        DatatypeBase decimalType = new DecimalDatatype();
043        typeTable.put("decimal", decimalType);
044        DatatypeBase integerType = new IntegerRestrictDatatype(decimalType);
045        typeTable.put("integer", integerType);
046        typeTable.put("nonPositiveInteger", restrictMax(integerType, "0"));
047        typeTable.put("negativeInteger", restrictMax(integerType, "-1"));
048        typeTable.put("long", restrictMax(restrictMin(integerType, LONG_MIN), LONG_MAX));
049        typeTable.put("int", restrictMax(restrictMin(integerType, INT_MIN), INT_MAX));
050        typeTable.put("short", restrictMax(restrictMin(integerType, SHORT_MIN), SHORT_MAX));
051        typeTable.put("byte", restrictMax(restrictMin(integerType, BYTE_MIN), BYTE_MAX));
052        DatatypeBase nonNegativeIntegerType = restrictMin(integerType, "0");
053        typeTable.put("nonNegativeInteger", nonNegativeIntegerType);
054        typeTable.put("unsignedLong", restrictMax(nonNegativeIntegerType, UNSIGNED_LONG_MAX));
055        typeTable.put("unsignedInt", restrictMax(nonNegativeIntegerType, UNSIGNED_INT_MAX));
056        typeTable.put("unsignedShort", restrictMax(nonNegativeIntegerType, UNSIGNED_SHORT_MAX));
057        typeTable.put("unsignedByte", restrictMax(nonNegativeIntegerType, UNSIGNED_BYTE_MAX));
058        typeTable.put("positiveInteger", restrictMin(integerType, "1"));
059        typeTable.put("double", new DoubleDatatype());
060        typeTable.put("float", new FloatDatatype());
061    
062        typeTable.put("Name", new NameDatatype());
063        typeTable.put("QName", new QNameDatatype());
064    
065        DatatypeBase ncNameType = new NCNameDatatype();
066        typeTable.put("NCName", ncNameType);
067    
068        DatatypeBase nmtokenDatatype = new NmtokenDatatype();
069        typeTable.put("NMTOKEN", nmtokenDatatype);
070        typeTable.put("NMTOKENS", list(nmtokenDatatype));
071    
072        typeTable.put("ID", new IdDatatype());
073        DatatypeBase idrefType = new IdrefDatatype();
074        typeTable.put("IDREF", idrefType);
075        typeTable.put("IDREFS", list(idrefType));
076    
077        typeTable.put("NOTATION", new QNameDatatype());
078    
079        typeTable.put("base64Binary", new Base64BinaryDatatype());
080        typeTable.put("hexBinary", new HexBinaryDatatype());
081        typeTable.put("anyURI", new AnyUriDatatype());
082        typeTable.put("language", new RegexDatatype(LANGUAGE_PATTERN));
083    
084        typeTable.put("dateTime", new DateTimeDatatype("Y-M-DTt"));
085        typeTable.put("time", new DateTimeDatatype("t"));
086        typeTable.put("date", new DateTimeDatatype("Y-M-D"));
087        typeTable.put("gYearMonth", new DateTimeDatatype("Y-M"));
088        typeTable.put("gYear", new DateTimeDatatype("Y"));
089        typeTable.put("gMonthDay", new DateTimeDatatype("--M-D"));
090        typeTable.put("gDay", new DateTimeDatatype("---D"));
091        typeTable.put("gMonth", new DateTimeDatatype("--M"));
092    
093        DatatypeBase entityType = new EntityDatatype();
094        typeTable.put("ENTITY", entityType);
095        typeTable.put("ENTITIES", list(entityType));
096        // Partially implemented
097        typeTable.put("duration", new DurationDatatype());
098      }
099    
100      public DatatypeBuilder createDatatypeBuilder(String localName) throws DatatypeException {
101        DatatypeBase base = (DatatypeBase)typeTable.get(localName);
102        if (base == null)
103          throw new DatatypeException();
104        if (base instanceof RegexDatatype) {
105          try {
106            ((RegexDatatype)base).compile(getRegexEngine());
107          }
108          catch (RegexSyntaxException e) {
109            throw new DatatypeException(DatatypeBuilderImpl.localizer.message("regex_internal_error", localName));
110          }
111        }
112        return new DatatypeBuilderImpl(this, base);
113      }
114    
115      RegexEngine getRegexEngine() throws DatatypeException {
116        if (regexEngine == null)
117          throw new DatatypeException(DatatypeBuilderImpl.localizer.message("regex_impl_not_found"));
118        return regexEngine;
119      }
120    
121      private static DatatypeBase restrictMax(DatatypeBase base, String limit) {
122        return new MaxInclusiveRestrictDatatype(base, base.getValue(limit, null));
123      }
124    
125      private static DatatypeBase restrictMin(DatatypeBase base, String limit) {
126        return new MinInclusiveRestrictDatatype(base, base.getValue(limit, null));
127      }
128    
129      private static DatatypeBase list(DatatypeBase base) {
130        return new MinLengthRestrictDatatype(new ListDatatype(base), 1);
131      }
132    
133      private static RegexEngine findRegexEngine() {
134        Enumeration e = new Service(RegexEngine.class).getProviders();
135        if (!e.hasMoreElements())
136          return null;
137        return (RegexEngine)e.nextElement();
138      }
139    
140      public Datatype createDatatype(String type) throws DatatypeException {
141        return createDatatypeBuilder(type).createDatatype();
142      }
143    
144      static public void main(String[] args) throws DatatypeException {
145        System.err.println(new DatatypeLibraryImpl().createDatatype(args[0]).isValid(args[1], null)
146                           ? "valid"
147                           : "invalid");
148      }
149    }