package com.temenos.interaction.core.command; /* * #%L * interaction-core * %% * Copyright (C) 2012 - 2013 Temenos Holdings N.V. * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * #L% */ import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import javax.ws.rs.core.GenericEntity; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl; import com.temenos.interaction.core.entity.Entity; import com.temenos.interaction.core.resource.EntityResource; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class CommandHelper { private static final Logger LOGGER = LoggerFactory.getLogger(CommandHelper.class); /** * Create an Entity entity resource (entry) * @param e Entity * @return entity resource */ public static EntityResource<Entity> createEntityResource(Entity e) { String entityName = e != null ? e.getName() : null; return createEntityResource(entityName, e, Entity.class); } /** * Create a new entity resource. * @param entity entity * @param entityType * @return entity resource */ public static<E> EntityResource<E> createEntityResource(E entity, Class<?> entityType) { return createEntityResource(null, entity, entityType); } /** * Create a new entity resource. * @param entityName entity name * @param entity entity * @param entityType entity type - this should match the type of the template parameter 'E' * @return entity resource */ public static<E> EntityResource<E> createEntityResource(String entityName, final E entity, final Class<?> entityType) { return new EntityResource<E>(entityName, entity) { @Override public GenericEntity<EntityResource<E>> getGenericEntity() { //Override the generic type to be the type of the entity rather than 'E' Type genericType = entityType != null ? getEffectiveGenericType(this.getClass().getGenericSuperclass(), entity, entityType) : getEffectiveGenericType(this.getClass().getGenericSuperclass(), entity); return new GenericEntity<EntityResource<E>>(this, genericType); } }; } /** * Creates a new entity resource from an existing one. * If original entity resource supports cloning it will be cloned. * Otherwise a new entity resource will be created from * the entity, embedded transitions, links and entity tag * of the original entity resource. * * @param entityResource entity resource * @return entity resource */ public static<E> EntityResource<E> createEntityResource(EntityResource<E> entityResource) { if (entityResource == null) { return null; } EntityResource<E> clone; try { clone = entityResource.clone(); } catch (CloneNotSupportedException e) { LOGGER.debug("Cloning is not supported by entity resource, creating a copy...", e); clone = createEntityResource( entityResource.getEntity(), (entityResource.getEntity() != null) ? entityResource.getEntity().getClass() : null); clone.setEmbedded(entityResource.getEmbedded()); clone.setLinks(entityResource.getLinks()); clone.setEntityTag(entityResource.getEntityTag()); } return clone; } /* * Returns the type of the specified entity. * This method will try to evaluate entity type E. If entity type E implements exactly one interface * it will use the interface type, otherwise it will use the class type of entity type E. * @param genericType generic type * @param entity entity * @return type */ public static<E> Type getEffectiveGenericType(final Type superClassType, final E entity) { Class<?> entityType = entity.getClass(); Class<?> entityInterfaces[] = entityType.getInterfaces(); if(entityInterfaces != null && entityInterfaces.length == 1) { entityType = entityInterfaces[0]; } return getEffectiveGenericType(superClassType, entity, entityType); } /* * Returns the type of the specified entity. * @param superClassType parent class of generic type * @param entity entity * @param entityType entity type - this should match the type of the template parameter 'E' * @return type */ @SuppressWarnings("rawtypes") private static<E> Type getEffectiveGenericType(final Type superClassType, final E entity, final Class<?> entityType) { Type newGenericType; if(superClassType instanceof ParameterizedType) { ParameterizedType parametrizedType = (ParameterizedType) superClassType; Type[] types = parametrizedType.getActualTypeArguments(); Type[] newActualTypeArguments = new Type[types.length]; for(int i=0; i < types.length; i++) { Type type = types[i]; if(type instanceof TypeVariable) { final TypeVariable<?> typeVar = (TypeVariable<?>) type; newActualTypeArguments[i] = (TypeVariable) Proxy.newProxyInstance( typeVar.getClass().getClassLoader(), new Class[]{TypeVariable.class}, new TypeVariableHandler(typeVar, entityType)); } } newGenericType = ParameterizedTypeImpl.make((Class<?>) parametrizedType.getRawType(), newActualTypeArguments, parametrizedType.getOwnerType()); } else if (superClassType instanceof TypeVariable) { final TypeVariable<?> typeVar = (TypeVariable<?>) superClassType; Type t = (TypeVariable) Proxy.newProxyInstance( typeVar.getClass().getClassLoader(), new Class[]{TypeVariable.class}, new TypeVariableHandler(typeVar, entityType)); newGenericType = t; } else { newGenericType = superClassType; } return newGenericType; } private static class TypeVariableHandler implements InvocationHandler { private final TypeVariable<?> typeVar; private final Class<?> entityType; public TypeVariableHandler(TypeVariable<?> typeVar, Class<?> entityType) { this.typeVar = typeVar; this.entityType = entityType; } @Override public Object invoke(Object o, Method method, Object[] os) throws Throwable { final String methodName = method.getName(); if ("getBounds".equals(methodName)) { return typeVar.getBounds(); } if ("getGenericDeclaration".equals(methodName)) { return typeVar.getGenericDeclaration(); } if ("getName".equals(methodName)) { return entityType.getSimpleName(); } if ("getAnnotatedBounds".equals(methodName)) { return method.invoke(typeVar, os); } if ("getAnnotation".equals(methodName)) { return method.invoke(typeVar, os); } if ("getAnnotations".equals(methodName)) { return method.invoke(typeVar, os); } if ("getDeclaredAnnotations".equals(methodName)) { return method.invoke(typeVar, os); } return null; } } }