All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.axiom.util.stax.xop.XOPEncodingStreamReader Maven / Gradle / Ivy

There is a newer version: 1.4.0
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * 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 org.apache.axiom.util.stax.xop;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import javax.activation.DataHandler;
import javax.xml.namespace.NamespaceContext;
import javax.xml.namespace.QName;
import javax.xml.stream.Location;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;

import org.apache.axiom.ext.stax.datahandler.DataHandlerReader;

/**
 * {@link XMLStreamReader} wrapper that encodes XOP. It assumes that the underlying reader
 * implements the extension defined by {@link DataHandlerReader} so that it can identify the
 * information items to optimize (by looking for
 * {@link javax.xml.stream.XMLStreamConstants#CHARACTERS} events for which
 * {@link DataHandlerReader#isBinary()} returns true). The {@link DataHandler}
 * objects for the parts referenced by xop:Include element information items produced by
 * an instance of this class can be retrieved using the {@link #getDataHandler(String)} method.
 * 

* Note that the primary purpose of this class is not to serialize an XML infoset to an XOP package * (this is better done using {@link XOPEncodingStreamWriter}), but rather to optimize interaction * (by exchanging {@link DataHandler} objects instead of base64 encoded representations) with * databinding frameworks that understand XOP, but that are not aware of the * {@link DataHandlerReader} extension. *

* This class defers loading of {@link DataHandler} objects until {@link #getDataHandler(String)} is * called, except if this is not supported by the underlying stream. */ public class XOPEncodingStreamReader extends XOPEncodingStreamWrapper implements XMLStreamReader { /** * Wrapper that adds the XOP namespace to another namespace context. */ private static class NamespaceContextWrapper implements NamespaceContext { private static final List xopPrefixList = Arrays.asList(new String[] { XOPConstants.DEFAULT_PREFIX }); private final NamespaceContext parent; public NamespaceContextWrapper(NamespaceContext parent) { this.parent = parent; } public String getNamespaceURI(String prefix) { return XOPConstants.DEFAULT_PREFIX.equals(prefix) ? XOPConstants.NAMESPACE_URI : parent.getNamespaceURI(prefix); } public String getPrefix(String namespaceURI) { return XOPConstants.NAMESPACE_URI.equals(namespaceURI) ? XOPConstants.DEFAULT_PREFIX : parent.getPrefix(namespaceURI); } public Iterator getPrefixes(String namespaceURI) { Iterator prefixes = parent.getPrefixes(namespaceURI); if (XOPConstants.NAMESPACE_URI.equals(namespaceURI)) { if (!prefixes.hasNext()) { return xopPrefixList.iterator(); } else { // This case is very unusual List prefixList = new ArrayList(); do { prefixList.add(prefixes.next()); } while (prefixes.hasNext()); prefixList.add(XOPConstants.DEFAULT_PREFIX); return prefixList.iterator(); } } else { return prefixes; } } } private static final int STATE_PASS_THROUGH = 0; private static final int STATE_XOP_INCLUDE_START_ELEMENT = 1; private static final int STATE_XOP_INCLUDE_END_ELEMENT = 2; private final XMLStreamReader parent; private final DataHandlerReader dataHandlerReader; private int state = STATE_PASS_THROUGH; private String currentContentID; /** * Constructor. * * @param parent * The XML stream to encode. The reader must implement the extension defined by * {@link DataHandlerReader}. * @param contentIDGenerator * used to generate content IDs for the binary content exposed as * xop:Include element information items * @param optimizationPolicy * the policy to apply to decide which binary content to optimize * * @throws IllegalArgumentException * if the provided {@link XMLStreamReader} doesn't implement the extension defined * by {@link DataHandlerReader} */ public XOPEncodingStreamReader(XMLStreamReader parent, ContentIDGenerator contentIDGenerator, OptimizationPolicy optimizationPolicy) { super(contentIDGenerator, optimizationPolicy); this.parent = parent; DataHandlerReader dataHandlerReader; try { dataHandlerReader = (DataHandlerReader)parent.getProperty(DataHandlerReader.PROPERTY); } catch (IllegalArgumentException ex) { dataHandlerReader = null; } if (dataHandlerReader == null) { throw new IllegalArgumentException("The supplied XMLStreamReader doesn't implement the DataHandlerReader extension"); } this.dataHandlerReader = dataHandlerReader; } public int next() throws XMLStreamException { switch (state) { case STATE_XOP_INCLUDE_START_ELEMENT: state = STATE_XOP_INCLUDE_END_ELEMENT; return END_ELEMENT; case STATE_XOP_INCLUDE_END_ELEMENT: state = STATE_PASS_THROUGH; currentContentID = null; // Fall through default: int event = parent.next(); if (event == CHARACTERS && dataHandlerReader.isBinary()) { String contentID; try { if (dataHandlerReader.isDeferred()) { contentID = processDataHandler( dataHandlerReader.getDataHandlerProvider(), dataHandlerReader.getContentID(), dataHandlerReader.isOptimized()); } else { contentID = processDataHandler( dataHandlerReader.getDataHandler(), dataHandlerReader.getContentID(), dataHandlerReader.isOptimized()); } } catch (IOException ex) { throw new XMLStreamException("Error while processing data handler", ex); } if (contentID != null) { currentContentID = contentID; state = STATE_XOP_INCLUDE_START_ELEMENT; return START_ELEMENT; } else { return CHARACTERS; } } else { return event; } } } public boolean hasNext() throws XMLStreamException { return state == STATE_PASS_THROUGH ? parent.hasNext() : true; } public int nextTag() throws XMLStreamException { switch (state) { case STATE_XOP_INCLUDE_START_ELEMENT: state = STATE_XOP_INCLUDE_END_ELEMENT; return END_ELEMENT; case STATE_XOP_INCLUDE_END_ELEMENT: currentContentID = null; // Fall through default: return parent.nextTag(); } } public void require(int type, String namespaceURI, String localName) throws XMLStreamException { if (state == STATE_PASS_THROUGH) { parent.require(type, namespaceURI, localName); } else { if (state == STATE_XOP_INCLUDE_START_ELEMENT && type != START_ELEMENT || state == STATE_XOP_INCLUDE_END_ELEMENT && type != END_ELEMENT || namespaceURI != null && !namespaceURI.equals(XOPConstants.NAMESPACE_URI) || localName != null && !localName.equals(XOPConstants.INCLUDE)) { throw new XMLStreamException(); } } } public Location getLocation() { return parent.getLocation(); } public void close() throws XMLStreamException { parent.close(); } public Object getProperty(String name) throws IllegalArgumentException { return parent.getProperty(name); } public String getEncoding() { return parent.getEncoding(); } public String getCharacterEncodingScheme() { return parent.getCharacterEncodingScheme(); } public String getVersion() { return parent.getVersion(); } public boolean isStandalone() { return parent.isStandalone(); } public boolean standaloneSet() { return parent.standaloneSet(); } public String getPIData() { return parent.getPIData(); } public String getPITarget() { return parent.getPITarget(); } public int getAttributeCount() { switch (state) { case STATE_XOP_INCLUDE_START_ELEMENT: return 1; case STATE_XOP_INCLUDE_END_ELEMENT: throw new IllegalStateException(); default: return parent.getAttributeCount(); } } public String getAttributeLocalName(int index) { switch (state) { case STATE_XOP_INCLUDE_START_ELEMENT: if (index != 0) { throw new IllegalArgumentException(); } return XOPConstants.HREF; case STATE_XOP_INCLUDE_END_ELEMENT: throw new IllegalStateException(); default: return parent.getAttributeLocalName(index); } } public QName getAttributeName(int index) { switch (state) { case STATE_XOP_INCLUDE_START_ELEMENT: if (index != 0) { throw new IllegalArgumentException(); } return new QName(XOPConstants.HREF); case STATE_XOP_INCLUDE_END_ELEMENT: throw new IllegalStateException(); default: return parent.getAttributeName(index); } } public String getAttributeNamespace(int index) { switch (state) { case STATE_XOP_INCLUDE_START_ELEMENT: if (index != 0) { throw new IllegalArgumentException(); } return null; case STATE_XOP_INCLUDE_END_ELEMENT: throw new IllegalStateException(); default: return parent.getAttributeNamespace(index); } } public String getAttributePrefix(int index) { switch (state) { case STATE_XOP_INCLUDE_START_ELEMENT: if (index != 0) { throw new IllegalArgumentException(); } return null; case STATE_XOP_INCLUDE_END_ELEMENT: throw new IllegalStateException(); default: return parent.getAttributePrefix(index); } } public String getAttributeType(int index) { switch (state) { case STATE_XOP_INCLUDE_START_ELEMENT: if (index != 0) { throw new IllegalArgumentException(); } return "CDATA"; case STATE_XOP_INCLUDE_END_ELEMENT: throw new IllegalStateException(); default: return parent.getAttributeType(index); } } public String getAttributeValue(int index) { switch (state) { case STATE_XOP_INCLUDE_START_ELEMENT: if (index != 0) { throw new IllegalArgumentException(); } // We don't use full URL encoding here, because this might cause // interoperability issues. The specs (RFC 2111 and 2392) are not very clear // on which characters should be URL encoded, but one can consider that '%' // is the only really unsafe character. return "cid:" + currentContentID.replaceAll("%", "%25"); case STATE_XOP_INCLUDE_END_ELEMENT: throw new IllegalStateException(); default: return parent.getAttributeValue(index); } } public boolean isAttributeSpecified(int index) { switch (state) { case STATE_XOP_INCLUDE_START_ELEMENT: if (index != 0) { throw new IllegalArgumentException(); } return true; case STATE_XOP_INCLUDE_END_ELEMENT: throw new IllegalStateException(); default: return parent.isAttributeSpecified(index); } } public String getAttributeValue(String namespaceURI, String localName) { switch (state) { case STATE_XOP_INCLUDE_START_ELEMENT: if ((namespaceURI == null || namespaceURI.length() == 0) && localName.equals(XOPConstants.HREF)) { return "cid:" + currentContentID; } else { return null; } case STATE_XOP_INCLUDE_END_ELEMENT: throw new IllegalStateException(); default: return parent.getAttributeValue(namespaceURI, localName); } } public String getElementText() throws XMLStreamException { switch (state) { case STATE_XOP_INCLUDE_START_ELEMENT: state = STATE_XOP_INCLUDE_END_ELEMENT; return ""; case STATE_XOP_INCLUDE_END_ELEMENT: throw new IllegalStateException(); default: return parent.getElementText(); } } public int getEventType() { switch (state) { case STATE_XOP_INCLUDE_START_ELEMENT: return START_ELEMENT; case STATE_XOP_INCLUDE_END_ELEMENT: return END_ELEMENT; default: return parent.getEventType(); } } public String getNamespaceURI() { return state == STATE_PASS_THROUGH ? parent.getNamespaceURI() : XOPConstants.NAMESPACE_URI; } public String getLocalName() { return state == STATE_PASS_THROUGH ? parent.getLocalName() : XOPConstants.INCLUDE; } public String getPrefix() { return state == STATE_PASS_THROUGH ? parent.getPrefix() : XOPConstants.DEFAULT_PREFIX; } public QName getName() { return state == STATE_PASS_THROUGH ? parent.getName() : XOPConstants.INCLUDE_QNAME; } public NamespaceContext getNamespaceContext() { NamespaceContext ctx = parent.getNamespaceContext(); if (state != STATE_PASS_THROUGH) { ctx = new NamespaceContextWrapper(ctx); } return ctx; } public String getNamespaceURI(String prefix) { if (state != STATE_PASS_THROUGH && XOPConstants.DEFAULT_PREFIX.equals(prefix)) { return XOPConstants.NAMESPACE_URI; } else { return parent.getNamespaceURI(prefix); } } public int getNamespaceCount() { return state == STATE_PASS_THROUGH ? parent.getNamespaceCount() : 1; } public String getNamespacePrefix(int index) { if (state == STATE_PASS_THROUGH) { return parent.getNamespacePrefix(index); } else if (index != 0) { throw new IllegalArgumentException(); } else { return XOPConstants.DEFAULT_PREFIX; } } public String getNamespaceURI(int index) { if (state == STATE_PASS_THROUGH) { return parent.getNamespaceURI(index); } else if (index != 0) { throw new IllegalArgumentException(); } else { return XOPConstants.NAMESPACE_URI; } } public String getText() { if (state == STATE_PASS_THROUGH) { return parent.getText(); } else { throw new IllegalStateException(); } } public int getTextStart() { if (state == STATE_PASS_THROUGH) { return parent.getTextStart(); } else { throw new IllegalStateException(); } } public int getTextLength() { if (state == STATE_PASS_THROUGH) { return parent.getTextLength(); } else { throw new IllegalStateException(); } } public char[] getTextCharacters() { if (state == STATE_PASS_THROUGH) { return parent.getTextCharacters(); } else { throw new IllegalStateException(); } } public int getTextCharacters(int sourceStart, char[] target, int targetStart, int length) throws XMLStreamException { if (state == STATE_PASS_THROUGH) { return parent.getTextCharacters(sourceStart, target, targetStart, length); } else { throw new IllegalStateException(); } } public boolean hasName() { return state == STATE_PASS_THROUGH ? parent.hasName() : true; } public boolean hasText() { return state == STATE_PASS_THROUGH ? parent.hasText() : false; } public boolean isCharacters() { return state == STATE_PASS_THROUGH ? parent.isCharacters() : false; } public boolean isWhiteSpace() { return state == STATE_PASS_THROUGH ? parent.isWhiteSpace() : false; } public boolean isStartElement() { switch (state) { case STATE_XOP_INCLUDE_START_ELEMENT: return true; case STATE_XOP_INCLUDE_END_ELEMENT: return false; default: return parent.isStartElement(); } } public boolean isEndElement() { switch (state) { case STATE_XOP_INCLUDE_START_ELEMENT: return false; case STATE_XOP_INCLUDE_END_ELEMENT: return true; default: return parent.isEndElement(); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy