package com.thaiopensource.datatype.xsd; import com.thaiopensource.datatype.xsd.regex.RegexEngine; import com.thaiopensource.datatype.xsd.regex.RegexSyntaxException; import org.relaxng.datatype.Datatype; import org.relaxng.datatype.DatatypeBuilder; import org.relaxng.datatype.DatatypeException; import org.relaxng.datatype.DatatypeLibrary; import java.util.HashMap; import java.util.Map; public class DatatypeLibraryImpl implements DatatypeLibrary { private final Map<String, DatatypeBase> typeMap = new HashMap<String, DatatypeBase>(); private final RegexEngine regexEngine; static private final String LONG_MAX = "9223372036854775807"; static private final String LONG_MIN = "-9223372036854775808"; static private final String INT_MAX = "2147483647"; static private final String INT_MIN = "-2147483648"; static private final String SHORT_MAX = "32767"; static private final String SHORT_MIN = "-32768"; static private final String BYTE_MAX = "127"; static private final String BYTE_MIN = "-128"; static private final String UNSIGNED_LONG_MAX = "18446744073709551615"; static private final String UNSIGNED_INT_MAX = "4294967295"; static private final String UNSIGNED_SHORT_MAX = "65535"; static private final String UNSIGNED_BYTE_MAX = "255"; // Follow RFC 3066 syntax. static private final String LANGUAGE_PATTERN = "[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*"; public DatatypeLibraryImpl(RegexEngine regexEngine) { this.regexEngine = regexEngine; typeMap.put("string", new StringDatatype()); typeMap.put("normalizedString", new CdataDatatype()); typeMap.put("token", new TokenDatatype()); typeMap.put("boolean", new BooleanDatatype()); DatatypeBase decimalType = new DecimalDatatype(); typeMap.put("decimal", decimalType); DatatypeBase integerType = new IntegerRestrictDatatype(decimalType); typeMap.put("integer", integerType); typeMap.put("nonPositiveInteger", restrictMax(integerType, "0")); typeMap.put("negativeInteger", restrictMax(integerType, "-1")); typeMap.put("long", restrictMax(restrictMin(integerType, LONG_MIN), LONG_MAX)); typeMap.put("int", restrictMax(restrictMin(integerType, INT_MIN), INT_MAX)); typeMap.put("short", restrictMax(restrictMin(integerType, SHORT_MIN), SHORT_MAX)); typeMap.put("byte", restrictMax(restrictMin(integerType, BYTE_MIN), BYTE_MAX)); DatatypeBase nonNegativeIntegerType = restrictMin(integerType, "0"); typeMap.put("nonNegativeInteger", nonNegativeIntegerType); typeMap.put("unsignedLong", restrictMax(nonNegativeIntegerType, UNSIGNED_LONG_MAX)); typeMap.put("unsignedInt", restrictMax(nonNegativeIntegerType, UNSIGNED_INT_MAX)); typeMap.put("unsignedShort", restrictMax(nonNegativeIntegerType, UNSIGNED_SHORT_MAX)); typeMap.put("unsignedByte", restrictMax(nonNegativeIntegerType, UNSIGNED_BYTE_MAX)); typeMap.put("positiveInteger", restrictMin(integerType, "1")); typeMap.put("double", new DoubleDatatype()); typeMap.put("float", new FloatDatatype()); typeMap.put("Name", new NameDatatype()); typeMap.put("QName", new QNameDatatype()); DatatypeBase ncNameType = new NCNameDatatype(); typeMap.put("NCName", ncNameType); DatatypeBase nmtokenDatatype = new NmtokenDatatype(); typeMap.put("NMTOKEN", nmtokenDatatype); typeMap.put("NMTOKENS", list(nmtokenDatatype)); typeMap.put("ID", new IdDatatype()); DatatypeBase idrefType = new IdrefDatatype(); typeMap.put("IDREF", idrefType); typeMap.put("IDREFS", list(idrefType)); typeMap.put("NOTATION", new QNameDatatype()); typeMap.put("base64Binary", new Base64BinaryDatatype()); typeMap.put("hexBinary", new HexBinaryDatatype()); typeMap.put("anyURI", new AnyUriDatatype()); typeMap.put("language", new RegexDatatype(LANGUAGE_PATTERN) { String getLexicalSpaceKey() { return "language"; } }); typeMap.put("dateTime", new DateTimeDatatype("Y-M-DTt")); typeMap.put("time", new DateTimeDatatype("t")); typeMap.put("date", new DateTimeDatatype("Y-M-D")); typeMap.put("gYearMonth", new DateTimeDatatype("Y-M")); typeMap.put("gYear", new DateTimeDatatype("Y")); typeMap.put("gMonthDay", new DateTimeDatatype("--M-D")); typeMap.put("gDay", new DateTimeDatatype("---D")); typeMap.put("gMonth", new DateTimeDatatype("--M")); DatatypeBase entityType = new EntityDatatype(); typeMap.put("ENTITY", entityType); typeMap.put("ENTITIES", list(entityType)); // Partially implemented typeMap.put("duration", new DurationDatatype()); } public DatatypeBuilder createDatatypeBuilder(String localName) throws DatatypeException { DatatypeBase base = typeMap.get(localName); if (base == null) throw new DatatypeException(); if (base instanceof RegexDatatype) { try { ((RegexDatatype)base).compile(getRegexEngine()); } catch (RegexSyntaxException e) { throw new DatatypeException(DatatypeBuilderImpl.localizer.message("regex_internal_error", localName)); } } return new DatatypeBuilderImpl(this, base); } RegexEngine getRegexEngine() throws DatatypeException { if (regexEngine == null) throw new DatatypeException(DatatypeBuilderImpl.localizer.message("regex_impl_not_found")); return regexEngine; } private static DatatypeBase restrictMax(DatatypeBase base, String limit) { try { return new MaxInclusiveRestrictDatatype(base, base.getValue(limit, null), limit); } catch (DatatypeException e) { throw new AssertionError(); } } private static DatatypeBase restrictMin(DatatypeBase base, String limit) { try { return new MinInclusiveRestrictDatatype(base, base.getValue(limit, null), limit); } catch (DatatypeException e) { throw new AssertionError(); } } private static DatatypeBase list(DatatypeBase base) { return new MinLengthRestrictDatatype(new ListDatatype(base), 1); } public Datatype createDatatype(String type) throws DatatypeException { return createDatatypeBuilder(type).createDatatype(); } }