/* * Copyright 2019 Arcus Project * * 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.iris.type; import java.lang.reflect.Type; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.lang3.reflect.TypeUtils; import com.google.common.base.Function; import com.google.common.base.Predicate; import com.iris.type.functional.CollectionTransformer; import com.iris.type.functional.EnumTransformer; import com.iris.type.functional.SupportedCollectionPredicate; import com.iris.type.functional.SupportedEnumPredicate; import com.iris.type.functional.SupportedTypePredicate; import com.iris.type.functional.TypeHandlerTransformer; import com.iris.type.functional.TypePredicate; import com.iris.type.functional.UnsupportedTransformer; import com.iris.type.functional.UnsupportedTypePredicate; import com.iris.type.handler.CollectionHandler; import com.iris.type.handler.EnumHandler; import com.iris.type.handler.ListFactory; import com.iris.type.handler.SetFactory; import com.iris.util.IrisCollections; @SuppressWarnings("serial") public class TypeCoercerImpl implements TypeCoercer { private final static Map<Class<?>, TypeHandler<?>> staticHandlers = IrisCollections.toUnmodifiableMap(standardHandlers, new Function<TypeHandler<?>, Class<?>>() { @Override public Class<?> apply(TypeHandler<?> input) { return input.getTargetType(); } }); private final Map<Class<?>, TypeHandler<?>> handlers = new HashMap<>(); private final EnumHandler enumHandler = new EnumHandler(); private final CollectionHandler collectionHandler; public TypeCoercerImpl(TypeHandler<?>... handlers) { registerHandlers(handlers); collectionHandler = new CollectionHandler(this); } @Override public boolean isSupportedType(Class<?> clazz, Type type) { if (type == null) { return false; } if (TypeUtils.isAssignable(type, clazz)) { return true; } TypeHandler<?> handler = getHandler(clazz); if (handler != null) { return handler.isSupportedType(type); } if (clazz.isEnum()) { return enumHandler.isSupportedType(clazz, type); } return false; } @Override public boolean isSupportedCollectionType(Class<?> containedClazz, Type type) { return type != null ? collectionHandler.isSupportedType(containedClazz, type) : false; } @Override public boolean isCoercible(Class<?> clazz, Object obj) { if (obj == null) { return true; } if (clazz.isInstance(obj)) { return true; } TypeHandler<?> handler = getHandler(clazz); if (handler != null) { return handler.isCoercible(obj); } if (clazz.isEnum()) { return enumHandler.isCoercible(clazz, obj); } return false; } @Override public boolean isCoercibleCollection(Class<?> containedClazz, Object obj) { if (obj == null) { return true; } return collectionHandler.isCoercible(containedClazz, obj); } @SuppressWarnings("unchecked") @Override public <T> T coerce(Class<T> clazz, Object obj) { if (obj == null) { return null; } if (clazz.isInstance(obj)) { // If it is the same class or a subclass, just cast it. return (T)obj; } TypeHandler<T> handler = getHandler(clazz); if (handler != null) { return handler.coerce(obj); } if (clazz.isEnum()) { return enumHandler.coerce(clazz, obj); } throw new IllegalArgumentException("Object of class " + obj.getClass().getName() + " cannot be coerced to " + clazz.getName()); } @Override public <T> List<T> coerceList(Class<T> clazz, Object obj) { return collectionHandler.coerce(clazz, obj, new ListFactory<T>()); } @Override public <T> Set<T> coerceSet(Class<T> clazz, Object obj) { return collectionHandler.coerce(clazz, obj, new SetFactory<T>()); } @Override public <T> T attemptCoerce(Class<T> clazz, Object obj) { try { return coerce(clazz, obj); } catch (Exception ex) { return null; } } @Override public <T> List<T> attemptCoerceList(Class<T> clazz, Object obj) { try { return coerceList(clazz, obj); } catch (Exception ex) { return null; } } @Override public <T> Set<T> attemptCoerceSet(Class<T> clazz, Object obj) { try { return coerceSet(clazz, obj); } catch (Exception ex) { return null; } } @Override public <T> Function<Object, T> createTransformer(Class<T> clazz) { return createTransformer(clazz, "to" + clazz.getSimpleName()); } @Override public <T> Function<Object, T> createTransformer(Class<T> clazz, String description) { TypeHandler<T> handler = getHandler(clazz); if (handler != null) { return new TypeHandlerTransformer<T>(handler, clazz, description); } if (clazz.isEnum()) { return new EnumTransformer<T>(enumHandler, clazz, description); } return new UnsupportedTransformer<T>(clazz, description); } @Override public <T> Function<Object, List<T>> createListTransformer(Class<T> containedClazz) { return createListTransformer(containedClazz, "toListOf" + containedClazz.getSimpleName()); } @Override public <T> Function<Object, List<T>> createListTransformer(Class<T> containedClazz, String description) { return new CollectionTransformer<T, List<T>>(collectionHandler, containedClazz, new ListFactory<T>(), description); } @Override public <T> Function<Object, Set<T>> createSetTransformer(Class<T> containedClazz) { return createSetTransformer(containedClazz, "toSetOf" + containedClazz.getSimpleName()); } @Override public <T> Function<Object, Set<T>> createSetTransformer(Class<T> containedClazz, String description) { return new CollectionTransformer<T, Set<T>>(collectionHandler, containedClazz, new SetFactory<T>(), description); } @Override public <T> Predicate<Object> createPredicate(Class<T> clazz, Predicate<T> predicate) { return new TypePredicate<T>(createTransformer(clazz), predicate, false); } @Override public <T> Predicate<Object> createPredicate(Class<T> clazz, Predicate<T> predicate, boolean returnFalseOnException) { return new TypePredicate<T>(createTransformer(clazz), predicate, returnFalseOnException); } @Override public <T> Predicate<Object> createListPredicate(Class<T> clazz, Predicate<List<T>> predicate) { return new TypePredicate<List<T>>(createListTransformer(clazz), predicate, false); } @Override public <T> Predicate<Object> createListPredicate(Class<T> clazz, Predicate<List<T>> predicate, boolean returnFalseOnException) { return new TypePredicate<List<T>>(createListTransformer(clazz), predicate, returnFalseOnException); } @Override public <T> Predicate<Object> createSetPredicate(Class<T> clazz, Predicate<Set<T>> predicate) { return new TypePredicate<Set<T>>(createSetTransformer(clazz), predicate, false); } @Override public <T> Predicate<Object> createSetPredicate(Class<T> clazz, Predicate<Set<T>> predicate, boolean returnFalseOnException) { return new TypePredicate<Set<T>>(createSetTransformer(clazz), predicate, returnFalseOnException); } @Override public <T> Predicate<Type> createSupportedTypePredicate(Class<T> clazz) { TypeHandler<T> handler = getHandler(clazz); if (handler != null) { return new SupportedTypePredicate(handler); } if (clazz.isEnum()) { return new SupportedEnumPredicate(enumHandler, clazz); } return new UnsupportedTypePredicate(clazz); } @Override public <T> Predicate<Type> createSupportedCollectionPredicate(Class<T> containedClazz) { return new SupportedCollectionPredicate(collectionHandler, containedClazz); } @Override public void registerHandlers(TypeHandler<?>... newHandlers) { if (newHandlers != null && newHandlers.length > 0) { for (TypeHandler<?> handler : newHandlers) { handlers.put(handler.getTargetType(), handler); } } } @SuppressWarnings("unchecked") private <T> TypeHandler<T> getHandler(Class<T> clazz) { // Use the custom handlers first so they can override the static handlers TypeHandler<?> handler = handlers.get(clazz); return (TypeHandler<T>)(handler != null ? handler : staticHandlers.get(clazz)); } }