001 package com.thaiopensource.datatype.xsd;
002
003 import org.relaxng.datatype.ValidationContext;
004
005 class Base64BinaryDatatype extends BinaryDatatype {
006 static private final byte[] weightTable = makeWeightTable();
007 static private final byte INVALID = (byte)-1;
008 static private final byte WHITESPACE = (byte)-2;
009 static private final byte PADDING = (byte)-3;
010
011 // for efficiency, don't assume whitespace normalized
012 boolean lexicallyAllows(String str) {
013 return byteCount(str) >= 0;
014 }
015
016 private static int byteCount(String str) {
017 int nChars = 0;
018 int nPadding = 0;
019 int lastCharWeight = -1;
020 for (int i = 0, len = str.length(); i < len; i++) {
021 char c = str.charAt(i);
022 if (c >= 128)
023 return -1;
024 int w = weightTable[c];
025 switch (w) {
026 case WHITESPACE:
027 break;
028 case PADDING:
029 if (++nPadding > 2)
030 return -1;
031 break;
032 case INVALID:
033 return -1;
034 default:
035 if (nPadding > 0)
036 return -1;
037 lastCharWeight = w;
038 nChars++;
039 break;
040 }
041 }
042 if (((nChars + nPadding) & 0x3) != 0)
043 return -1;
044 switch (nPadding) {
045 case 1:
046 // 1 padding char; last quartet specifies 2 bytes = 16 bits = 6 + 6 + 4 bits
047 // lastChar must have 6 - 4 = 2 unused bits
048 if ((lastCharWeight & 0x3) != 0)
049 return -1;
050 break;
051 case 2:
052 // 2 padding chars; last quartet specifies 1 byte = 8 bits = 6 + 2 bits
053 // lastChar must have 6 - 2 = 4 unused bits
054 if ((lastCharWeight & 0xF) != 0)
055 return -1;
056 break;
057 }
058 return ((nChars + nPadding) >> 2)*3 - nPadding;
059 }
060
061 Object getValue(String str, ValidationContext vc) {
062 int nBytes = byteCount(str);
063 byte[] value = new byte[nBytes];
064 int valueIndex = 0;
065 int nBytesAccum = 0;
066 int accum = 0;
067 for (int i = 0, len = str.length(); i < len; i++) {
068 int w = weightTable[str.charAt(i)];
069 if (w != WHITESPACE) {
070 accum <<= 6;
071 if (w != PADDING)
072 accum |= w;
073 if (++nBytesAccum == 4) {
074 for (int shift = 16; shift >= 0; shift -= 8) {
075 if (valueIndex < nBytes)
076 value[valueIndex++] = (byte)((accum >> shift) & 0xFF);
077 }
078 nBytesAccum = 0;
079 accum = 0;
080 }
081 }
082 }
083 return value;
084 }
085
086 static private byte[] makeWeightTable() {
087 byte[] w = new byte[128];
088 byte n = INVALID;
089 for (int i = 0; i < 128; i++)
090 w[i] = n;
091 n = 0;
092 for (int i = 'A'; i <= 'Z'; i++, n++)
093 w[i] = n;
094 for (int i = 'a'; i <= 'z'; i++, n++)
095 w[i] = n;
096 for (int i = '0'; i <= '9'; i++, n++)
097 w[i] = n;
098 w['+'] = n++;
099 w['/'] = n++;
100 w[' '] = w['\t'] = w['\r'] = w['\n'] = WHITESPACE;
101 w['='] = PADDING;
102 return w;
103 }
104
105 }