/***
* Copyright (c) 2009 Caelum - www.caelum.com.br/opensource All rights reserved.
*
* 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 br.com.caelum.vraptor.serialization;
import static java.util.Objects.requireNonNull;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.enterprise.context.Dependent;
import javax.inject.Inject;
import br.com.caelum.vraptor.core.ReflectionProvider;
import com.google.common.collect.LinkedListMultimap;
import com.google.common.collect.Multimap;
@Dependent
public class Serializee {
private final ReflectionProvider reflectionProvider;
private Object root;
private Class<?> rootClass;
private Multimap<String, Class<?>> includes;
private Multimap<String, Class<?>> excludes;
private Set<Class<?>> elementTypes;
private boolean recursive;
@Inject
public Serializee(ReflectionProvider reflectionProvider) {
this.reflectionProvider = reflectionProvider;
}
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 Multimap<String, Class<?>> getIncludes() {
if (includes == null) {
includes = LinkedListMultimap.create();
}
return includes;
}
public Multimap<String, Class<?>> getExcludes() {
if (excludes == null) {
excludes = LinkedListMultimap.create();
}
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(String... names) {
for (String name : names) {
getExcludes().putAll(name.replaceAll("\\?", ""), getParentTypesFor(name));
}
}
public void excludeAll() {
Set<Class<?>> types = new HashSet<>();
if (isCollection(getRootClass())) {
types.addAll(getElementTypes());
} else {
types.add(getRootClass());
}
for (Class<?> type : types) {
for (Field field : reflectionProvider.getFieldsFor(type)) {
getExcludes().putAll(field.getName(), getParentTypes(field.getName(), type));
}
}
}
public void includeAll(String... names) {
for (String name : names) {
getIncludes().putAll(name.replaceAll("\\?", ""), getParentTypesFor(name));
}
}
private Set<Class<?>> getParentTypesFor(String name) {
if (getElementTypes() == null) {
Class<?> type = getRootClass();
return getParentTypes(name, type);
} else {
Set<Class<?>> result = new HashSet<>();
for (Class<?> type : getElementTypes()) {
result.addAll(getParentTypes(name, type));
}
return result;
}
}
private Set<Class<?>> getParentTypes(String name, Class<?> type) {
String[] path = name.split("\\.");
try {
for (int i = 0; i < path.length; i++) {
Field field = reflectField(path[i], type);
if (field == null) break;
if (i < path.length - 1) type = getActualType(field);
}
} catch (NullPointerException e) {
throw new IllegalArgumentException("Field path '" + name + "' doesn't exists in " + type, e);
}
Set<Class<?>> types = new HashSet<>();
while (type != Object.class) {
types.add(type);
type = type.getSuperclass();
}
return types;
}
private Field reflectField(String path, Class<?> type) {
Field field = reflectionProvider.getField(type, path.replaceAll("\\?", ""));
if (!path.startsWith("?"))
requireNonNull(field);
return field;
}
protected Class<?> getActualType(Field field) {
Type genericType = field.getGenericType();
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;
}
private 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);
}
}