/* * Copyright 2011 JBoss Inc * * 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 org.drools.semantics.traits.java; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; public class TraitMantle extends HashMap<String,Object> implements InvocationHandler { private Set<Class<?>> types = new LinkedHashSet<Class<?>>(); private Object proxy; private Object core; private static HashMap<Class, List<Object>> delegateCache = new HashMap<Class,List<Object>>();; private LinkedList<Object> delegates; public static <K> IThing wrap(K obj) { return wrap(obj, new HashMap<String,Object>(), IThing.class); } public static <K> IThing wrap(K obj, Map<String,Object> map) { return wrap(obj, map, IThing.class); } public static <T> T wrap(Object obj, Map<String,Object> map, Class<T> trait) { TraitMantle mantle = new TraitMantle(map, obj); Class[] interfaces = null; if (obj.getClass().getInterfaces().length > 0) { interfaces = obj.getClass().getInterfaces(); interfaces = Arrays.copyOf(interfaces,interfaces.length + 1); interfaces[interfaces.length-1] = trait; for (Class itf : obj.getClass().getInterfaces()) { mantle.types.add(itf); } } else { interfaces = new Class[] {trait}; } T proxy = (T) Proxy.newProxyInstance(TraitMantle.class.getClassLoader(), interfaces, mantle ); mantle.types.add(IThing.class); if ( trait != IThing.class ) { bindImpl( mantle, trait ); } mantle.proxy = proxy; return proxy; } private static <T> T don(IThing thing, Class<T> intface) { TraitMantle mantle = (TraitMantle) thing.getMantle(); if (mantle.types.contains(intface)) { rebindImpl(mantle, intface); return (T) mantle.proxy; } Class[] interfaces = (Class[]) mantle.types.toArray(new Class[mantle.types.size()]); interfaces = Arrays.copyOf(interfaces,interfaces.length + 1); interfaces[interfaces.length-1] = intface; T proxy = (T) Proxy.newProxyInstance(TraitMantle.class.getClassLoader(), interfaces, mantle ); mantle.types.add(intface); bindImpl( mantle, intface ); mantle.proxy = proxy; return proxy; } private static <T> T cast(IThing thing, Class<T> intface) { TraitMantle mantle = (TraitMantle) thing.getMantle(); if (! mantle.types.contains(intface)) { return null; } Class[] interfaces = new Class[] {intface}; T proxy = (T) Proxy.newProxyInstance(TraitMantle.class.getClassLoader(), interfaces, mantle ); return proxy; } private static <T> IThing shed(IThing thing, Class<T> intface) { if (IThing.class.equals(intface)) { return thing; } TraitMantle mantle = (TraitMantle) thing.getMantle(); if (! mantle.types.contains(intface)) { return thing; } mantle.types.remove(intface); unbindImpl( mantle, intface ); Class[] interfaces = (Class[]) mantle.types.toArray(new Class[mantle.types.size()]); Object proxy = Proxy.newProxyInstance(TraitMantle.class.getClassLoader(), interfaces, mantle ); mantle.proxy = proxy; return (IThing) proxy; } private static <T> void unbindImpl(TraitMantle mantle, Class<T> intface) { System.out.println("Removing " + intface); List<Object> implementors = delegateCache.get(intface); System.err.println("Removing " + implementors + " frm 0" + delegateCache); mantle.delegates.removeAll(implementors); } private static void bindImpl( TraitMantle mantle, Class<?> intface ) { List<Object> implementors; if (delegateCache.containsKey(intface)) { implementors = delegateCache.get(intface); } else { implementors = findAllImplementors(intface); delegateCache.put(intface,implementors); } System.err.println("Bound " + intface + " in " + delegateCache); mantle.delegates.removeAll(implementors); mantle.delegates.addAll(implementors); } private static <T> void rebindImpl(TraitMantle mantle, Class<T> intface) { List<Object> implementors = delegateCache.get(intface); mantle.delegates.removeAll(implementors); mantle.delegates.addAll(implementors); System.err.println("reBound " + intface + " in " + delegateCache); } private static List<Object> findAllImplementors(Class<?> intface) { List<Object> list = new ArrayList<Object>(5); recur(list, intface); System.out.println("Found all implementors for " + intface + " > > " + list); return list; } private static void recur(List<Object> list, Class<?> intface) { for (Class intf : intface.getInterfaces()) { recur(list,intf); } Trait aTrait = intface.getAnnotation(Trait.class); if (aTrait != null) { try { Class impl = aTrait.impl(); if (impl != null) { Method factory = impl.getMethod("newInstance"); Object del = factory.invoke(null); list.add(del); } } catch (Exception ignored) { } } } protected TraitMantle(Map<String, Object> properties, Object obj) { this.delegates = new LinkedList<Object>(); this.types = new HashSet<Class<?>>(); this.putAll(properties); this.core = obj; } public Object invoke(Object proxy, Method method, Object[] args) throws NoSuchMethodException { String name = method.getName(); if (args == null) { if ("getTypes".equals(name)) { return types; } else if ("getCore".equals(name)) { return core; } else if ("getMantle".equals(name)) { return this; } else if (name.startsWith("get")) { String prop = name.substring(3,4).toLowerCase() + name.substring(4); if (containsKey(prop)) { return get(prop); } else { try { return checkCoreThenDelegate(name); } catch ( NoSuchMethodException nsme ) { if ( method.getReturnType().isPrimitive() ) { if (int.class.isAssignableFrom(method.getReturnType())) { return 0; } else if (double.class.isAssignableFrom(method.getReturnType())) { return 0.0; } else { throw new RuntimeException("Missing prim. type " + method.getReturnType()); } } else { return null; } } } } else if ("hashCode".equals(name)) { return core.hashCode(); } else if ("size".equals(name)) { return size(); } else if ("entrySet".equals(name)) { return entrySet(); } else { return checkCoreThenDelegate(name); } } int len = args.length; if (len == 1) { Object arg = args[0]; if ("get".equals(name)) { if (containsKey(arg)) { return get(arg); } else { if (arg instanceof String) { try { String propName = (String) arg; return checkCoreThenDelegate("get"+ propName.substring(0,1).toUpperCase() + propName.substring(1)); } catch (NoSuchMethodException nsme) { return null; // undefined } } else { return checkCoreThenDelegate(name); } } } else if (name.startsWith("set")) { String propName = name.substring(3).toLowerCase(); if (containsKey(propName)) { return put(propName,arg); } else { try { System.out.println("x" + name); return checkCoreThenDelegate(name, method.getParameterTypes(), args); } catch (NoSuchMethodException nsme) { System.out.println("y" + propName); put(propName,arg); } } } else if ("is".equals(name)) { if (containsKey(arg) && get(arg) instanceof Boolean ) { return get(arg); } else { if (arg instanceof String) { String propName = (String) arg; return checkCoreThenDelegate("is"+ propName.substring(0,1).toUpperCase() + propName.substring(1)); } else { return checkDelegateThenCore(name, method.getParameterTypes(), args); } } } else if ("equals".equals(name)) { return core.equals(arg); } else if ("hasType".equals(name)) { return types.contains(arg); } else if ("cast".equals(name)) { return TraitMantle.cast((IThing) proxy, (Class<?>) arg); } else if ("don".equals(name)) { return TraitMantle.don((IThing) proxy, (Class<?>) arg); } else if ("shed".equals(name)) { return shed((IThing) proxy, (Class<?>) arg); } else if ("remove".equals(name)) { return remove(arg); } else if ("containsKey".equals(name)) { return containsKey(arg); } else { return checkDelegateThenCore(name, method.getParameterTypes(), args); } } else if (len == 2) { if ("set".equals(name)) { if (args[0] instanceof String) { String prop = (String) args[0]; if (containsKey(prop)) { return put(prop,args[1]); } else { try { prop = "set" + prop.substring(0,1).toUpperCase() + prop.substring(1); return checkCoreThenDelegate(prop, new Class[] {args[1].getClass()}, new Object[] {args[1]}); } catch ( NoSuchMethodException nsme ) { return checkDelegateThenCore(name, method.getParameterTypes(), args); } } } else { return checkDelegateThenCore(name, method.getParameterTypes(), args); } } else if ("put".equals(name)) { if (args[0] instanceof String) { return put((String) args[0],args[1]); } else { return checkDelegateThenCore(name, method.getParameterTypes(), args); } } else { return checkDelegateThenCore(name, method.getParameterTypes(), args); } } else { return checkDelegateThenCore(name, method.getParameterTypes(), args); } return null; } private Object checkCoreThenDelegate(String name) throws NoSuchMethodException { try { Method method = core.getClass().getMethod(name); return method.invoke(core); } catch (Exception e) { Iterator<Object> iter = delegates.descendingIterator(); while (iter.hasNext()) { Object delegate = iter.next(); try { Method method = delegate.getClass().getMethod(name); return method.invoke(delegate); } catch (Exception e1) { System.err.println("method " + name + "() not found in " + delegate.getClass() +" , moving to next one " ); } } throw new NoSuchMethodException(name+"()"); } } private Object checkCoreThenDelegate(String name, Class[] argClasses, Object[] args) throws NoSuchMethodException { try { Method method = core.getClass().getMethod(name,argClasses); return method.invoke(core,args); } catch (Exception e) { System.out.println("Method not dounf in" + core.getClass().getName() + " : " + name + " << " + format(argClasses)); Iterator<Object> iter = delegates.descendingIterator(); while (iter.hasNext()) { Object delegate = iter.next(); try { Method method = delegate.getClass().getMethod(name, argClasses); return method.invoke(delegate, args); } catch (Exception e1) { System.err.println("method " + name + "(" + format(argClasses) +") not found in " + delegate.getClass() +" , moving to next one " ); // throw new NoSuchMethodException(name+"()"); } } throw new NoSuchMethodException(name+ "(" + format(argClasses) +")"); } } private Object checkDelegateThenCore(String name, Class[] argClasses, Object[] args) throws NoSuchMethodException { Iterator<Object> iter = delegates.descendingIterator(); while (iter.hasNext()) { Object delegate = iter.next(); try { Method method = delegate.getClass().getMethod(name, argClasses); return method.invoke(delegate, args); } catch (Exception e1) { System.err.println("method " + name + "(" + format(argClasses) +") not found in " + delegate.getClass() +" , moving to next one " ); // throw new NoSuchMethodException(name+"()"); } } try { Method method = core.getClass().getMethod(name,argClasses); return method.invoke(core,args); } catch (Exception e3) { throw new NoSuchMethodException(name + "(" + format(argClasses) +")"); } } private static String format(Class[] argz) { StringBuilder sb = new StringBuilder(); sb.append("["); for (int j = 0; j < argz.length -1; j++) { sb.append(argz[j].getName()).append(","); } if (argz.length > 0) { sb.append(argz[argz.length-1]).append("]"); } else { sb.append("]"); } return sb.toString(); } }