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 }