PageRenderTime 44ms CodeModel.GetById 14ms app.highlight 22ms RepoModel.GetById 1ms app.codeStats 1ms

/interpreter/tags/at2-build060407/src/edu/vub/at/objects/coercion/Coercer.java

http://ambienttalk.googlecode.com/
Java | 152 lines | 75 code | 14 blank | 63 comment | 25 complexity | 50e1324d1ca8f7708614f14808b91ef8 MD5 | raw file
  1/**
  2 * AmbientTalk/2 Project
  3 * Coercer.java created on 3-okt-2006 at 16:12:05
  4 * (c) Programming Technology Lab, 2006 - 2007
  5 * Authors: Tom Van Cutsem & Stijn Mostinckx
  6 * 
  7 * Permission is hereby granted, free of charge, to any person
  8 * obtaining a copy of this software and associated documentation
  9 * files (the "Software"), to deal in the Software without
 10 * restriction, including without limitation the rights to use,
 11 * copy, modify, merge, publish, distribute, sublicense, and/or
 12 * sell copies of the Software, and to permit persons to whom the
 13 * Software is furnished to do so, subject to the following
 14 * conditions:
 15 *
 16 * The above copyright notice and this permission notice shall be
 17 * included in all copies or substantial portions of the Software.
 18 *
 19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 20 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 21 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 22 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 23 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 24 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 25 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 26 * OTHER DEALINGS IN THE SOFTWARE.
 27 */
 28package edu.vub.at.objects.coercion;
 29
 30import edu.vub.at.actors.eventloops.Callable;
 31import edu.vub.at.actors.eventloops.EventLoop;
 32import edu.vub.at.actors.eventloops.EventLoop.EventProcessor;
 33import edu.vub.at.actors.natives.ELActor;
 34import edu.vub.at.exceptions.XIllegalOperation;
 35import edu.vub.at.exceptions.XTypeMismatch;
 36import edu.vub.at.objects.ATObject;
 37import edu.vub.at.objects.mirrors.Reflection;
 38import edu.vub.at.objects.natives.NATObject;
 39import edu.vub.at.objects.symbiosis.Symbiosis;
 40import edu.vub.at.objects.symbiosis.SymbioticATObjectMarker;
 41
 42import java.lang.reflect.InvocationHandler;
 43import java.lang.reflect.InvocationTargetException;
 44import java.lang.reflect.Method;
 45import java.lang.reflect.Proxy;
 46
 47/**
 48 * A coercer is a dynamic proxy which is used to 'cast' Ambienttalk base-level NATObjects to a certain ATxxx interface.
 49 * The dynamic proxy is responsible for transforming java calls to meta_invoke calls.
 50 * 
 51 * For example, a method from an AT interface
 52 * 
 53 * ATExpression base_getExpression() throws NATException
 54 * 
 55 * receives the following implementation:
 56 * 
 57 * ATExpression getExpression() throws NATException {
 58 *	return principal.meta_invoke(principal, Reflection.downSelector("getExpression"), NATTable.EMPTY).asExpression();
 59 * }
 60 * 
 61 * where principal is the original object 'coerced into' the given interface
 62 * 
 63 * @author tvcutsem
 64 */
 65public final class Coercer implements InvocationHandler {
 66	
 67	private final NATObject principal_;
 68	
 69	// we have to remember which thread owned the principal
 70	private final Thread wrappingThread_;
 71	
 72	private Coercer(NATObject principal) {
 73		principal_ = principal;
 74		wrappingThread_ = Thread.currentThread();
 75	}
 76	
 77	public String toString() {
 78		return "<coercer on: "+principal_+">";
 79	}
 80	
 81	public static final Object coerce(ATObject object, Class type) throws XTypeMismatch {
 82		if (type.isInstance(object)) { // object instanceof type
 83			return object; // no need to coerce
 84		} else if (object.isAmbientTalkObject() && type.isInterface()) {
 85			// note that the proxy implements both the required type
 86			// and the Symbiotic object marker interface to identify it as a wrapper
 87			return Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),
 88					                      new Class[] { type, SymbioticATObjectMarker.class },
 89					                      new Coercer(object.asAmbientTalkObject()));
 90		} else {
 91			throw new XTypeMismatch(type, object);
 92		}
 93	}
 94
 95	public Object invoke(Object receiver, final Method method, Object[] arguments) throws Throwable {
 96		Class methodImplementor = method.getDeclaringClass();
 97		// handle toString, hashCode and equals in a dedicated fashion
 98		// similarly, handle AT conversion methods by simply forwarding them to the native AT object
 99		if (methodImplementor == Object.class || methodImplementor == ATConversions.class) {
100			// invoke these methods on the principal rather than on the proxy
101			try {
102				return method.invoke(principal_, arguments);
103			} catch (InvocationTargetException e) {
104				throw e.getTargetException();
105			}
106		// intercept access to the wrapped object for Java->AT value conversion
107		// or for serialization purposes
108		} else if (method.getDeclaringClass() == SymbioticATObjectMarker.class) {
109			return principal_;
110		} else {
111			
112			final ATObject[] symbioticArgs;
113            // support for variable-arity invocations from within AmbientTalk
114			if ((arguments != null) && (arguments.length == 1) && (arguments[0] instanceof ATObject[])) {
115				// no need to convert arguments
116				symbioticArgs = (ATObject[]) arguments[0];
117			} else {
118				symbioticArgs = new ATObject[(arguments == null) ? 0 : arguments.length];
119				for (int i = 0; i < symbioticArgs.length; i++) {
120					symbioticArgs[i] = Symbiosis.javaToAmbientTalk(arguments[i]);
121				}
122			}
123			
124			// if the current thread is not an actor thread, treat the Java invocation
125			// as a message send instead and enqueue it in my actor's thread
126			
127			if (Thread.currentThread() != wrappingThread_) {
128				if (Thread.currentThread() instanceof EventProcessor) {
129					// another event loop has direct access to this object, this means
130					// an AT object has been shared between actors via Java, signal an error
131					throw new XIllegalOperation("Detected illegal invocation: sharing via Java level of object " + principal_);
132				}
133				
134				// because a message send is asynchronous and Java threads work synchronously,
135				// we'll have to make the Java thread wait for the result
136				ELActor owningActor = (ELActor) EventLoop.toEventLoop(wrappingThread_);
137				return owningActor.sync_event_symbioticInvocation(new Callable() {
138					public Object call(Object actorMirror) throws Exception {
139						ATObject result = Reflection.downInvocation(principal_, method, symbioticArgs);
140						return Symbiosis.ambientTalkToJava(result, method.getReturnType());
141					}
142				});
143			} else {
144				// perform a synchronous invocation
145				ATObject result = Reflection.downInvocation(principal_, method, symbioticArgs);
146				// properly 'cast' the returned object into the appropriate interface
147				return Symbiosis.ambientTalkToJava(result, method.getReturnType());		
148			}
149		}
150	}
151	
152}