1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
|
package com.jclark.xml.tok;
/**
* An XML TextDecl.
* @version $Revision: 1.4 $ $Date: 1998/12/30 02:42:01 $
*/
public class TextDecl {
private String version;
private String encoding;
/**
* Creates a <code>TextDecl</code> from the specified byte subarray.
* The specified encoding is used to convert bytes to characters.
* The byte subarray should be a <code>TOK_XML_DECL</code> token
* returned from Encoding.tokenizeProlog or Encoding.tokenizeContent,
* starting with <code><?</code> and ending with <code>?></code>.
* @exception InvalidTokenException if the specified byte subarray
* is not a legal XML TextDecl.
*/
public TextDecl(Encoding enc, byte[] buf, int off, int end)
throws InvalidTokenException {
init(false, enc, buf, off, end);
}
/**
* Return the encoding specified in the declaration, or null
* if no encoding was specified.
*/
public String getEncoding() {
return encoding;
}
/**
* Return the version specified in the declaration, or null
* if no version was specified.
*/
public String getVersion() {
return version;
}
TextDecl() { }
boolean init(boolean isXmlDecl, Encoding enc, byte[] buf, int off, int end) throws InvalidTokenException {
// Skip <?xml
off += enc.getMinBytesPerChar()*5;
// Skip ?>
end -= enc.getMinBytesPerChar()*2;
ContentToken ct = new ContentToken();
int firstErrorIndex = -1;
try {
parsePseudoAttributes(enc, buf, off, end, ct);
}
catch (InvalidTokenException e) {
firstErrorIndex = e.getOffset();
}
int nAtts = ct.getAttributeSpecifiedCount();
if (nAtts == 0) {
if (firstErrorIndex == -1)
firstErrorIndex = end;
throw new InvalidTokenException(firstErrorIndex);
}
String[] names = new String[nAtts];
String[] values = new String[nAtts];
char[] cbuf = new char[32];
for (int i = 0; i < nAtts; i++) {
int s = ct.getAttributeNameStart(i);
int e = ct.getAttributeNameEnd(i);
if (e - s > cbuf.length)
cbuf = new char[e - s];
names[i] = new String(cbuf, 0, enc.convert(buf, s, e, cbuf, 0));
s = ct.getAttributeValueStart(i);
e = ct.getAttributeValueEnd(i);
if (e - s > cbuf.length)
cbuf = new char[e - s];
values[i] = new String(cbuf, 0, enc.convert(buf, s, e, cbuf, 0));
}
int att = 0;
if (names[0].equals("version")) {
version = values[0];
att++;
}
if ((att == 1 || !isXmlDecl)
&& att < nAtts && names[att].equals("encoding")) {
encoding = values[att];
if (values[att].length() == 0
|| !Character.isLetter(values[att].charAt(0))
|| values[att].indexOf(':') >= 0) {
int k = ct.getAttributeValueStart(att);
if (firstErrorIndex == -1 || k < firstErrorIndex)
firstErrorIndex = k;
}
att++;
}
else if (!isXmlDecl)
firstErrorIndex = 0; // encoding is required in a TextDecl
boolean standalone = false;
if (isXmlDecl && att > 0 && att < nAtts
&& names[att].equals("standalone")) {
if (values[att].equals("yes"))
standalone = true;
else if (!values[att].equals("no")) {
int k = ct.getAttributeValueStart(att);
if (firstErrorIndex == -1 || k < firstErrorIndex)
firstErrorIndex = k;
}
att++;
}
if (att < nAtts) {
int k = ct.getAttributeNameStart(att);
if (firstErrorIndex == -1 || k < firstErrorIndex)
firstErrorIndex = k;
}
if (firstErrorIndex != -1)
throw new InvalidTokenException(firstErrorIndex);
return standalone;
}
private final
void parsePseudoAttributes(Encoding enc, byte[] buf, int off, int end,
ContentToken ct) throws InvalidTokenException {
final int minBPC = enc.getMinBytesPerChar();
for (;;) {
off = skipWS(enc, buf, off, end);
if (off == end)
break;
int nameStart = off;
int nameEnd;
nameLoop:
for (;;) {
switch (enc.byteType(buf, off)) {
case Encoding.BT_NMSTRT:
break;
case Encoding.BT_EQUALS:
nameEnd = off;
break nameLoop;
case Encoding.BT_S:
case Encoding.BT_LF:
case Encoding.BT_CR:
nameEnd = off;
off += minBPC;
off = skipWS(enc, buf, off, end);
if (off == end || !enc.charMatches(buf, off, '='))
throw new InvalidTokenException(off);
break nameLoop;
default:
throw new InvalidTokenException(off);
}
off += minBPC;
if (off == end)
throw new InvalidTokenException(off);
}
off += minBPC;
off = skipWS(enc, buf, off, end);
if (off == end || !(enc.charMatches(buf, off, '\'')
|| enc.charMatches(buf, off, '"')))
throw new InvalidTokenException(off);
off += minBPC;
int valueStart = off;
valueLoop:
for (;;) {
if (off == end)
throw new InvalidTokenException(off);
switch (enc.byteType(buf, off)) {
case Encoding.BT_NMSTRT:
case Encoding.BT_NAME:
case Encoding.BT_MINUS:
if ((enc.byteToAscii(buf, off) & ~0x7F) != 0)
throw new InvalidTokenException(off);
off += minBPC;
break;
case Encoding.BT_QUOT:
case Encoding.BT_APOS:
if (enc.byteType(buf, off) != enc.byteType(buf, valueStart - minBPC))
throw new InvalidTokenException(off);
break valueLoop;
default:
throw new InvalidTokenException(off);
}
}
ct.appendAttribute(nameStart, nameEnd, valueStart, off, true);
off += minBPC;
if (off == end)
break;
switch (enc.byteType(buf, off)) {
case Encoding.BT_S:
case Encoding.BT_LF:
case Encoding.BT_CR:
off += minBPC;
break;
default:
throw new InvalidTokenException(off);
}
}
}
private int skipWS(Encoding enc, byte[] buf, int off, int end) {
loop:
while (off != end) {
switch (enc.byteType(buf, off)) {
case Encoding.BT_S:
case Encoding.BT_LF:
case Encoding.BT_CR:
off += enc.getMinBytesPerChar();
break;
default:
break loop;
}
}
return off;
}
}
|