1 23 24 package org.enhydra.xml.xmlc.misc; 25 26 import java.io.IOException ; 27 import java.io.Reader ; 28 29 import org.enhydra.xml.io.InputSourceOps; 30 import org.xml.sax.InputSource ; 31 32 42 final class SSIParsedStream { 43 46 public static final int AT_EOF = -1; 47 48 52 public static final int AT_SSI = -2; 53 54 57 private static final String SSI_PREFIX_STR = "<!--#"; 58 private static final int SSI_PREFIX_LEN = SSI_PREFIX_STR.length(); 59 private static final char[] SSI_PREFIX; 60 61 64 private static final String SSI_SUFFIX_STR = "-->"; 65 private static final int SSI_SUFFIX_LEN = SSI_SUFFIX_STR.length(); 66 private static final char[] SSI_SUFFIX; 67 68 69 private static final int INIT_BUFFER_SIZE = 8*1024; 70 71 72 private static final int MAX_NESTING_DEPTH = 64; 73 74 75 private static final int SSI_INIT_ARG_STRING_SIZE = 64; 76 77 78 private InputSource fSource; 79 80 84 private SSIParsedStream fPrevStream; 85 86 89 private int fDepth; 90 91 94 private char[] fBuffer; 95 96 99 private int fNextCharIdx; 100 101 104 private int fLastCharIdx; 105 106 110 private int fSSIStartIdx; 111 112 116 private LineNumberRecorder fLineNumbers; 117 118 121 static { 122 SSI_PREFIX = new char[SSI_PREFIX_LEN]; 123 SSI_PREFIX_STR.getChars(0, SSI_PREFIX_LEN, SSI_PREFIX, 0); 124 SSI_SUFFIX = new char[SSI_SUFFIX_LEN]; 125 SSI_SUFFIX_STR.getChars(0, SSI_SUFFIX_LEN, SSI_SUFFIX, 0); 126 } 127 128 131 public SSIParsedStream(InputSource source, 132 LineNumberRecorder lineNumbers) throws IOException { 133 this(source, lineNumbers, null); 134 } 135 136 139 public SSIParsedStream(InputSource source, 140 LineNumberRecorder lineNumbers, 141 SSIParsedStream prevStream) throws IOException { 142 if (source.getSystemId() == null) { 143 throw new IOException ("SSI InputSource must have a system id"); 144 } 145 fSource = source; 146 fLineNumbers = lineNumbers; 147 fLineNumbers.pushFile(getSystemId()); 148 149 if (prevStream != null) { 150 fSource.setEncoding(prevStream.fSource.getEncoding()); 151 152 fPrevStream = prevStream; 153 fDepth = prevStream.fDepth + 1; 154 if (fDepth > MAX_NESTING_DEPTH) { 155 throw new IOException ("SSI max nesting depth exceeded at: " 156 + getSystemId()); 157 } 158 } 159 readIntoBuffer(fSource); 160 } 161 162 163 166 private void expandBuffer() { 167 char[] newBuffer = new char[fBuffer.length * 2]; 168 System.arraycopy(fBuffer, 0, newBuffer, 0, fBuffer.length); 169 fBuffer = newBuffer; 170 } 171 172 175 private void readIntoBuffer(Reader in) throws IOException { 176 int charsRead; 177 int readIdx = 0; 178 fBuffer = new char[INIT_BUFFER_SIZE]; 179 while (true) { 180 if (readIdx >= fBuffer.length) { 181 expandBuffer(); 182 } 183 charsRead = in.read(fBuffer, readIdx, fBuffer.length - readIdx); 184 if (charsRead < 0) { 185 break; 186 } 187 readIdx += charsRead; 188 } 189 fNextCharIdx = 0; 190 fLastCharIdx = readIdx - 1; fSSIStartIdx = scanForSSIStart(fNextCharIdx); 192 } 193 194 197 private void readIntoBuffer(InputSource source) throws IOException { 198 Reader in = InputSourceOps.open(source); 199 try { 200 readIntoBuffer(in); 201 } finally { 202 InputSourceOps.closeIfOpened(source, in); 203 } 204 } 205 206 207 210 public String getSystemId() { 211 return fSource.getSystemId(); 212 } 213 214 217 public SSIParsedStream pop() throws IOException { 218 fLineNumbers.popFile(); 219 return fPrevStream; 220 } 221 222 226 private boolean isSSIPrefix(int idx) throws IOException { 227 for (int prefixIdx = 0; prefixIdx < SSI_PREFIX_LEN; prefixIdx++, idx++) { 228 if (fBuffer[idx] != SSI_PREFIX[prefixIdx]) { 229 return false; 230 } 231 } 232 return true; 233 } 234 235 238 private boolean isSSISuffix(int idx) throws IOException { 239 for (int suffixIdx = 0; suffixIdx < SSI_SUFFIX_LEN; suffixIdx++, idx++) { 240 if (fBuffer[idx] != SSI_SUFFIX[suffixIdx]) { 241 return false; 242 } 243 } 244 return true; 245 } 246 247 251 private int scanForSSIStart(int startIdx) throws IOException { 252 for (int idx = startIdx; idx <= fLastCharIdx; idx++) { 253 if ((fBuffer[idx] == '<') && isSSIPrefix(idx)) { 254 return idx; 255 } 256 } 257 return fLastCharIdx + 1; } 259 260 265 public int read() throws IOException { 266 if (fNextCharIdx > fLastCharIdx) { 267 return AT_EOF; 268 } 269 270 if (fNextCharIdx == fSSIStartIdx) { 273 return AT_SSI; 274 } 275 char ch = fBuffer[fNextCharIdx++]; 276 fLineNumbers.countChar(ch); 277 return ch; 278 } 279 280 286 public int read(char cbuf[], int off, int len) throws IOException { 287 if ((off < 0) || (off > cbuf.length) || (len < 0) 288 || ((off + len) > cbuf.length) || ((off + len) < 0)) { 289 throw new IndexOutOfBoundsException (); 290 } else if (len == 0) { 291 return 0; 292 } 293 294 if (fNextCharIdx > fLastCharIdx) { 296 return AT_EOF; 297 } 298 299 if (fNextCharIdx == fSSIStartIdx) { 302 return AT_SSI; 303 } 304 305 int copyLen; 307 if (fSSIStartIdx <= fLastCharIdx) { 308 copyLen = fSSIStartIdx - fNextCharIdx; 310 } else { 311 copyLen = fLastCharIdx - fNextCharIdx + 1; 313 } 314 if (copyLen > len) { 315 copyLen = len; 317 } 318 System.arraycopy(fBuffer, fNextCharIdx, cbuf, off, copyLen); 319 fNextCharIdx += copyLen; 320 fLineNumbers.countChars(cbuf, off, copyLen); 321 return copyLen; 322 } 323 324 328 private void ssiAdvanceChar() throws IOException { 329 fNextCharIdx++; 330 if (fNextCharIdx > fLastCharIdx) { 331 throw new IOException ("Unexpected EOF in SSI directive: " 332 + getSystemId()); 333 } 334 fLineNumbers.countChar(fBuffer[fNextCharIdx]); 335 } 336 337 342 private void ssiSkipWhiteSpace() throws IOException { 343 while (Character.isWhitespace(fBuffer[fNextCharIdx])) { 344 ssiAdvanceChar(); 345 } 346 } 347 348 353 private void ssiNonSkipWhiteSpace() throws IOException { 354 while (!Character.isWhitespace(fBuffer[fNextCharIdx])) { 355 ssiAdvanceChar(); 356 } 357 } 358 359 363 private String ssiParseCmd() throws IOException { 364 int startIdx = fNextCharIdx; 365 ssiNonSkipWhiteSpace(); 366 return new String (fBuffer, startIdx, (fNextCharIdx-startIdx)); 367 } 368 369 373 private String ssiParseArgName() throws IOException { 374 int startIdx = fNextCharIdx; 375 while (Character.isLetterOrDigit(fBuffer[fNextCharIdx])) { 376 ssiAdvanceChar(); 377 } 378 if (fBuffer[fNextCharIdx] != '=') { 379 throw new IOException ("SSI argument name not terminated with a `=': " 380 + getSystemId()); 381 } 382 ssiAdvanceChar(); return new String (fBuffer, startIdx, (fNextCharIdx-startIdx)-1); 384 } 385 386 390 private String ssiParseArgValue() throws IOException { 391 StringBuffer buf = new StringBuffer (SSI_INIT_ARG_STRING_SIZE); 392 393 if (fBuffer[fNextCharIdx] != '"') { 395 throw new IOException ("Missing open quote in SSI argument value: " 396 + getSystemId()); 397 } 398 ssiAdvanceChar(); 400 while (fBuffer[fNextCharIdx] != '"') { 402 if (fBuffer[fNextCharIdx] == '\\') { 403 ssiAdvanceChar(); } 405 buf.append(fBuffer[fNextCharIdx]); 406 ssiAdvanceChar(); 407 } 408 ssiAdvanceChar(); return buf.toString(); 410 } 411 412 416 private void ssiParseArg(SSIDirective directive) throws IOException { 417 directive.addArg(ssiParseArgName(), ssiParseArgValue()); 418 } 419 420 424 public SSIDirective parseSSIDirective() throws IOException { 425 fNextCharIdx += SSI_PREFIX_LEN; 427 428 SSIDirective directive = new SSIDirective(ssiParseCmd(), getSystemId()); 430 ssiSkipWhiteSpace(); 431 432 while (!isSSISuffix(fNextCharIdx)) { 434 ssiParseArg(directive); 435 ssiSkipWhiteSpace(); 436 } 437 fNextCharIdx += SSI_SUFFIX_LEN; 438 fSSIStartIdx = scanForSSIStart(fNextCharIdx); 439 return directive; 440 } 441 } 442 | Popular Tags |