Skip to content

Commit

Permalink
Fixing bug of includes from subclasses, including major refactor of X…
Browse files Browse the repository at this point in the history
…StreamSerializer. Closes gh-400
  • Loading branch information
lucascs committed Feb 23, 2012
1 parent 7b5444f commit 6d7c144
Show file tree
Hide file tree
Showing 7 changed files with 339 additions and 136 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package br.com.caelum.vraptor.serialization.xstream;

import br.com.caelum.vraptor.serialization.ProxyInitializer;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;

public final class ProxyConverter implements Converter {
private final ProxyInitializer initializer;
private final XStream xstream;

public ProxyConverter(ProxyInitializer initializer, XStream xstream) {
this.initializer = initializer;
this.xstream = xstream;
}
public boolean canConvert(Class clazz) {
return initializer.isProxy(clazz);
}

public Object unmarshal(HierarchicalStreamReader reader,
UnmarshallingContext context) {
throw new AssertionError();
}

public void marshal(Object value, HierarchicalStreamWriter writer,
MarshallingContext context) {
Converter converter = xstream.getConverterLookup().lookupConverterForType(initializer.getActualClass(value));
initializer.initialize(value);
converter.marshal(value, writer, context);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package br.com.caelum.vraptor.serialization.xstream;

import java.util.List;
import java.util.Set;

import com.google.common.collect.Lists;

public class Serializee {
private Object root;
private Class<?> rootClass;
private List<String> includes = Lists.newArrayList();
private List<String> excludes = Lists.newArrayList();
private Set<Class<?>> elementTypes;
private boolean recursive;

public Object getRoot() {
return root;
}
public void setRoot(Object root) {
this.root = root;
}
public Class<?> getRootClass() {
return rootClass;
}
public void setRootClass(Class<?> rootClass) {
this.rootClass = rootClass;
}
public List<String> getIncludes() {
return includes;
}
public List<String> getExcludes() {
return excludes;
}
public Set<Class<?>> getElementTypes() {
return elementTypes;
}
public void setElementTypes(Set<Class<?>> elementTypes) {
this.elementTypes = elementTypes;
}
public boolean isRecursive() {
return recursive;
}
public void setRecursive(boolean recursive) {
this.recursive = recursive;
}
public void excludeAll(List<String> names) {
checkPresenceOf(names);
excludes.addAll(names);
}
public void includeAll(List<String> names) {
checkPresenceOf(names);
includes.addAll(names);
}

private void checkPresenceOf(List<String> names) {

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,68 @@

package br.com.caelum.vraptor.serialization.xstream;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import net.vidageek.mirror.dsl.Mirror;
import br.com.caelum.vraptor.interceptor.TypeNameExtractor;
import br.com.caelum.vraptor.validator.Message;

import com.google.common.base.Supplier;
import com.google.common.collect.Sets;
import com.thoughtworks.xstream.mapper.Mapper;
import com.thoughtworks.xstream.mapper.MapperWrapper;

public class VRaptorClassMapper extends MapperWrapper {

private final Supplier<TypeNameExtractor> extractor;

private Serializee serializee;

public VRaptorClassMapper(Mapper wrapped, Supplier<TypeNameExtractor> supplier) {
super(wrapped);
this.extractor = supplier;
}

static boolean isPrimitive(Class<?> type) {
return type.isPrimitive()
|| type.isEnum()
|| Number.class.isAssignableFrom(type)
|| type.equals(String.class)
|| Date.class.isAssignableFrom(type)
|| Calendar.class.isAssignableFrom(type)
|| Boolean.class.equals(type)
|| Character.class.equals(type);
}

@Override
public boolean shouldSerializeMember(Class definedIn, String fieldName) {
for (String include : serializee.getIncludes()) {
if (isCompatiblePath(include, definedIn, fieldName)) {
return true;
}
}
for (String exclude : serializee.getExcludes()) {
if (isCompatiblePath(exclude, definedIn, fieldName)) {
return false;
}
}

boolean should = super.shouldSerializeMember(definedIn, fieldName);
if (!serializee.isRecursive())
should = should && isPrimitive(new Mirror().on(definedIn).reflect().field(fieldName).getType());
return should;
}

private boolean isCompatiblePath(String path, Class definedIn, String fieldName) {
return (path.equals(fieldName) || path.endsWith("." + fieldName)) && getParentTypesFor(serializee, path).contains(definedIn);
}

@Override
public String serializedClass(Class type) {
Expand All @@ -44,5 +91,64 @@ public String serializedClass(Class type) {
}
return superName;
}

static Class<?> getActualType(Type genericType) {
if (genericType instanceof ParameterizedType) {
ParameterizedType type = (ParameterizedType) genericType;

if (isCollection(type)) {
Type actualType = type.getActualTypeArguments()[0];

if (actualType instanceof TypeVariable<?>) {
return (Class<?>) type.getRawType();
}

return (Class<?>) actualType;
}
}

return (Class<?>) genericType;
}

static boolean isCollection(Type type) {
if (type instanceof ParameterizedType) {
ParameterizedType ptype = (ParameterizedType) type;
return Collection.class.isAssignableFrom((Class<?>) ptype.getRawType())
|| Map.class.isAssignableFrom((Class<?>) ptype.getRawType());
}
return Collection.class.isAssignableFrom((Class<?>) type);
}


static Set<Class<?>> getParentTypesFor(Serializee serializee, String name) {
if (serializee.getElementTypes() == null) {
Class<?> type = serializee.getRootClass();
return getParentTypes(name, type);
} else {
Set<Class<?>> result = new HashSet<Class<?>>();
for (Class<?> type : serializee.getElementTypes()) {
result.addAll(getParentTypes(name, type));
}
return result;
}
}

static Set<Class<?>> getParentTypes(String name, Class<?> type) {
String[] path = name.split("\\.");
for (int i = 0; i < path.length - 1; i++) {
type = getActualType(new Mirror().on(type).reflect().field(path[i]).getGenericType());
}
Set<Class<?>> types = Sets.newHashSet();
while(type != Object.class) {
types.add(type);
type = type.getSuperclass();
}
return types;
}

public void setSerializee(Serializee serializee) {
this.serializee = serializee;
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

public final class VRaptorXStream extends XStream {
private final TypeNameExtractor extractor;
private VRaptorClassMapper vraptorMapper;

{setMode(NO_REFERENCES);}

Expand All @@ -23,10 +24,18 @@ public VRaptorXStream(TypeNameExtractor extractor, HierarchicalStreamDriver hier

@Override
protected MapperWrapper wrapMapper(MapperWrapper next) {
return new VRaptorClassMapper(next, new Supplier<TypeNameExtractor>() {

vraptorMapper = new VRaptorClassMapper(next,
/* this method is called in the super constructor, so we cannot use instance variables, so we're
* using this 'lazy' get */
new Supplier<TypeNameExtractor>() {
public TypeNameExtractor get() {
return extractor;
}
});
return vraptorMapper;
}
public VRaptorClassMapper getVRaptorMapper() {
return vraptorMapper;
}
}
Loading

0 comments on commit 6d7c144

Please sign in to comment.