/*
* Copyright 2013 Gordon Burgett and individual contributors
*
* 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.xflatdb.xflat.convert.converters;
import java.beans.XMLDecoder;
import java.beans.XMLEncoder;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
import org.jdom2.output.XMLOutputter;
import org.xflatdb.xflat.convert.ConversionException;
import org.xflatdb.xflat.convert.ConversionNotSupportedException;
import org.xflatdb.xflat.convert.ConversionService;
import org.xflatdb.xflat.convert.Converter;
import org.xflatdb.xflat.convert.PojoConverter;
/**
* This PojoConverter maps Java beans to XML using {@link java.beans.XMLEncoder}.
* @author Gordon
*/
public class JavaBeansPojoConverter implements PojoConverter {
@Override
public ConversionService extend(ConversionService service) {
return new JavaBeansConversionService(service);
}
private static class JavaBeansConversionService implements ConversionService {
ConversionService base;
public JavaBeansConversionService(ConversionService base){
this.base = base;
}
@Override
public boolean canConvert(Class<?> source, Class<?> target) {
if(!base.canConvert(source, target)){
if(Element.class.equals(target))
makeConverters(source);
else if(Element.class.equals(source)){
makeConverters(target);
}
else{
//can't convert
return false;
}
}
//else the base can convert
return true;
}
@Override
public <T> T convert(Object source, Class<T> target) throws ConversionException {
try{
return base.convert(source, target);
}
catch(ConversionNotSupportedException ex){
if(source == null){
throw ex;
}
//the base class does not support the conversion - try to make converters
if(Element.class.equals(target))
makeConverters(source.getClass());
else if(Element.class.equals(source.getClass())){
makeConverters(target);
}
else{
//can't convert
throw ex;
}
//try again now that we successfully made converters
return base.convert(source, target);
}
}
@Override
public <S, T> void addConverter(Class<S> sourceType, Class<T> targetType, Converter<? super S, ? extends T> converter) {
base.addConverter(sourceType, targetType, converter);
}
@Override
public void removeConverter(Class<?> sourceType, Class<?> targetType) {
base.removeConverter(sourceType, targetType);
}
private <T> void makeConverters(Class<T> clazz){
base.addConverter(clazz, Element.class, XMLEncoderConverter);
base.addConverter(Element.class, clazz, getDecoder(clazz));
}
}
private static Converter<Object, Element> XMLEncoderConverter = new Converter<Object, Element>() {
@Override
public Element convert(Object source) throws ConversionException {
byte[] bytes;
try(ByteArrayOutputStream os = new ByteArrayOutputStream()){
try(XMLEncoder encoder = new XMLEncoder(os)){
encoder.writeObject(source);
}
bytes = os.toByteArray();
}
catch(IOException ex){
throw new ConversionException("Error converting object of class " + source.getClass(), ex);
}
try(ByteArrayInputStream is = new ByteArrayInputStream(bytes)){
Document doc = new SAXBuilder().build(is);
return doc.getRootElement().getChild("object").detach();
}
catch(JDOMException | IOException ex){
throw new ConversionException("Error converting object of class " + source.getClass(), ex);
}
}
};
private static <U> Converter<Element, U> getDecoder(Class<U> clazz){
return new Converter<Element, U>(){
final String version = System.getProperty("java.version");
@Override
public U convert(Element source) throws ConversionException {
byte[] bytes;
try(ByteArrayOutputStream os = new ByteArrayOutputStream()){
Document doc = new Document();
doc.setRootElement(new Element("java")
.setAttribute("version", version)
.setAttribute("class", "java.beans.XMLDecoder"));
doc.getRootElement().addContent(source.detach());
XMLOutputter outputter = new XMLOutputter();
outputter.output(doc, os);
bytes = os.toByteArray();
}
catch(IOException ex){
throw new ConversionException("Error reading object of class " + source.getClass(), ex);
}
try(ByteArrayInputStream is = new ByteArrayInputStream(bytes)){
try(XMLDecoder decoder = new XMLDecoder(is)){
return (U)decoder.readObject();
}
}
catch(IOException ex){
throw new ConversionException("Error reading object of class " + source.getClass(), ex);
}
}
};
}
}