/* Woodstox Lite ("wool") XML processor
 *
 * Copyright (c) 2006- Tatu Saloranta, tatu.saloranta@iki.fi
 *
 * Licensed under the License specified in the file LICENSE which is
 * included with the source code.
 * You may not use this file except in compliance with the License.
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.fasterxml.aalto.stax;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.net.URL;
import java.nio.ByteBuffer;

import javax.xml.stream.*;
import javax.xml.stream.util.XMLEventAllocator;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stream.StreamSource;

import org.codehaus.stax2.XMLEventReader2;
import org.codehaus.stax2.XMLStreamReader2;
import org.codehaus.stax2.io.Stax2ByteArraySource;
import org.codehaus.stax2.io.Stax2CharArraySource;
import org.codehaus.stax2.io.Stax2Source;
import org.codehaus.stax2.ri.Stax2FilteredStreamReader;
import org.codehaus.stax2.ri.Stax2ReaderAdapter;
import org.codehaus.stax2.ri.evt.Stax2EventReaderAdapter;
import org.codehaus.stax2.ri.evt.Stax2FilteredEventReader;
import org.xml.sax.InputSource;

import com.fasterxml.aalto.*;
import com.fasterxml.aalto.async.AsyncByteArrayScanner;
import com.fasterxml.aalto.async.AsyncByteBufferScanner;
import com.fasterxml.aalto.async.AsyncStreamReaderImpl;
import com.fasterxml.aalto.dom.DOMReaderImpl;
import com.fasterxml.aalto.evt.EventAllocatorImpl;
import com.fasterxml.aalto.evt.EventReaderImpl;
import com.fasterxml.aalto.impl.IoStreamException;
import com.fasterxml.aalto.in.*;
import com.fasterxml.aalto.util.URLUtil;

Aalto implementation of basic Stax factory (both XMLInputFactory and XMLInputFactory2) as well as API for producing non-blocking (async) parsers (that is, AsyncXMLInputFactory).
Author:Tatu Saloranta
/** * Aalto implementation of basic Stax factory (both * {@link javax.xml.stream.XMLInputFactory} and {@link org.codehaus.stax2.XMLInputFactory2}) * as well as API for producing non-blocking (async) parsers * (that is, {@link AsyncXMLInputFactory}). * * @author Tatu Saloranta */
public final class InputFactoryImpl extends AsyncXMLInputFactory {
This is the currently active configuration that will be used for readers created by this factory.
/** * This is the currently active configuration that will be used * for readers created by this factory. */
final ReaderConfig _config; // // // StAX - mandated objects: protected XMLEventAllocator _allocator = null; /* /********************************************************************** /* Life-cycle: /********************************************************************** */ public InputFactoryImpl() { _config = new ReaderConfig(); } /* /********************************************************************** /* Stax, XMLInputFactory: filtered reader factory methods /********************************************************************** */ // // // Filtered reader factory methods @Override public XMLEventReader createFilteredReader(XMLEventReader reader, EventFilter filter) { return new Stax2FilteredEventReader(Stax2EventReaderAdapter.wrapIfNecessary(reader), filter); } @Override public XMLStreamReader createFilteredReader(XMLStreamReader reader, StreamFilter filter) throws XMLStreamException { Stax2FilteredStreamReader fr = new Stax2FilteredStreamReader(reader, filter); /* As per Stax 1.0 TCK, apparently the filtered * reader is expected to be automatically forwarded to the first * acceptable event. This is different from the way RI works, but * since specs don't say anything about filtered readers, let's * consider TCK to be "more formal" for now, and implement that * behavior. */ if (!filter.accept(fr)) { // START_DOCUMENT ok? // Ok, nope, this should do the trick: fr.next(); } return fr; } /* /********************************************************************** /* Stax, XMLInputFactory: XMLEventReader factory methods /********************************************************************** */ @Override public XMLEventReader createXMLEventReader(InputStream in) throws XMLStreamException { return createXMLEventReader(in, null); } @Override public XMLEventReader createXMLEventReader(InputStream in, String enc) throws XMLStreamException { return constructER(constructSR(in, enc, true)); } @Override public XMLEventReader createXMLEventReader(Reader r) throws XMLStreamException { return createXMLEventReader(null, r); } @Override public XMLEventReader createXMLEventReader(javax.xml.transform.Source source) throws XMLStreamException { return constructER(constructSR(source, true)); } @Override public XMLEventReader createXMLEventReader(String systemId, InputStream in) throws XMLStreamException { return constructER(constructSR(systemId, in, true)); } @Override public XMLEventReader createXMLEventReader(String systemId, Reader r) throws XMLStreamException { return constructER(constructSR(systemId, r, true)); } @Override public XMLEventReader createXMLEventReader(XMLStreamReader sr) throws XMLStreamException { return constructER(Stax2ReaderAdapter.wrapIfNecessary(sr)); } /* /********************************************************************** /* Stax, XMLInputFactory: XMLStreamReader factory methods /********************************************************************** */ @Override public XMLStreamReader createXMLStreamReader(InputStream in) throws XMLStreamException { return constructSR(in, null, false); } @Override public XMLStreamReader createXMLStreamReader(InputStream in, String enc) throws XMLStreamException { return constructSR(in, enc, false); } @Override public XMLStreamReader createXMLStreamReader(Reader r) throws XMLStreamException { return constructSR(null, r, false); } @Override public XMLStreamReader createXMLStreamReader(String systemId, Reader r) throws XMLStreamException { return constructSR(systemId, r, false); } @Override public XMLStreamReader createXMLStreamReader(javax.xml.transform.Source src) throws XMLStreamException { return constructSR(src, false); } @Override public XMLStreamReader createXMLStreamReader(String systemId, InputStream in) throws XMLStreamException { return constructSR(systemId, in, false); } /* /********************************************************************** /* Stax, XMLInputFactory; generic accessors/mutators /********************************************************************** */ @Override public Object getProperty(String name) { // false -> is mandatory, unrecognized will throw IllegalArgumentException return _config.getProperty(name, true); } @Override public void setProperty(String propName, Object value) { _config.setProperty(propName, value); } @Override public XMLEventAllocator getEventAllocator() { return _allocator; } @Override public XMLReporter getXMLReporter() { return _config.getXMLReporter(); } @Override public XMLResolver getXMLResolver() { return _config.getXMLResolver(); } @Override public boolean isPropertySupported(String name) { return _config.isPropertySupported(name); } @Override public void setEventAllocator(XMLEventAllocator allocator) { _allocator = allocator; } @Override public void setXMLReporter(XMLReporter r) { _config.setXMLReporter(r); } @Override public void setXMLResolver(XMLResolver r) { _config.setXMLResolver(r); } /* /********************************************************************** /* Stax2 implementation; additional factory methods /********************************************************************** */ @Override public XMLEventReader2 createXMLEventReader(URL src) throws XMLStreamException { return constructER(constructSR(src, true)); } @Override public XMLEventReader2 createXMLEventReader(File f) throws XMLStreamException { return constructER(constructSR(f, true)); } @Override public XMLStreamReader2 createXMLStreamReader(URL src) throws XMLStreamException { return constructSR(src, false); }
Convenience factory method that allows for parsing a document stored in the specified file.
/** * Convenience factory method that allows for parsing a document * stored in the specified file. */
@Override public XMLStreamReader2 createXMLStreamReader(File f) throws XMLStreamException { return constructSR(f, false); } // // // StAX2 "Profile" mutators @Override public void configureForXmlConformance() { _config.configureForXmlConformance(); } @Override public void configureForConvenience() { _config.configureForConvenience(); } @Override public void configureForSpeed() { _config.configureForSpeed(); } @Override public void configureForLowMemUsage() { _config.configureForLowMemUsage(); } @Override public void configureForRoundTripping() { _config.configureForRoundTripping(); } /* /********************************************************************** /* Non-blocking reader factories (AsyncXMLInputFactory) /********************************************************************** */ @Override public AsyncXMLStreamReader<AsyncByteArrayFeeder> createAsyncForByteArray() { // TODO: pass system and/or public ids? ReaderConfig cfg = getNonSharedConfig(null, null, null, false, false); cfg.setActualEncoding("UTF-8"); return new AsyncStreamReaderImpl<AsyncByteArrayFeeder>(new AsyncByteArrayScanner(cfg)); } @Override public AsyncXMLStreamReader<AsyncByteArrayFeeder> createAsyncFor(byte[] input) throws XMLStreamException { return createAsyncFor(input, 0, input.length); } @Override public AsyncXMLStreamReader<AsyncByteArrayFeeder> createAsyncFor(byte[] input, int offset, int length) throws XMLStreamException { ReaderConfig cfg = getNonSharedConfig(null, null, null, false, false); cfg.setActualEncoding("UTF-8"); AsyncByteArrayScanner scanner = new AsyncByteArrayScanner(cfg); scanner.feedInput(input, offset, length); return new AsyncStreamReaderImpl<AsyncByteArrayFeeder>(scanner); } @Override public AsyncXMLStreamReader<AsyncByteBufferFeeder> createAsyncForByteBuffer() { ReaderConfig cfg = getNonSharedConfig(null, null, null, false, false); cfg.setActualEncoding("UTF-8"); return new AsyncStreamReaderImpl<AsyncByteBufferFeeder>(new AsyncByteBufferScanner(cfg)); } @Override public AsyncXMLStreamReader<AsyncByteBufferFeeder> createAsyncFor(ByteBuffer input) throws XMLStreamException { ReaderConfig cfg = getNonSharedConfig(null, null, null, false, false); cfg.setActualEncoding("UTF-8"); AsyncByteBufferScanner scanner = new AsyncByteBufferScanner(cfg); scanner.feedInput(input); return new AsyncStreamReaderImpl<AsyncByteBufferFeeder>(scanner); } /* /********************************************************************** /* Internal/package methods /********************************************************************** */
Method called when a non-shared copy of the current configuration is needed. This is usually done when a new reader is constructed.
/** * Method called when a non-shared copy of the current configuration * is needed. This is usually done when a new reader is constructed. */
public ReaderConfig getNonSharedConfig(String systemId, String publicId, String extEncoding, boolean forEventReader, boolean forceAutoClose) { ReaderConfig cfg = _config.createNonShared(publicId, systemId, extEncoding); if (forEventReader) { /* No point in lazy parsing for event readers: no more efficient * (and possible less) since all data is needed, always; and * exceptions also get lazily thrown after the fact. */ cfg.doParseLazily(false); } if (forceAutoClose) { cfg.doAutoCloseInput(true); } return cfg; } protected XMLStreamReader2 constructSR(InputStream in, String enc, boolean forEventReader) throws XMLStreamException { ReaderConfig cfg = getNonSharedConfig(null, null, enc, forEventReader, false); return StreamReaderImpl.construct(ByteSourceBootstrapper.construct(cfg, in)); } protected XMLStreamReader2 constructSR(String systemId, Reader r, boolean forEventReader) throws XMLStreamException { ReaderConfig cfg = getNonSharedConfig(null, systemId, null, forEventReader, false); return StreamReaderImpl.construct(CharSourceBootstrapper.construct(cfg, r)); } protected XMLStreamReader2 constructSR(String systemId, InputStream in, boolean forEventReader) throws XMLStreamException { ReaderConfig cfg = getNonSharedConfig(null, systemId, null, forEventReader, false); return StreamReaderImpl.construct(ByteSourceBootstrapper.construct(cfg, in)); } @SuppressWarnings("resource") protected XMLStreamReader2 constructSR(javax.xml.transform.Source src, boolean forEventReader) throws XMLStreamException { if (src instanceof Stax2Source) { return constructSR2((Stax2Source) src, forEventReader); } Reader r = null; InputStream in = null; String pubId = null; String sysId = null; String encoding = null; boolean autoCloseInput; if (src instanceof StreamSource) { StreamSource ss = (StreamSource) src; sysId = ss.getSystemId(); pubId = ss.getPublicId(); in = ss.getInputStream(); if (in == null) { r = ss.getReader(); } /* Caller still has access to stream/reader (except if we only * get system-id); no need to force auto-close here */ autoCloseInput = false; } else if (src instanceof SAXSource) { SAXSource ss = (SAXSource) src; // Not a complete implementation, but maybe it's enough? sysId = ss.getSystemId(); InputSource isrc = ss.getInputSource(); if (isrc != null) { sysId = isrc.getSystemId(); pubId = isrc.getPublicId(); encoding = isrc.getEncoding(); in = isrc.getByteStream(); if (in == null) { r = isrc.getCharacterStream(); } } /* Caller still has access to stream/reader (except if we only * get system-id); no need to force auto-close here */ autoCloseInput = false; } else if (src instanceof DOMSource) { autoCloseInput = false; // shouldn't matter ReaderConfig cfg = getNonSharedConfig(pubId, sysId, encoding, forEventReader, autoCloseInput); return DOMReaderImpl.createFrom((DOMSource) src, cfg); } else { throw new IllegalArgumentException("Can not instantiate StAX reader for XML source type "+src.getClass()+" (unrecognized type)"); } if (in != null) { ReaderConfig cfg = getNonSharedConfig(pubId, sysId, encoding, forEventReader, autoCloseInput); return StreamReaderImpl.construct(ByteSourceBootstrapper.construct(cfg, in)); } if (r != null) { ReaderConfig cfg = getNonSharedConfig(pubId, sysId, encoding, forEventReader, autoCloseInput); return StreamReaderImpl.construct(CharSourceBootstrapper.construct(cfg, r)); } if (sysId != null && sysId.length() > 0) { /* If we must construct URL from system id, caller will not have * access to resulting stream, need to force auto-closing. */ autoCloseInput = true; ReaderConfig cfg = getNonSharedConfig(pubId, sysId, encoding, forEventReader, autoCloseInput); try { URL url = URLUtil.urlFromSystemId(sysId); in = URLUtil.inputStreamFromURL(url); return StreamReaderImpl.construct(ByteSourceBootstrapper.construct(cfg, in)); } catch (IOException ioe) { throw new IoStreamException(ioe); } } throw new XMLStreamException("Can not create Stax reader for the Source passed -- neither reader, input stream nor system id was accessible; can not use other types of sources (like embedded SAX streams)"); } protected XMLStreamReader2 constructSR2(Stax2Source ss, boolean forEventReader) throws XMLStreamException { /* Caller has no access to these input sources, so we must force * auto-close ('true' after 'forEventReader') */ ReaderConfig cfg = getNonSharedConfig(ss.getPublicId(), ss.getSystemId(), ss.getEncoding(), forEventReader, true); // Byte arrays can be accessed VERY efficiently... if (ss instanceof Stax2ByteArraySource) { Stax2ByteArraySource bs = (Stax2ByteArraySource) ss; return StreamReaderImpl.construct(ByteSourceBootstrapper.construct (cfg, bs.getBuffer(), bs.getBufferStart(), bs.getBufferLength())); } if (ss instanceof Stax2CharArraySource) { Stax2CharArraySource cs = (Stax2CharArraySource) ss; return StreamReaderImpl.construct(CharSourceBootstrapper.construct (cfg, cs.getBuffer(), cs.getBufferStart(), cs.getBufferLength())); } /* Ok, and this is the default, if we don't know a better * type-specific method: */ try { InputStream in = ss.constructInputStream(); if (in != null) { return StreamReaderImpl.construct(ByteSourceBootstrapper.construct(cfg, in)); } Reader r = ss.constructReader(); if (r != null) { return StreamReaderImpl.construct(CharSourceBootstrapper.construct(cfg, r)); } } catch (IOException ioe) { throw new IoStreamException(ioe); } throw new IllegalArgumentException("Can not create stream reader for given Stax2Source: neither InputStream nor Reader available"); } protected XMLStreamReader2 constructSR(URL src, boolean forEventReader) throws XMLStreamException { InputStream in; try { in = URLUtil.inputStreamFromURL(src); } catch (IOException ioe) { throw new IoStreamException(ioe); } // Construct from URL? Must auto-close: ReaderConfig cfg = getNonSharedConfig(URLUtil.urlToSystemId(src), null, null, forEventReader, true); return StreamReaderImpl.construct(ByteSourceBootstrapper.construct(cfg, in)); } protected XMLStreamReader2 constructSR(File f, boolean forEventReader) throws XMLStreamException { try { InputStream in = new FileInputStream(f); String systemId = URLUtil.fileToSystemId(f); // Construct from File? Must auto-close: ReaderConfig cfg = getNonSharedConfig(systemId, null, null, forEventReader, true); return StreamReaderImpl.construct(ByteSourceBootstrapper.construct(cfg, in)); } catch (IOException ioe) { throw new IoStreamException(ioe); } } public XMLEventReader2 constructER(XMLStreamReader2 sr) { return new EventReaderImpl(createEventAllocator(), sr); } protected XMLEventAllocator createEventAllocator() { // Explicitly set allocate? if (_allocator != null) { return _allocator.newInstance(); } // Complete or fast one? return _config.willPreserveLocation() ? EventAllocatorImpl.getDefaultInstance() : EventAllocatorImpl.getFastInstance(); } }