/* * RHQ Management Platform * Copyright (C) 2005-2012 Red Hat, Inc. * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation version 2 of the License. * * 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 General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ package org.rhq.enterprise.server.naming.util; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; /** * Given as set of decorators extending given type, this class can pick * the most appropriate set of decorators for a class or a method call. * <p> * To configure the decorator, one has to provide a {@link DecoratorSetContext} that * is then used to obtain the list of * {@link DecoratorSetContext#getSupportedInterfaces() supported interfaces}, which are * all the interfaces that should be used for decorator resolution (i.e. all other interfaces * that a class might implement are ignored during decorator resolution), the list of * {@link DecoratorSetContext#getDecoratorClasses() decorator classes}, which is a list * of decorators the picker can choose from and is also used to instantiate and initialize * the decorators. * * @author Lukas Krejci */ public class DecoratorPicker<Type, Decorator extends Type> { private DecoratorSetContext<Type, Decorator> context; public DecoratorSetContext<Type, Decorator> getContext() { return context; } public void setContext(DecoratorSetContext<Type, Decorator> decoratorSetContext) { this.context = decoratorSetContext; } /** * Returns a set of decorators applicable for given method. The set is established based * on the declaring class of the method. * * @param method the method to inspect * @return the set of decorators that can be used to wrap a method call * @throws Exception */ public Set<Decorator> getDecoratorsForMethod(Method method) throws Exception { return getDecoratorsForClass_Private(method.getDeclaringClass()); } /** * Returns a set of decorators that can be used on instances of given class. * @param cls the class to inspect * @return * @throws Exception */ public Set<Decorator> getDecoratorsForClass(Class<? extends Type> cls) throws Exception { return getDecoratorsForClass_Private(cls); } /** * This method first establishes the set of decorators to use based on the class of the supplied * object and then chains the decorators (in arbitrary order) with the supplied object at the * "root" of the chain. * <p> * If a method is then called on the returned object, the methods of all the decorators are called * in chain (each supposedly calling the next) and finally, at the end of the chain, the method on * the original object (the one supplied to this method) is called. * <p> * Note that the above is only an intended behavior and actually depends on the implementation of * the decorators that are resposinble for the chaining. Each decorator is initialized * (@{link {@link DecoratorSetContext#init(Object, Object)} which should set it up for such chaining. * * @param object * @return * @throws Exception */ public Type decorate(Type object) throws Exception { Set<Decorator> decs = getDecoratorsForClass_Private(object.getClass()); Type ret = object; for(Decorator d : decs) { context.init(d, ret); ret = d; } return ret; } /** * Similar to {@link #decorate(Object)} but instead of the class of the object itself, * uses the significantSuperClass as the basis for the decorator resolution. * <p> * This is important, because if the object implements two mutually incompatible sub-interfaces of <code>Type</code>, * the chained decorators might fail to execute a method later on if the decorator depends on the upper part * of the chain to implement certain sub-interface of <code>Type</code>. * * @param object the object to wrap in decorators * @param significantSuperClass the class to base the decorator resolution on * @return * @throws Exception */ public Type decorate(Type object, Class<?> significantSuperClass) throws Exception { Set<Decorator> decs = getDecoratorsForClass_Private(significantSuperClass); Type ret = object; for(Decorator d : decs) { context.init(d, ret); ret = d; } return ret; } private Set<Decorator> getDecoratorsForClass_Private(Class<?> cls) throws Exception { Set<Class<? extends Type>> ifaces = getNearestApplicableInterfaces(cls); HashSet<Decorator> ret = new HashSet<Decorator>(); for (Class<? extends Type> iface : ifaces) { for (Class<? extends Decorator> decClass : getMatch(iface)) { ret.add(context.instantiate(decClass)); } } return ret; } private Set<Class<? extends Type>> getNearestApplicableInterfaces(Class<?> cls) { List<Class<? extends Type>> ifaces = new ArrayList<Class<? extends Type>>(getAllApplicableInterfaces(cls)); //now compact the set to only contain the most concrete interfaces Iterator<Class<? extends Type>> it = ifaces.iterator(); while (it.hasNext()) { Class<? extends Type> c = it.next(); for (int i = 0; i < ifaces.size(); ++i) { Class<? extends Type> nextC = ifaces.get(i); if (!c.equals(nextC) && c.isAssignableFrom(nextC)) { it.remove(); break; } } } return new HashSet<Class<? extends Type>>(ifaces); } private Set<Class<? extends Type>> getAllApplicableInterfaces(Class<?> cls) { Set<Class<? extends Type>> ifaces = new HashSet<Class<? extends Type>>(); for (Class<? extends Type> iface : context.getSupportedInterfaces()) { if (iface.isAssignableFrom(cls)) { ifaces.add(iface); } } if (ifaces.isEmpty()) { throw new IllegalArgumentException("Class " + cls + " doesn't implement any of the applicable interfaces. Cannot find decorators for it."); } return ifaces; } private Set<Class<? extends Decorator>> getMatch(Class<?> targetIface) { Set<Class<? extends Decorator>> ret = new HashSet<Class<? extends Decorator>>(); for (Class<? extends Decorator> cls : context.getDecoratorClasses()) { if (Arrays.asList(cls.getInterfaces()).contains(targetIface)) { ret.add(cls); } } return ret; } }