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    }