001 package com.thaiopensource.datatype.xsd;
002
003 import org.relaxng.datatype.DatatypeException;
004 import org.relaxng.datatype.DatatypeStreamingValidator;
005 import org.relaxng.datatype.ValidationContext;
006 import org.relaxng.datatype.helpers.StreamingValidatorImpl;
007
008 import com.thaiopensource.datatype.Datatype2;
009
010 abstract class DatatypeBase implements Datatype2 {
011 abstract boolean lexicallyAllows(String str);
012 private final int whiteSpace;
013
014 static final int WHITE_SPACE_PRESERVE = 0;
015 static final int WHITE_SPACE_REPLACE = 1;
016 static final int WHITE_SPACE_COLLAPSE = 2;
017
018 DatatypeBase() {
019 whiteSpace = WHITE_SPACE_COLLAPSE;
020 }
021
022 DatatypeBase(int whiteSpace) {
023 this.whiteSpace = whiteSpace;
024 }
025
026 int getWhiteSpace() {
027 return whiteSpace;
028 }
029
030 public boolean isValid(String str, ValidationContext vc) {
031 str = normalizeWhiteSpace(str);
032 return lexicallyAllows(str) && allowsValue(str, vc);
033 }
034
035 public void checkValid(String str, ValidationContext vc) throws DatatypeException {
036 if (!isValid(str, vc))
037 throw new DatatypeException();
038 }
039
040 public Object createValue(String str, ValidationContext vc) {
041 str = normalizeWhiteSpace(str);
042 if (!lexicallyAllows(str))
043 return null;
044 return getValue(str, vc);
045 }
046
047 final String normalizeWhiteSpace(String str) {
048 switch (whiteSpace) {
049 case WHITE_SPACE_COLLAPSE:
050 return collapseWhiteSpace(str);
051 case WHITE_SPACE_REPLACE:
052 return replaceWhiteSpace(str);
053 }
054 return str;
055 }
056
057 /* Requires lexicallyAllows to be true. Must return same value as
058 getValue(str, vc) != null. */
059 boolean allowsValue(String str, ValidationContext vc) {
060 return true;
061 }
062
063 /* Requires lexicallyAllows to be true. Returns null if value does not satisfy
064 constraints on value space. */
065 abstract Object getValue(String str, ValidationContext vc);
066
067 OrderRelation getOrderRelation() {
068 return null;
069 }
070
071 /* For datatypes that have a length. */
072 Measure getMeasure() {
073 return null;
074 }
075
076 static private final String collapseWhiteSpace(String s) {
077 int i = collapseStart(s);
078 if (i < 0)
079 return s;
080 StringBuffer buf = new StringBuffer(s.substring(0, i));
081 boolean collapsing = (i == 0 || s.charAt(i - 1) == ' ');
082 for (int len = s.length(); i < len; i++) {
083 char c = s.charAt(i);
084 switch (c) {
085 case '\r':
086 case '\n':
087 case '\t':
088 case ' ':
089 if (!collapsing) {
090 buf.append(' ');
091 collapsing = true;
092 }
093 break;
094 default:
095 collapsing = false;
096 buf.append(c);
097 break;
098 }
099 }
100 if (buf.length() > 0 && buf.charAt(buf.length() - 1) == ' ')
101 buf.setLength(buf.length() - 1);
102 return buf.toString();
103 }
104
105 static private final int collapseStart(String s) {
106 for (int i = 0, len = s.length(); i < len; i++) {
107 switch (s.charAt(i)) {
108 case ' ':
109 if (i == 0 || s.charAt(i - 1) == ' ' || i == len - 1)
110 return i;
111 break;
112 case '\r':
113 case '\n':
114 case '\t':
115 return i;
116 }
117 }
118 return -1;
119 }
120
121 static private final String replaceWhiteSpace(String s) {
122 int len = s.length();
123 for (int i = 0; i < len; i++)
124 switch (s.charAt(i)) {
125 case '\r':
126 case '\n':
127 case '\t':
128 {
129 char[] buf = s.toCharArray();
130 buf[i] = ' ';
131 for (++i; i < len; i++)
132 switch (buf[i]) {
133 case '\r':
134 case '\n':
135 case '\t':
136 buf[i] = ' ';
137 }
138 return new String(buf);
139 }
140 }
141 return s;
142 }
143
144 DatatypeBase getPrimitive() {
145 return this;
146 }
147
148 public boolean isContextDependent() {
149 return false;
150 }
151
152 public boolean alwaysValid() {
153 return false;
154 }
155
156 public int getIdType() {
157 return ID_TYPE_NULL;
158 }
159
160 public int valueHashCode(Object value) {
161 return value.hashCode();
162 }
163
164 public boolean sameValue(Object value1, Object value2) {
165 return value1.equals(value2);
166 }
167
168 public DatatypeStreamingValidator createStreamingValidator(ValidationContext vc) {
169 return new StreamingValidatorImpl(this, vc);
170 }
171 }