package org.jibx.runtime.impl; import javax.xml.stream.Location; import javax.xml.stream.XMLStreamConstants; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; import org.jibx.runtime.IXMLReader; import org.jibx.runtime.IntStack; import org.jibx.runtime.JiBXException; /** * Wrapper for a StAX parser implementation. This delegates most calls * more or less directly, only adding the required namespace functionality * on top of the StAX API. */ public class StAXReaderWrapper implements IXMLReader { /** Event type code translation array. Indexed by the StAX event code, it returns the corresponding XML reader event code. */ static final byte[] s_eventTranslations = new byte[256]; static { s_eventTranslations[XMLStreamConstants.CDATA] = IXMLReader.CDSECT; s_eventTranslations[XMLStreamConstants.CHARACTERS] = IXMLReader.TEXT; s_eventTranslations[XMLStreamConstants.COMMENT] = IXMLReader.COMMENT; s_eventTranslations[XMLStreamConstants.DTD] = IXMLReader.DOCDECL; s_eventTranslations[XMLStreamConstants.END_DOCUMENT] = IXMLReader.END_DOCUMENT; s_eventTranslations[XMLStreamConstants.END_ELEMENT] = IXMLReader.END_TAG; s_eventTranslations[XMLStreamConstants.ENTITY_REFERENCE] = IXMLReader.ENTITY_REF; s_eventTranslations[XMLStreamConstants.PROCESSING_INSTRUCTION] = IXMLReader.PROCESSING_INSTRUCTION; s_eventTranslations[XMLStreamConstants.SPACE] = IXMLReader.IGNORABLE_WHITESPACE; s_eventTranslations[XMLStreamConstants.START_DOCUMENT] = IXMLReader.START_DOCUMENT; s_eventTranslations[XMLStreamConstants.START_ELEMENT] = IXMLReader.START_TAG; } /** Actual parser. */ private final XMLStreamReader m_parser; /** Parser processing namespaces flag. */ final boolean m_isNamespaceAware; /** Document name. */ private final String m_docName; /** Current element nesting depth. */ int m_nestingDepth; /** Namespace definitions in scope at each nesting depth. */ private IntStack m_inScopeCounts; /** Namespace URIs in scope. */ private StringArray m_inScopeUris; /** Namespace prefixes in scope. */ private StringArray m_inScopePrefixes; /** Accumulated text for return. */ private String m_accumulatedText; /** Accumulated text is processing instruction flag (otherwise content) */ private boolean m_isProcessingInstruction; /** Document encoding (apparently cannot be read after parse done). */ private String m_encoding; /** * Constructor used by factory. * * @param rdr event reader * @param nsa namespace aware flag */ public StAXReaderWrapper(XMLStreamReader rdr, String name, boolean nsa) { m_parser = rdr; m_docName = name; m_isNamespaceAware = nsa; m_inScopeCounts = new IntStack(); m_inScopeCounts.push(0); m_inScopeUris = new StringArray(); m_inScopePrefixes = new StringArray(); } /** * Build current parse input position description. * * @return text description of current parse position */ public String buildPositionString() { Location location = m_parser.getLocation(); String base = "(line " + location.getLineNumber() + ", col " + location.getColumnNumber(); if (m_docName != null) { base += ", in " + m_docName; } return base + ')'; } /** * Handle start tag. This increments the nesting count, and records all * namespaces associated with the start tag. */ private void startTag() { if (m_nestingDepth == 0) { m_encoding = m_parser.getEncoding(); if (m_encoding == null) { m_encoding = m_parser.getCharacterEncodingScheme(); if (m_encoding == null) { m_encoding = "UTF-8"; } } } m_nestingDepth++; int count = m_parser.getNamespaceCount(); for (int i = 0; i < count; i++) { m_inScopeUris.add(m_parser.getNamespaceURI(i)); m_inScopePrefixes.add(m_parser.getNamespacePrefix(i)); } m_inScopeCounts.push(m_inScopeUris.size()); } /** * Handle end tag. This decrements the nesting count, and deletes all * namespaces associated with the start tag. */ private void endTag() { m_nestingDepth--; int count = m_inScopeCounts.pop() - m_inScopeCounts.peek(); if (count > 0) { m_inScopeUris.remove(count); m_inScopePrefixes.remove(count); } } /* (non-Javadoc) * @see org.jibx.runtime.IXMLReader#nextToken() */ public int nextToken() throws JiBXException { if (m_accumulatedText == null) { try { int code; loop: while (true) { code = s_eventTranslations[m_parser.next()]; switch (code) { case START_TAG: startTag(); break loop; case END_TAG: endTag(); break loop; case PROCESSING_INSTRUCTION: m_accumulatedText = m_parser.getPITarget() + ' ' + m_parser.getPIData(); m_isProcessingInstruction = true; while (s_eventTranslations[m_parser.next()] == 0); break loop; case 0: break; default: break loop; } } return code; } catch (XMLStreamException e) { throw new JiBXException ("Error parsing document " + buildPositionString(), e); } } else { m_accumulatedText = null; m_isProcessingInstruction = false; int code = s_eventTranslations[m_parser.getEventType()]; if (code == START_TAG) { startTag(); } else if (code == END_TAG) { endTag(); } return code; } } /* (non-Javadoc) * @see org.jibx.runtime.IXMLReader#next() */ public int next() throws JiBXException { String text = null; StringBuffer buff = null; try { int type; if (m_accumulatedText == null) { m_parser.next(); } else { m_accumulatedText = null; m_isProcessingInstruction = false; } loop: while (true) { type = s_eventTranslations[m_parser.getEventType()]; switch (type) { case ENTITY_REF: if (m_parser.getText() == null) { throw new JiBXException ("Unexpanded entity reference in text at " + buildPositionString()); } // fall through into text accumulation case CDSECT: case TEXT: if (text == null) { text = m_parser.getText(); } else { if (buff == null) { buff = new StringBuffer(text); } buff.append(m_parser.getTextCharacters()); } break; case END_TAG: if (text == null) { endTag(); return type; } break loop; case START_TAG: if (text == null) { startTag(); return type; } break loop; case END_DOCUMENT: if (text == null) { return type; } break loop; default: break; } m_parser.next(); } if (buff == null) { m_accumulatedText = text; } else { m_accumulatedText = buff.toString(); } return TEXT; } catch (XMLStreamException e) { throw new JiBXException ("Error parsing document " + buildPositionString(), e); } } /* (non-Javadoc) * @see org.jibx.runtime.IXMLReader#getEventType() */ public int getEventType() throws JiBXException { if (m_accumulatedText == null) { return s_eventTranslations[m_parser.getEventType()]; } else if (m_isProcessingInstruction) { return PROCESSING_INSTRUCTION; } else { return TEXT; } } /* (non-Javadoc) * @see org.jibx.runtime.IXMLReader#getName() */ public String getName() { return m_parser.getLocalName(); } /* (non-Javadoc) * @see org.jibx.runtime.IXMLReader#getNamespace() */ public String getNamespace() { String uri = m_parser.getNamespaceURI(); if (uri == null) { return ""; } else { return uri; } } /* (non-Javadoc) * @see org.jibx.runtime.IXMLReader#getPrefix() */ public String getPrefix() { String prefix = m_parser.getPrefix(); if (prefix != null && prefix.length() == 0) { return null; } else { return prefix; } } /* (non-Javadoc) * @see org.jibx.runtime.IXMLReader#getAttributeCount() */ public int getAttributeCount() { return m_parser.getAttributeCount(); } /* (non-Javadoc) * @see org.jibx.runtime.IXMLReader#getAttributeName(int) */ public String getAttributeName(int index) { try { return m_parser.getAttributeLocalName(index); } catch (ArrayIndexOutOfBoundsException e) { throw new IllegalStateException(e.getMessage()); } } /* (non-Javadoc) * @see org.jibx.runtime.IXMLReader#getAttributeNamespace(int) */ public String getAttributeNamespace(int index) { try { String uri = m_parser.getAttributeNamespace(index); if (uri == null) { return ""; } else { return uri; } } catch (ArrayIndexOutOfBoundsException e) { throw new IllegalStateException(e.getMessage()); } } /* (non-Javadoc) * @see org.jibx.runtime.IXMLReader#getAttributePrefix(int) */ public String getAttributePrefix(int index) { try { String prefix = m_parser.getAttributePrefix(index); if (prefix != null && prefix.length() == 0) { return null; } else { return prefix; } } catch (ArrayIndexOutOfBoundsException e) { throw new IllegalStateException(e.getMessage()); } } /* (non-Javadoc) * @see org.jibx.runtime.IXMLReader#getAttributeValue(int) */ public String getAttributeValue(int index) { try { return m_parser.getAttributeValue(index); } catch (ArrayIndexOutOfBoundsException e) { throw new IllegalStateException(e.getMessage()); } } /* (non-Javadoc) * @see org.jibx.runtime.IXMLReader#getAttributeValue(java.lang.String, java.lang.String) */ public String getAttributeValue(String ns, String name) { try { return m_parser.getAttributeValue(ns, name); } catch (ArrayIndexOutOfBoundsException e) { throw new IllegalStateException(e.getMessage()); } } /* (non-Javadoc) * @see org.jibx.runtime.IXMLReader#getText() */ public String getText() { if (m_accumulatedText == null) { return m_parser.getText(); } else { return m_accumulatedText; } } /* (non-Javadoc) * @see org.jibx.runtime.IXMLReader#getNestingDepth() */ public int getNestingDepth() { return m_nestingDepth; } /* (non-Javadoc) * @see org.jibx.runtime.IXMLReader#getNamespaceCount(int) */ public int getNamespaceCount(int depth) { return m_inScopeCounts.peek(m_nestingDepth-depth); } /* (non-Javadoc) * @see org.jibx.runtime.IXMLReader#getNamespaceUri(int) */ public String getNamespaceUri(int index) { return m_inScopeUris.get(index); } /* (non-Javadoc) * @see org.jibx.runtime.IXMLReader#getNamespacePrefix(int) */ public String getNamespacePrefix(int index) { String prefix = m_inScopePrefixes.get(index); if (prefix != null && prefix.length() == 0) { return null; } else { return prefix; } } /* (non-Javadoc) * @see org.jibx.runtime.IXMLReader#getDocumentName() */ public String getDocumentName() { return m_docName; } /* (non-Javadoc) * @see org.jibx.runtime.IXMLReader#getLineNumber() */ public int getLineNumber() { return m_parser.getLocation().getLineNumber(); } /* (non-Javadoc) * @see org.jibx.runtime.IXMLReader#getColumnNumber() */ public int getColumnNumber() { return m_parser.getLocation().getColumnNumber(); } /* (non-Javadoc) * @see org.jibx.runtime.IXMLReader#getNamespace(java.lang.String) */ public String getNamespace(String prefix) { int index = m_inScopePrefixes.size(); while (--index >= 0) { String comp = m_inScopePrefixes.get(index); if ((prefix == null && comp == null) || (prefix != null && prefix.equals(comp))) { return m_inScopeUris.get(index); } } return null; } /* (non-Javadoc) * @see org.jibx.runtime.IXMLReader#getInputEncoding() */ public String getInputEncoding() { return m_encoding; } /* (non-Javadoc) * @see org.jibx.runtime.IXMLReader#isNamespaceAware() */ public boolean isNamespaceAware() { return m_isNamespaceAware; } }