/** * Copyright 2012 Universitat Pompeu Fabra. * * 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.onexus.resource.serializer.xstream.internal; import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.converters.ConversionException; import com.thoughtworks.xstream.mapper.MapperWrapper; import org.onexus.resource.api.Folder; import org.onexus.resource.api.IResourceSerializer; import org.onexus.resource.api.Loader; import org.onexus.resource.api.Parameter; import org.onexus.resource.api.Plugin; import org.onexus.resource.api.Project; import org.onexus.resource.api.Property; import org.onexus.resource.api.Resource; import org.onexus.resource.api.annotations.ResourceAlias; import org.onexus.resource.api.annotations.ResourceImplicitList; import org.onexus.resource.api.annotations.ResourceRegister; import org.onexus.resource.api.exceptions.UnserializeException; import org.onexus.resource.api.utils.AbstractMetadata; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.InputStream; import java.io.OutputStream; import java.lang.reflect.Field; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.HashSet; import java.util.Set; public class ResourceSerializer implements IResourceSerializer { private static final Logger LOGGER = LoggerFactory.getLogger(ResourceSerializer.class); private Set<ClassLoader> registeredLoaders = new HashSet<ClassLoader>(); private XStream xstream; public ResourceSerializer() { super(); this.xstream = new XStream() { @Override protected MapperWrapper wrapMapper(MapperWrapper next) { return new MapperWrapper(next) { @Override public boolean shouldSerializeMember(Class definedIn, String fieldName) { if (definedIn == Object.class) { LOGGER.warn("Tag '" + fieldName + "' not defined."); return false; } return super.shouldSerializeMember(definedIn, fieldName); } }; } }; this.xstream.setClassLoader(new RegisteredClassLoader()); // Resource xstream.addImplicitCollection(Resource.class, "properties", "property", Property.class); xstream.addImplicitCollection(AbstractMetadata.class, "properties", "property", Property.class); // Project alias("project", Project.class); xstream.addImplicitCollection(Plugin.class, "parameters", "parameter", Parameter.class); xstream.omitField(Project.class, "name"); // Plugin alias("plugin", Plugin.class); // Folder alias("folder", Folder.class); // Loader alias("parameter", Parameter.class); xstream.addImplicitCollection(Loader.class, "parameters", "parameter", Parameter.class); alias("property", Property.class); xstream.registerConverter(new ClassConverter()); xstream.registerConverter(new ORIConverter()); } @Override public String getMediaType() { return "text/xml"; } @SuppressWarnings("unchecked") @Override public <T> T unserialize(Class<T> resourceType, InputStream input) throws UnserializeException { try { return (T) xstream.fromXML(input); } catch (ConversionException e) { String path = e.get("path"); String line = e.get("line number"); throw new UnserializeException(path, line, e); } } @Override public void serialize(Object resource, OutputStream output) { xstream.toXML(resource, output); } private void alias(String alias, Class<?> resourceType) { xstream.alias(alias, resourceType); register(resourceType); } @Override public void register(Class<?> resourceType) { processAnnotations(resourceType); registeredLoaders.add(resourceType.getClassLoader()); // Register dependent types ResourceRegister resourceRegister = resourceType.getAnnotation(ResourceRegister.class); if (resourceRegister != null) { for (Class type : resourceRegister.value()) { processAnnotations(type); } } } private void processAnnotations(Class<?> resourceType) { ResourceAlias resourceAlias = resourceType.getAnnotation(ResourceAlias.class); if (resourceAlias != null) { xstream.alias(resourceAlias.value(), resourceType); } for (Field field : resourceType.getDeclaredFields()) { ResourceAlias resourceFieldAlias = field.getAnnotation(ResourceAlias.class); if (resourceFieldAlias != null) { xstream.aliasField(resourceFieldAlias.value(), resourceType, field.getName()); } ResourceImplicitList implicitList = field.getAnnotation(ResourceImplicitList.class); if (implicitList != null) { xstream.addImplicitCollection(resourceType, field.getName(), implicitList.value(), getCollectionItemClass(field)); } } } private Class<?> getCollectionItemClass(Field field) { Class<?> type = null; final Type genericType = field.getGenericType(); if (genericType instanceof ParameterizedType) { final Type typeArgument = ((ParameterizedType)genericType).getActualTypeArguments()[0]; if (typeArgument instanceof ParameterizedType) { type = (Class<?>)((ParameterizedType)typeArgument).getRawType(); } else if (typeArgument instanceof Class) { type = (Class<?>)typeArgument; } } return type; } private class RegisteredClassLoader extends ClassLoader { @SuppressWarnings({"rawtypes", "unchecked"}) public Class loadClass(String name) throws ClassNotFoundException { for (ClassLoader loader : registeredLoaders) { try { return loader.loadClass(name); } catch (ClassNotFoundException e) { // Continue } } throw new ClassNotFoundException(); } } }