// Copyright 2003, FreeHEP. package org.freehep.util.io; import java.io.IOException; import java.io.InputStream; import java.util.NoSuchElementException; /** * The XMLSequence allows parsing by an XML Parser of concatenated XML segments. * Each segment needs to start with "<?xml". If the underlying stream contains * just one segment "<xml?" may be left out. Methods hasNext() and next() * should be called to retrieve the inputstream for the XMLParser. * * Typical usage: * * <PRE> * * XMLSequence sequence = new XMLSequence(new FileInputStream("file.xml")); * * SAXParserFactory factory = SAXParserFactory.newInstance(); XMLReader * xmlReader = factory.newSAXParser().getXMLReader(); * * while (sequence.hasNext()) { InputStream input = sequence.next(); InputSource * source = new InputSource(input); xmlReader.parse(source); input.close(); } * sequence.close(); * * </PRE> * * IMPORTANT: inherits from InputStream rather than FilterInputStream so that * the correct read(byte[], int, int) method is used. * * @author Mark Donszelmann * @version $Id: XMLSequence.java 8584 2006-08-10 23:06:37Z duns $ */ public class XMLSequence extends InputStream { // xml declaration (lowercase!) private int[] xml = new int[] { '<', '?', 'x', 'm', 'l' }; private int xmlIndex; // look ahead buffer private int buffer[] = new int[xml.length]; private int index; private boolean bufferEmpty; // underlying stream private InputStream in; private boolean closed; private boolean eof; /** * Create a XML Sequence. * * @param input stream to read from */ public XMLSequence(InputStream input) { super(); in = input; closed = false; eof = false; xmlIndex = 0; index = 0; bufferEmpty = true; } /** * Is another XML segment available. * @return true if another XML segment can be read */ public boolean hasNext() { if (closed) return false; try { return readUntilXMLDeclaration(); } catch (IOException e) { return false; } } /** * Returns the next XML segment. * @return stream to read next XML segment from. * @throws IOException if read fails */ public InputStream next() throws IOException { if (eof) throw new NoSuchElementException(getClass() + ": at EOF."); if (closed) throw new NoSuchElementException(getClass() + ": already closed."); if (!readUntilXMLDeclaration()) throw new NoSuchElementException(getClass() + ": No more sequences."); xmlIndex = 0; return new NoCloseInputStream(this); } public int read() throws IOException { if (xmlIndex == xml.length) return -1; if (closed) return -1; int a = -1; if (!eof) { // fill buffer (at start of stream) if (bufferEmpty) { index = 0; for (int i = 0; i < xml.length; i++) { int c = in.read(); buffer[i] = c; if (buffer[i] == xml[xmlIndex]) { // we are reading <?xml or < xmlIndex++; } else if (xmlIndex > 0) { // we are reading <..., which is also fine. xmlIndex++; } else { xmlIndex = 0; } } bufferEmpty = false; if (xmlIndex == xml.length) return -1; } // read next character a = in.read(); if (a != -1) { // check if seq if (a == xml[xmlIndex]) { xmlIndex++; } else { xmlIndex = 0; } } else { // underlying end-of-file eof = true; } } // returned all... if (eof && (buffer[index] == -1)) return -1; // replace and return next character int b = buffer[index]; buffer[index] = a; index = (index + 1) % buffer.length; return b; } public void mark(int readLimit) { in.mark(readLimit); } public boolean markSupported() { return in.markSupported(); } public void reset() throws IOException { if (closed) throw new IOException(getClass() + ": already closed."); if (!in.markSupported()) throw new IOException(getClass() + ": does not support reset()."); in.reset(); xmlIndex = 0; index = 0; bufferEmpty = true; eof = false; } public void close() throws IOException { if (!closed) { in.close(); closed = true; } } private boolean readUntilXMLDeclaration() throws IOException { while (read() >= 0) ; return (xmlIndex == xml.length); } }