Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 795e5a74510241c6041101c35d2f762eda90013a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
/*
 * Copyright (C) 2005, 2006 db4objects Inc.  http://www.db4o.com
 * 
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     db4objects - Initial API and implementation
 */
package org.eclipse.jface.examples.databinding.ducks;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * DuckType. Implements Duck Typing for Java.  ("If it walks like a duck, 
 * quacks like a duck, it...").  Essentially allows programs to treat
 * objects from separate hierarchies as if they were designed with common
 * interfaces as long as they adhere to common naming conventions.
 * <p>
 * This version is the strict DuckType.  All methods present in
 * interfaceToImplement must be present on the target object.
 *
 * @author djo
 */
public class DuckType implements InvocationHandler {
   
   /**
    * Interface DuckType#Wrapper.  An interface for DuckType proxies that
    * allows clients to access the proxied value.  The value returned by
    * calling DuckType#implement always implements this interface.
    */
   public static interface Wrapper {
      /**
       * Method duckType_GetWrappedValue.  Returns the proxied value.
       * 
       * @return The proxied value.
       */
      public Object duckType_GetWrappedValue();
   }
   
	/**
     * Causes object to implement the interfaceToImplement and returns
     * an instance of the object implementing interfaceToImplement even
     * if interfaceToImplement was not declared in object.getClass()'s 
     * implements declaration.<p>
     * 
     * This works as long as all methods declared in interfaceToImplement
     * are present on object.
     * 
	 * @param interfaceToImplement The Java class of the interface to implement
	 * @param object The object to force to implement interfaceToImplement
	 * @return object, but now implementing interfaceToImplement
	 */
	public static Object implement(Class interfaceToImplement, Object object) {
		return Proxy.newProxyInstance(interfaceToImplement.getClassLoader(), 
				new Class[] {interfaceToImplement, Wrapper.class}, new DuckType(object));
	}
    
    /**
     * Indicates if object is a (DuckType) instace of intrface.  That is,
     * is every method in intrface present on object.
     * 
     * @param intrface The interface to implement
     * @param object The object to test
     * @return true if every method in intrface is present on object.  false otherwise
     */
    public static boolean instanceOf(Class intrface, Object object) {
        final Method[] methods = intrface.getMethods();
        Class candclass=object.getClass();
        for (int methodidx = 0; methodidx < methods.length; methodidx++) {
            Method method=methods[methodidx];
            try {
                candclass.getMethod(method.getName(), method.getParameterTypes());
            } catch (NoSuchMethodException e) {
                return false;
            }
        }
        return true;
    }
	
	protected DuckType(Object object) {
		this.object = object;
		this.objectClass = object.getClass();
	}

	protected Object object;
	protected Class objectClass;
	
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      if (method.getName().equals("equals") && args != null && args.length == 1) {
         return new Boolean(equals(args[0]));
      }
      if (method.getName().equals("hashCode") && args == null) {
         return new Integer(hashCode());
      }
      if (method.getName().equals("duckType_GetWrappedValue") && args == null) {
         return object;
      }
		Method realMethod = objectClass.getMethod(method.getName(), method.getParameterTypes());
      if (!realMethod.isAccessible()) {
         realMethod.setAccessible(true);
      }
		return realMethod.invoke(object, args);
	}
   
   public boolean equals(Object obj) {
      if (obj instanceof Wrapper) {
         Wrapper proxy = (Wrapper) obj;
         Object wrappedValue = proxy.duckType_GetWrappedValue();
         return wrappedValue.equals(object);
      }
      return obj == this || super.equals(obj) || object.equals(obj);
   }
   
   public int hashCode() {
      return object.hashCode();
   }
}

Back to the top