com.sun.jersey.json.impl.reader.JsonXmlStreamReader Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ehcache Show documentation
Show all versions of ehcache Show documentation
Ehcache is an open source, standards-based cache used to boost performance,
offload the database and simplify scalability. Ehcache is robust, proven and full-featured and
this has made it the most widely-used Java-based cache.
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2012 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* http://glassfish.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package com.sun.jersey.json.impl.reader;
import java.io.IOException;
import java.io.Reader;
import java.util.Collections;
import java.util.List;
import javax.xml.bind.JAXBContext;
import javax.xml.namespace.NamespaceContext;
import javax.xml.namespace.QName;
import javax.xml.stream.Location;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import com.sun.jersey.api.json.JSONConfiguration;
import com.sun.jersey.api.json.JSONJAXBContext;
import org.codehaus.jackson.JsonFactory;
import org.codehaus.jackson.JsonParser;
/**
* Implementation of {@link XMLStreamReader} for JSON streams in natural or mapped notation. This class contains a factory
* method for an instance creation.
*
* @author Jakub Podlesak (jakub.podlesak at oracle.com)
* @author Michal Gajdos (michal.gajdos at oracle.com)
*/
public class JsonXmlStreamReader implements XMLStreamReader {
/**
* Provider of Xml events.
*/
private final XmlEventProvider eventProvider;
/**
* Default namespace context for this class.
*/
private final JsonNamespaceContext namespaceContext = new JsonNamespaceContext();
/**
* Exception that occurred during processing of the JSON stream. This property is supposed to be used in methods that are not
* designed to throw the {@code XMLStreamException} directly (i.e. {@code #getAttributeXXX}).
*/
private XMLStreamException validationException;
/**
* Factory method for creating instances of this class.
*
* @param reader JSON input.
* @param configuration JSON configuration.
* @param rootName if non-{@code null} then the {@code JsonXmlStreamReader} emulates presence of root element with this
* name for JAXB provider.
* @param expectedType expected type of JAXB element.
* @param jaxbContext JAXB context.
* @param readingList flag whether it is expected that root is an JSON array instead of an object.
* @return an instance of JSON XML stream reader
* @throws XMLStreamException if an {@link IOException} has been thrown during the creation of an {@code JsonParser} instance.
*/
public static XMLStreamReader create(final Reader reader,
final JSONConfiguration configuration,
String rootName,
final Class> expectedType,
JAXBContext jaxbContext,
final boolean readingList) throws XMLStreamException {
try {
if ((rootName == null || "".equals(rootName)) && (configuration.isRootUnwrapping())) {
rootName = "rootElement";
}
final JsonParser rawParser = new JsonFactory().createJsonParser(reader);
final JsonParser nonListParser = configuration.isRootUnwrapping() ? JacksonRootAddingParser.createRootAddingParser
(rawParser, rootName) : rawParser;
XmlEventProvider eventStack = null;
switch (configuration.getNotation()) {
case MAPPED:
eventStack = new MappedNotationEventProvider(nonListParser, configuration, rootName);
break;
case NATURAL:
if (jaxbContext instanceof JSONJAXBContext) {
jaxbContext = ((JSONJAXBContext) jaxbContext).getOriginalJaxbContext();
}
if (!readingList) {
eventStack = new NaturalNotationEventProvider(nonListParser, configuration, rootName, jaxbContext, expectedType);
} else {
eventStack = new NaturalNotationEventProvider(
JacksonRootAddingParser.createRootAddingParser(nonListParser, "jsonArrayRootElement"),
configuration,
rootName,
jaxbContext,
expectedType);
}
break;
}
return new JsonXmlStreamReader(eventStack);
} catch (IOException ex) {
throw new XMLStreamException(ex);
}
}
private JsonXmlStreamReader(final XmlEventProvider nodeStack) {
this.eventProvider = nodeStack;
}
/**
* Returns a list of attribute of the current element. This method also checks if the parser is in the proper state ({@code
* XMLStreamConstants.START_ELEMENT} or {@code XMLStreamConstants.ATTRIBUTE}).
*
* @return list of the current elements attributes.
*/
private List getAttributes() {
if (getEventType() != XMLStreamConstants.START_ELEMENT
&& getEventType() != XMLStreamConstants.ATTRIBUTE) {
throw new IllegalArgumentException("Parser must be on START_ELEMENT or ATTRIBUTE to read next attribute.");
}
final JsonXmlEvent currentNode = eventProvider.getCurrentNode();
try {
if (currentNode.getAttributes() == null) {
eventProvider.processAttributesOfCurrentElement();
}
return currentNode.getAttributes();
} catch (XMLStreamException xse) {
// Cannot throw an exception from here - #getAttributeXXX methods doesn't support it - throw it when #next() method
// is invoked.
validationException = xse;
return Collections.emptyList();
}
}
/**
* Returns an attribute of the current element at given index.
*
* @param index index of an attribute to retrieve.
* @return attribute at given index or {@code null} if the index is outside of boundaries of the list of attributes.
*/
private JsonXmlEvent.Attribute getAttribute(int index) {
List attributes = getAttributes();
if (index < 0 || index >= attributes.size()) {
return null;
}
return attributes.get(index);
}
@Override
public void close() throws XMLStreamException {
eventProvider.close();
}
@Override
public int getAttributeCount() {
return getAttributes().size();
}
@Override
public String getAttributeLocalName(int index) {
JsonXmlEvent.Attribute attribute = getAttribute(index);
return attribute == null ? null : attribute.getName().getLocalPart();
}
@Override
public QName getAttributeName(int index) {
JsonXmlEvent.Attribute attribute = getAttribute(index);
return attribute == null ? null : attribute.getName();
}
@Override
public String getAttributeNamespace(int index) {
JsonXmlEvent.Attribute attribute = getAttribute(index);
return attribute == null ? null : attribute.getName().getNamespaceURI();
}
@Override
public String getAttributePrefix(int index) {
JsonXmlEvent.Attribute attribute = getAttribute(index);
return attribute == null ? null : attribute.getName().getPrefix();
}
@Override
public String getAttributeType(int index) {
return null;
}
@Override
public String getAttributeValue(String namespaceURI, String localName) {
if (localName == null || "".equals(localName)) {
return null;
}
for (JsonXmlEvent.Attribute attribute : getAttributes()) {
if (localName.equals(attribute.getName().getLocalPart())
&& ((namespaceURI == null) || (namespaceURI.equals(attribute.getName().getNamespaceURI())))) {
return attribute.getValue();
}
}
return null;
}
@Override
public String getAttributeValue(int index) {
JsonXmlEvent.Attribute attribute = getAttribute(index);
return attribute == null ? null : attribute.getValue();
}
@Override
public String getCharacterEncodingScheme() {
return "UTF-8";
}
@Override
public String getElementText() throws XMLStreamException {
if(getEventType() != XMLStreamConstants.START_ELEMENT) {
throw new XMLStreamException(
"Parser must be on START_ELEMENT to read next text.", getLocation());
}
int eventType = next();
StringBuilder content = new StringBuilder();
while (eventType != XMLStreamConstants.END_ELEMENT) {
if(eventType == XMLStreamConstants.CHARACTERS
|| eventType == XMLStreamConstants.CDATA
|| eventType == XMLStreamConstants.SPACE
|| eventType == XMLStreamConstants.ENTITY_REFERENCE) {
content.append(getText());
} else if (eventType == XMLStreamConstants.PROCESSING_INSTRUCTION
|| eventType == XMLStreamConstants.COMMENT) {
// skipping
} else if (eventType == XMLStreamConstants.END_DOCUMENT) {
throw new XMLStreamException(
"Unexpected end of document when reading element text content.", getLocation());
} else if (eventType == XMLStreamConstants.START_ELEMENT) {
throw new XMLStreamException(
"Element text content may not contain START_ELEMENT.", getLocation());
} else {
throw new XMLStreamException(
"Unexpected event type " + eventType + ".", getLocation());
}
eventType = next();
}
return content.toString();
}
@Override
public String getEncoding() {
return "UTF-8";
}
@Override
public int getEventType() {
return eventProvider.getCurrentNode().getEventType();
}
@Override
public String getLocalName() {
final int eventType = getEventType();
if (eventType != XMLStreamReader.START_ELEMENT
&& eventType != XMLStreamReader.END_ELEMENT
&& eventType != XMLStreamReader.ENTITY_REFERENCE) {
throw new IllegalArgumentException(
"Parser must be on START_ELEMENT, END_ELEMENT or ENTITY_REFERENCE to read local name.");
}
return eventProvider.getCurrentNode().getName().getLocalPart();
}
@Override
public Location getLocation() {
return eventProvider.getCurrentNode().getLocation();
}
@Override
public QName getName() {
final int eventType = getEventType();
if (eventType != XMLStreamReader.START_ELEMENT
&& eventType != XMLStreamReader.END_ELEMENT) {
throw new IllegalArgumentException("Parser must be on START_ELEMENT or END_ELEMENT to read the name.");
}
return eventProvider.getCurrentNode().getName();
}
@Override
public NamespaceContext getNamespaceContext() {
return namespaceContext;
}
@Override
public int getNamespaceCount() {
return this.namespaceContext.getNamespaceCount();
}
@Override
public String getNamespacePrefix(int index) {
return null;
}
@Override
public String getNamespaceURI(String prefix) {
return null;
}
@Override
public String getNamespaceURI(int index) {
return null;
}
@Override
public String getNamespaceURI() {
final int eventType = getEventType();
if (eventType != XMLStreamReader.START_ELEMENT
&& eventType != XMLStreamReader.END_ELEMENT) {
throw new IllegalArgumentException("Parser must be on START_ELEMENT or END_ELEMENT to read the namespace URI.");
}
return eventProvider.getCurrentNode().getName().getNamespaceURI();
}
@Override
public String getPIData() {
return null;
}
@Override
public String getPITarget() {
return null;
}
@Override
public String getPrefix() {
return eventProvider.getCurrentNode().getPrefix();
}
@Override
public Object getProperty(String name) throws IllegalArgumentException {
if (name == null) {
throw new IllegalArgumentException("Name is null.");
}
return null;
}
@Override
public String getText() {
final int eventType = getEventType();
if(eventType == XMLStreamConstants.CHARACTERS
|| eventType == XMLStreamConstants.CDATA
|| eventType == XMLStreamConstants.SPACE
|| eventType == XMLStreamConstants.ENTITY_REFERENCE) {
return eventProvider.getCurrentNode().getText();
}
throw new IllegalArgumentException(
"Parser must be on CHARACTERS, CDATA, SPACE or ENTITY_REFERENCE to read text.");
}
@Override
public char[] getTextCharacters() {
final String text = getText();
return text != null ? text.toCharArray() : new char[0];
}
@Override
public int getTextCharacters(int sourceStart, char[] target, int targetStart, int length)
throws XMLStreamException {
getText().getChars(sourceStart, sourceStart + length, target, targetStart);
return length;
}
@Override
public int getTextLength() {
final String text = getText();
return text == null ? 0 : text.length();
}
@Override
public int getTextStart() {
return 0;
}
@Override
public String getVersion() {
return null;
}
@Override
public boolean hasName() {
final int eventType = getEventType();
if (eventType != XMLStreamReader.START_ELEMENT
&& eventType != XMLStreamReader.END_ELEMENT) {
throw new IllegalArgumentException("Parser must be on START_ELEMENT or END_ELEMENT to read the name.");
}
return eventProvider.getCurrentNode().getName() != null;
}
@Override
public boolean hasNext() throws XMLStreamException {
// Failure in the previous state?
if (validationException != null) {
throw validationException;
}
return eventProvider.getCurrentNode().getEventType() != XMLStreamConstants.END_DOCUMENT;
}
@Override
public boolean hasText() {
final int eventType = getEventType();
return eventType == XMLStreamConstants.CHARACTERS
|| eventType == XMLStreamConstants.CDATA
|| eventType == XMLStreamConstants.SPACE
|| eventType == XMLStreamConstants.ENTITY_REFERENCE
|| eventType == XMLStreamConstants.COMMENT
|| eventType == XMLStreamConstants.DTD;
}
@Override
public boolean isAttributeSpecified(int index) {
return false;
}
@Override
public boolean isCharacters() {
return eventProvider.getCurrentNode().getEventType() == XMLStreamConstants.CHARACTERS;
}
@Override
public boolean isEndElement() {
return eventProvider.getCurrentNode().getEventType() == XMLStreamConstants.END_ELEMENT;
}
@Override
public boolean isStandalone() {
return false;
}
@Override
public boolean isStartElement() {
return eventProvider.getCurrentNode().getEventType() == XMLStreamConstants.START_ELEMENT;
}
@Override
public boolean isWhiteSpace() {
return false; // JsonParser does not return any whitespace element.
}
@Override
public int next() throws XMLStreamException {
if (!hasNext()) {
throw new IllegalArgumentException("No more parsing elements.");
}
return eventProvider.readNext().getEventType();
}
@Override
public int nextTag() throws XMLStreamException {
int eventType = next();
while ((eventType == XMLStreamConstants.CHARACTERS && isWhiteSpace()) // skip whitespace
|| (eventType == XMLStreamConstants.CDATA && isWhiteSpace()) // skip whitespace
|| eventType == XMLStreamConstants.SPACE
|| eventType == XMLStreamConstants.PROCESSING_INSTRUCTION
|| eventType == XMLStreamConstants.COMMENT) {
eventType = next();
}
if (eventType != XMLStreamConstants.START_ELEMENT && eventType != XMLStreamConstants.END_ELEMENT) {
throw new XMLStreamException("Expected start or end tag.", getLocation());
}
return eventType;
}
@Override
public void require(int type, String namespaceURI, String localName) throws XMLStreamException {
}
@Override
public boolean standaloneSet() {
return false;
}
}