/*
* This class is adapted from code available at https://github.com/codedance/silken
* which has been published with the below license information, and which contains
* the following modifications:
*
* 1. Added enum support
*
* --------------------- Start original license information ---------------------
*
* (c) Copyright 2011-2013 PaperCut Software Int. Pty. Ltd. http://www.papercut.com/
*
* 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 com.jivesoftware.os.amza.ui.soy;
import com.google.common.collect.Lists;
import com.google.common.primitives.Primitives;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
*
*/
public class SoyDataUtils {
/**
* Convert all data stored in a POJO or Map<String, Object> into a format compatible with Soy's DataMap.
* This method will convert nested POJOs to a corresponding nested Maps.
*
* @param obj The Map or POJO who's data should be converted.
* @return A Map of data compatible with Soy.
*/
@SuppressWarnings("unchecked")
public Map<String, ?> toSoyCompatibleMap(Object obj) {
Object ret = toSoyCompatibleObjects(obj);
if (!(ret instanceof Map)) {
throw new IllegalArgumentException("Input should be a Map or POJO.");
}
return (Map<String, ?>) ret;
}
/**
* Convert an object (or graph of objects) to types compatible with Soy (data able to be stored in SoyDataMap).
* This will convert:
* - POJOs to Maps
* - Iterables to Lists
* - all strings and primitives remain as is.
*
* @param obj The object to convert.
* @return The object converted (in applicable).
*/
private Object toSoyCompatibleObjects(Object obj) {
if (obj == null) {
return obj;
}
if (Primitives.isWrapperType(obj.getClass())
|| obj.getClass().isPrimitive()
|| obj instanceof String) {
return obj;
}
if (obj instanceof Map) {
@SuppressWarnings("unchecked")
Map<String, Object> map = (Map<String, Object>) obj;
Map<String, Object> newMap = new HashMap<>(map.size());
for (String key : map.keySet()) {
newMap.put(key, toSoyCompatibleObjects(map.get(key)));
}
return newMap;
}
if (obj instanceof Iterable<?>) {
List<Object> list = Lists.newArrayList();
for (Object subValue : ((Iterable<?>) obj)) {
list.add(toSoyCompatibleObjects(subValue));
}
return list;
}
if (obj.getClass().isArray()) {
return obj;
}
if (obj.getClass().isEnum()) {
return ((Enum) obj).name();
}
// At this point we must assume it's a POJO so map-it.
{
@SuppressWarnings("unchecked")
Map<String, Object> pojoMap = (Map<String, Object>) pojoToMap(obj);
Map<String, Object> newMap = new HashMap<>(pojoMap.size());
for (String key : pojoMap.keySet()) {
newMap.put(key, toSoyCompatibleObjects(pojoMap.get(key)));
}
return newMap;
}
}
/**
* Convert a Java POJO (aka Bean) to a Map<String, Object>.
*
* @param pojo The Java pojo object with standard getters and setters.
* @return Pojo data as a Map.
*/
private Map<String, ?> pojoToMap(Object pojo) {
Map<String, Object> map = new HashMap<>();
BeanInfo beanInfo;
try {
beanInfo = Introspector.getBeanInfo(pojo.getClass());
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor pd : propertyDescriptors) {
if (pd.getReadMethod() != null && !"class".equals(pd.getName())) {
map.put(pd.getName(), pd.getReadMethod().invoke(pojo));
}
}
} catch (IntrospectionException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
throw new RuntimeException(e);
}
return map;
}
}