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 jersey-bundle Show documentation
Show all versions of jersey-bundle Show documentation
A bundle containing code of all jar-based modules that provide
JAX-RS and Jersey-related features. Such a bundle is *only intended* for
developers that do not use Maven's dependency system.
The bundle does not include code for contributes, tests and samples.
/*
* 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;
}
}