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 }