/*
* Copyright 2011, 2012 Odysseus Software GmbH
*
* Licensed 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.synapse.commons.staxon.core.json.jaxb;
import java.io.Reader;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.XMLStreamWriter;
import org.apache.synapse.commons.staxon.core.json.JsonXMLConfig;
import org.apache.synapse.commons.staxon.core.json.JsonXMLConfigBuilder;
import org.apache.synapse.commons.staxon.core.json.JsonXMLInputFactory;
import org.apache.synapse.commons.staxon.core.json.JsonXMLOutputFactory;
import org.apache.synapse.commons.staxon.core.json.JsonXMLStreamConstants;
import org.apache.synapse.commons.staxon.core.json.util.XMLMultipleStreamWriter;
/**
* Read/write instances of JAXB-annotated classes from/to JSON.
*/
public class JsonXMLBinder {
private final JsonXMLRootProvider rootProvider;
private final boolean writeDocumentArray;
public JsonXMLBinder() {
this(true);
}
protected JsonXMLBinder(boolean writeDocumentArray) {
this(new JsonXMLRootProvider(), writeDocumentArray);
}
protected JsonXMLBinder(JsonXMLRootProvider rootProvider, boolean writeDocumentArray) {
this.rootProvider = rootProvider;
this.writeDocumentArray = writeDocumentArray;
}
private JsonXMLConfig toJsonXMLConfig(Class<?> type, JsonXML config) throws JAXBException {
return new JsonXMLConfigBuilder().
autoArray(config.autoArray()).
autoPrimitive(config.autoPrimitive()).
multiplePI(true).
namespaceDeclarations(config.namespaceDeclarations()).
namespaceSeparator(config.namespaceSeparator()).
prettyPrint(config.prettyPrint()).
virtualRoot(config.virtualRoot() ? rootProvider.getName(type) : null).
build();
}
protected JsonXMLInputFactory createInputFactory(Class<?> type, JsonXML config) throws JAXBException {
return new JsonXMLInputFactory(toJsonXMLConfig(type, config));
}
protected XMLStreamReader createXMLStreamReader(Class<?> type, JsonXML config, Reader stream) throws XMLStreamException, JAXBException {
return createInputFactory(type, config).createXMLStreamReader(stream);
}
protected JsonXMLOutputFactory createOutputFactory(Class<?> type, JsonXML config) throws JAXBException {
return new JsonXMLOutputFactory(toJsonXMLConfig(type, config));
}
protected XMLStreamWriter createXMLStreamWriter(Class<?> type, JsonXML config, Writer stream) throws XMLStreamException, JAXBException {
XMLStreamWriter writer = createOutputFactory(type, config).createXMLStreamWriter(stream);
if (config.multiplePaths().length > 0) {
writer = new XMLMultipleStreamWriter(writer, !config.virtualRoot(), config.multiplePaths());
}
return writer;
}
public boolean isBindable(Class<?> type) {
return type.isAnnotationPresent(XmlRootElement.class) || type.isAnnotationPresent(XmlType.class);
}
private void checkBindable(Class<?> type) throws JAXBException {
if (!isBindable(type)) {
throw new JAXBException("Cannot bind type: " + type.getName());
}
}
protected <T> T unmarshal(Class<? extends T> type, JsonXML config, Unmarshaller unmarshaller, XMLStreamReader reader) throws JAXBException, XMLStreamException {
if (type.isAnnotationPresent(XmlRootElement.class)) {
return type.cast(unmarshaller.unmarshal(reader));
} else if (type.isAnnotationPresent(XmlType.class)) {
return unmarshaller.unmarshal(reader, type).getValue();
} else { // good luck
return type.cast(unmarshaller.unmarshal(reader, type));
}
}
protected void marshal(Class<?> type, JsonXML config, Marshaller marshaller, XMLStreamWriter writer, Object value)
throws JAXBException, XMLStreamException {
Object element = null;
if (type.isAnnotationPresent(XmlRootElement.class)) {
element = value;
} else if (type.isAnnotationPresent(XmlType.class)) {
element = rootProvider.createElement(type, value);
if (element == null) {
throw new JAXBException("Cannot create JAXBElement");
}
} else { // good luck...
element = value;
}
marshaller.marshal(element, writer);
}
public <T> T readObject(Class<? extends T> type, JsonXML config, JAXBContext context, Reader stream)
throws XMLStreamException, JAXBException {
checkBindable(type);
XMLStreamReader reader = createXMLStreamReader(type, config, stream);
T result;
if (reader.isCharacters() && reader.getText() == null) { // hack: read null
result = null;
} else {
reader.require(XMLStreamConstants.START_DOCUMENT, null, null);
Unmarshaller unmarshaller = context.createUnmarshaller();
result = unmarshal(type, config, unmarshaller, reader);
reader.require(XMLStreamConstants.END_DOCUMENT, null, null);
}
reader.close();
return result;
}
public void writeObject(Class<?> type, JsonXML config, JAXBContext context, Writer stream, Object value)
throws XMLStreamException, JAXBException {
checkBindable(type);
XMLStreamWriter writer = createXMLStreamWriter(type, config, stream);
if (value == null) { // hack: write null
writer.writeCharacters(null);
} else {
Marshaller marshaller = context.createMarshaller();
marshal(type, config, marshaller, writer, value);
}
writer.close();
}
public <T> List<T> readArray(Class<? extends T> type, JsonXML config, JAXBContext context, Reader stream)
throws XMLStreamException, JAXBException {
checkBindable(type);
XMLStreamReader reader = createXMLStreamReader(type, config, stream);
List<T> result;
if (reader.isCharacters() && reader.getText() == null) { // hack: read null
result = null;
} else {
boolean documentArray = JsonXMLStreamConstants.MULTIPLE_PI_TARGET.equals(reader.getPITarget());
Unmarshaller unmarshaller = context.createUnmarshaller();
while (reader.hasNext() && !reader.isStartElement() && !reader.isCharacters()) {
reader.next();
}
result = new ArrayList<T>();
while (reader.hasNext() || reader.isCharacters() && reader.getText() == null) {
if (reader.isCharacters() && reader.getText() == null) { // hack: read null
result.add(null);
if (reader.hasNext()) {
reader.next();
} else {
break;
}
} else {
result.add(unmarshal(type, config, unmarshaller, reader));
if (documentArray && reader.hasNext()) { // move to next document
reader.next();
}
}
}
}
reader.close();
return result;
}
public void writeArray(Class<?> type, JsonXML config, JAXBContext context, Writer stream, Collection<?> collection)
throws XMLStreamException, JAXBException {
checkBindable(type);
XMLStreamWriter writer = createXMLStreamWriter(type, config, stream);
if (collection == null) { // hack: write null
writer.writeCharacters(null);
} else {
Marshaller marshaller = context.createMarshaller();
if (!writeDocumentArray) {
writer.writeStartDocument();
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);
}
writer.writeProcessingInstruction(JsonXMLStreamConstants.MULTIPLE_PI_TARGET);
for (Object value : collection) {
if (value == null) { // hack: write null
writer.writeCharacters(null);
} else {
marshal(type, config, marshaller, writer, value);
}
}
if (!writeDocumentArray) {
writer.writeEndDocument();
}
}
writer.close();
}
}