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 }