Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.disney.groovity.GroovityObjectConverter Maven / Gradle / Ivy
/*******************************************************************************
* © 2018 Disney | ABC Television Group
*
* Licensed under the Apache License, Version 2.0 (the "Apache License")
* with the following modification; you may not use this file except in
* compliance with the Apache License and the following modification to it:
* Section 6. Trademarks. is deleted and replaced with:
*
* 6. Trademarks. This License does not grant permission to use the trade
* names, trademarks, service marks, or product names of the Licensor
* and its affiliates, except as required to comply with Section 4(c) of
* the License and to reproduce the content of the NOTICE file.
*
* You may obtain a copy of the Apache License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the Apache License with the above modification is
* distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the Apache License for the specific
* language governing permissions and limitations under the Apache License.
*******************************************************************************/
package com.disney.groovity;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
import com.disney.groovity.model.Model;
import com.disney.groovity.model.ModelConsumer;
/**
* Static facade to centralize advanced type coercion used by ArgsResolver and the Taggable.resolve() method;
* this includes autoboxing to and from list and array types, and conversion to numeric and boolean types as well.
* Falls back on groovy's DefaultTypeTransformation for basic type conversions.
*
* @author Alex Vigdor
*/
public class GroovityObjectConverter {
@SuppressWarnings("rawtypes")
private static class ArrayIterable implements Iterable{
final private Object arr;
final private int len;
private ArrayIterable(Object arr) {
this.arr=arr;
this.len = Array.getLength(arr);
}
@Override
public Iterator iterator() {
return new Iterator() {
int pointer = 0;
@Override
public boolean hasNext() {
return pointer < len;
}
@Override
public Object next() {
return Array.get(arr, pointer++);
}
};
}
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private static Object convertIterable(Iterable in, Class out, Type[] typeArgs) {
//return coerced array
if(out.isArray()){
Class componentType = out.getComponentType();
ArrayList outObjs = new ArrayList();
for(Object i: in){
Object val = convert(i,componentType);
outObjs.add(val);
}
Object outArray = Array.newInstance(componentType, outObjs.size());
for(int i=0;i outObjs = null;
if(!out.isInterface()) {
try {
outObjs = (List) out.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
}
}
if(outObjs == null) {
outObjs = new ArrayList<>();
}
Type componentType = Object.class;
if(typeArgs!=null && typeArgs.length>0) {
componentType = typeArgs[0];
//System.out.println("CONVERTING ITERABLE TO GENERIC LIST "+componentType);
}
for(Object i: in){
Object val = convert(i,componentType);
outObjs.add(val);
}
return outObjs;
}
//return first non-null value
for(Object i: in){
Object val = convert(i,out);
if(val!=null){
return val;
}
}
return null;
}
@SuppressWarnings({"unchecked", "rawtypes"})
public static Object convert(Object in, Type type){
if(in==null){
return null;
}
Class> out;
Type[] typeArguments = null;
if(type instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) type;
out = (Class>)pt.getRawType();
typeArguments = pt.getActualTypeArguments();
}
else {
out = (Class>) type;
}
if(out.equals(Object.class)){
//special case, no conversion at all
return in;
}
if(type.equals(in.getClass())) {
return in;
}
if(typeArguments==null && out.isAssignableFrom(in.getClass())) {
return in;
}
if(in.getClass().isArray()){
return convertIterable(new ArrayIterable(in), out, typeArguments);
}
if(List.class.isAssignableFrom(in.getClass())){
List inList = (List) in;
return convertIterable(inList, out, typeArguments);
}
if(out.equals(String.class)) {
return in.toString();
}
if(out.isArray()){
Object val = convert(in,out.getComponentType());
if(val!=null){
//if we're expecting an array back but the input is not an array, let's wrap it
Object[] ra = (Object[]) Array.newInstance(out.getComponentType(), 1);
ra[0]=val;
return ra;
}
return Array.newInstance(out.getComponentType(), 0);
}
if(out.isEnum()) {
Enum val = Enum.valueOf((Class) out, in.toString());
return val;
}
if(!Number.class.isAssignableFrom(in.getClass())){
if(Number.class.isAssignableFrom(out)){
String ps = getParseable(in);
if(ps==null){
return null;
}
//groovy can only convert single digit number strings for some odd reason, so we have to roll our own
if(Integer.class.isAssignableFrom(out)){
return Integer.parseInt(ps);
}
if(Long.class.isAssignableFrom(out)){
return Long.parseLong(ps);
}
if(Float.class.isAssignableFrom(out)){
return Float.parseFloat(ps);
}
if(Double.class.isAssignableFrom(out)){
return Double.parseDouble(ps);
}
}
if(out.isPrimitive()){
String ps = getParseable(in);
if(ps==null){
return null;
}
if(out.equals(Integer.TYPE)){
return Integer.parseInt(ps);
}
if(out.equals(Long.TYPE)){
return Long.parseLong(ps);
}
if(out.equals(Float.TYPE)){
return Float.parseFloat(ps);
}
if(out.equals(Double.TYPE)){
return Double.parseDouble(ps);
}
}
}
if(Boolean.class.equals(out) || out == Boolean.TYPE){
Class ic = in.getClass();
if(!Boolean.class.equals(ic) && ic != Boolean.TYPE){
String ps = getParseable(in);
if(ps!=null){
return Boolean.parseBoolean(ps);
}
}
}
if(Map.class.isAssignableFrom(out)) {
Map result = null;
if(!out.isInterface()){
try {
result = (Map) out.newInstance();
} catch (Exception e) {
}
}
if(result==null) {
result = new LinkedHashMap<>();
}
Type kType;
Type vType;
if(typeArguments!=null) {
kType = typeArguments[0];
vType = typeArguments[1];
}
else {
kType = Object.class;
vType = Object.class;
}
Map finalResult = result;
ModelConsumer consumer = (k,v)->{
finalResult.put(convert(k,kType), convert(v, vType));
};
consume(in, consumer);
return finalResult;
}
//TODO special handling for string inputs, look for string-based constructors
if(!out.isPrimitive() && !out.isInterface()) {
try {
Constructor> c = out.getConstructor();
Object o = c.newInstance();
if(o instanceof Model) {
consume(in, ((Model)o)::put);
}
else {
consume(in,(k,v)->{
Model.put(o, k, v);
});
}
//System.out.println("USED MODEL TO CONVERT "+in.getClass().getName()+" to "+out.getName());
return o;
}
catch(Exception e) {
//e.printStackTrace();
}
}
return DefaultTypeTransformation.castToType(in,out);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private static void consume(Object in, ModelConsumer consumer) {
if(in instanceof Map) {
((Map)in).forEach(consumer);
}
else if(in instanceof Model) {
((Model)in).each(consumer);
}
else {
Model.each(in, consumer);
}
}
private static String getParseable(Object in){
if(in==null){
return null;
}
String inStr = in.toString();
if(inStr.length()==0 || "null".equals(inStr)){
return null;
}
return inStr;
}
}