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 }