All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openrdf.repository.object.composition.helpers.InvocationMessageContext Maven / Gradle / Ivy

Go to download

The Object Composition library merges multiple Java objects into a single multi-subject object.

There is a newer version: 2.4
Show newest version
/*
 * Copyright (c) 2007-2009, James Leigh All rights reserved.
 * Copyright (c) 2011 Talis Inc., Some rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * - Redistributions of source code must retain the above copyright notice, this
 *   list of conditions and the following disclaimer.
 * - Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution. 
 * - Neither the name of the openrdf.org nor the names of its contributors may
 *   be used to endorse or promote products derived from this software without
 *   specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 * 
 */
package org.openrdf.repository.object.composition.helpers;

import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;

import org.openrdf.annotations.Iri;
import org.openrdf.repository.object.exceptions.BehaviourException;
import org.openrdf.repository.object.traits.BooleanMessage;
import org.openrdf.repository.object.traits.ByteMessage;
import org.openrdf.repository.object.traits.CharacterMessage;
import org.openrdf.repository.object.traits.DoubleMessage;
import org.openrdf.repository.object.traits.FloatMessage;
import org.openrdf.repository.object.traits.IntegerMessage;
import org.openrdf.repository.object.traits.LongMessage;
import org.openrdf.repository.object.traits.MessageContext;
import org.openrdf.repository.object.traits.ObjectMessage;
import org.openrdf.repository.object.traits.ShortMessage;
import org.openrdf.repository.object.traits.VoidMessage;

/**
 * Implements the Message interface(s) through an InvocationHandler.
 *
 * @author James Leigh
 *
 */
public class InvocationMessageContext implements ObjectMessage {

	private static class Invocation implements MessageContext {
		protected final ObjectMessage delegate;

		public Invocation(ObjectMessage delegate) {
			this.delegate = delegate;
		}

		public Object getTarget() {
			return delegate.getTarget();
		}

		public Method getMethod() {
			return delegate.getMethod();
		}

		public Object[] getParameters() {
			return delegate.getParameters();
		}

		public void setParameters(Object[] parameters) {
			delegate.setParameters(parameters);
		}
	}

	private static class BooleanInvocation extends Invocation implements
			BooleanMessage {
		public BooleanInvocation(ObjectMessage delegate) {
			super(delegate);
		}

		public boolean proceed() throws Exception {
			return (Boolean) delegate.proceed();
		}
	}

	private static class ByteInvocation extends Invocation implements
			ByteMessage {
		public ByteInvocation(ObjectMessage delegate) {
			super(delegate);
		}

		public byte proceed() throws Exception {
			return (Byte) delegate.proceed();
		}
	}

	private static class CharacterInvocation extends Invocation implements
			CharacterMessage {
		public CharacterInvocation(ObjectMessage delegate) {
			super(delegate);
		}

		public char proceed() throws Exception {
			return (Character) delegate.proceed();
		}
	}

	private static class DoubleInvocation extends Invocation implements
			DoubleMessage {
		public DoubleInvocation(ObjectMessage delegate) {
			super(delegate);
		}

		public double proceed() throws Exception {
			return (Double) delegate.proceed();
		}
	}

	private static class FloatInvocation extends Invocation implements
			FloatMessage {
		public FloatInvocation(ObjectMessage delegate) {
			super(delegate);
		}

		public float proceed() throws Exception {
			return (Float) delegate.proceed();
		}
	}

	private static class IntegerInvocation extends Invocation implements
			IntegerMessage {
		public IntegerInvocation(ObjectMessage delegate) {
			super(delegate);
		}

		public int proceed() throws Exception {
			return (Integer) delegate.proceed();
		}
	}

	private static class LongInvocation extends Invocation implements
			LongMessage {
		public LongInvocation(ObjectMessage delegate) {
			super(delegate);
		}

		public long proceed() throws Exception {
			return (Long) delegate.proceed();
		}
	}

	private static class ShortInvocation extends Invocation implements
			ShortMessage {
		public ShortInvocation(ObjectMessage delegate) {
			super(delegate);
		}

		public short proceed() throws Exception {
			return (Short) delegate.proceed();
		}
	}

	private static class VoidInvocation extends Invocation implements
			VoidMessage {
		public VoidInvocation(ObjectMessage delegate) {
			super(delegate);
		}

		public void proceed() throws Exception {
			delegate.proceed();
		}
	}

	private final Object target;

	private final Method method;

	private Object[] parameters;

	private final List invokeTarget = new ArrayList();

	private final List invokeMethod = new ArrayList();

	private int count;

	public InvocationMessageContext(Object target, Method method,
			Object[] parameters) {
		this.target = target;
		this.method = method;
		this.parameters = parameters;
	}

	public synchronized InvocationMessageContext appendInvocation(Object target,
			Method method) {
		invokeTarget.add(target);
		invokeMethod.add(method);
		return this;
	}

	@Override
	public synchronized String toString() {
		String params = Arrays.asList(parameters).toString();
		String values = params.substring(1, params.length() - 1);
		return method.getName() + "(" + values + ")";
	}

	public Method getMethod() {
		return method;
	}

	public synchronized Object[] getParameters() {
		return parameters;
	}

	public synchronized void setParameters(Object[] parameters) {
		this.parameters = parameters;
	}

	public synchronized Object proceed() throws Exception {
		try {
			while (true) {
				Class responseType = method.getReturnType();
				if (count >= invokeTarget.size()) {
					return nil(responseType);
				}
				Method im = invokeMethod.get(count);
				Object it = invokeTarget.get(count);
				count++;
				Class[] param = im.getParameterTypes();
				Class resultType = im.getReturnType();
				if (param.length == 1
						&& MessageContext.class.isAssignableFrom(param[0])) {
					Object result = im.invoke(it, returns(resultType));
					return cast(result, resultType, responseType);
				} else {
					Object result = im.invoke(it, getParameters(im));
					if (isNil(result, resultType))
						continue;
					return cast(result, resultType, responseType);
				}
			}
		} catch (InvocationTargetException e) {
			Throwable cause = e.getCause();
			if (cause instanceof Exception)
				throw (Exception) cause;
			if (cause instanceof Error)
				throw (Error) cause;
			throw new BehaviourException(cause);
		} catch (IllegalArgumentException e) {
			throw e;
		} catch (IllegalAccessException e) {
			IllegalAccessError error = new IllegalAccessError(e.getMessage());
			error.initCause(e);
			throw error;
		}
	}

	public Object getTarget() {
		return target;
	}

	private MessageContext returns(Class returnType) {
		if (!returnType.isPrimitive())
			return this;
		if (Boolean.TYPE.equals(returnType))
			return new BooleanInvocation(this);
		if (Byte.TYPE.equals(returnType))
			return new ByteInvocation(this);
		if (Character.TYPE.equals(returnType))
			return new CharacterInvocation(this);
		if (Double.TYPE.equals(returnType))
			return new DoubleInvocation(this);
		if (Float.TYPE.equals(returnType))
			return new FloatInvocation(this);
		if (Integer.TYPE.equals(returnType))
			return new IntegerInvocation(this);
		if (Long.TYPE.equals(returnType))
			return new LongInvocation(this);
		if (Short.TYPE.equals(returnType))
			return new ShortInvocation(this);
		if (Void.TYPE.equals(returnType))
			return new VoidInvocation(this);
		throw new AssertionError("Unknown primitive: " + returnType);
	}

	private Object cast(Object result, Class resultType,
			Class responseType) {
		if (isNil(result, resultType))
			return nil(responseType);
		if (resultType.equals(responseType) || Object.class.equals(resultType))
			return result;
		if (responseType.equals(Set.class))
			return Collections.singleton(result);
		if (resultType.equals(Set.class)) {
			Set set = (Set) result;
			if (set.isEmpty())
				return nil(responseType);
			return set.iterator().next();
		}
		return result;
	}

	private boolean isNil(Object result, Class type) {
		if (result == null)
			return true;
		if (!type.isPrimitive())
			return false;
		return result.equals(nil(type));
	}

	private Object nil(Class type) {
		if (Set.class.equals(type))
			return Collections.emptySet();
		if (!type.isPrimitive())
			return null;
		if (Void.TYPE.equals(type))
			return null;
		if (Boolean.TYPE.equals(type))
			return Boolean.FALSE;
		if (Character.TYPE.equals(type))
			return Character.valueOf((char) 0);
		if (Byte.TYPE.equals(type))
			return Byte.valueOf((byte) 0);
		if (Short.TYPE.equals(type))
			return Short.valueOf((short) 0);
		if (Integer.TYPE.equals(type))
			return Integer.valueOf((int) 0);
		if (Long.TYPE.equals(type))
			return Long.valueOf((long) 0);
		if (Float.TYPE.equals(type))
			return Float.valueOf((float) 0);
		if (Double.TYPE.equals(type))
			return Double.valueOf((double) 0);
		throw new AssertionError();
	}

	private int getParameterIndex(String uri) {
		Annotation[][] anns = method.getParameterAnnotations();
		for (int i = 0; i < anns.length; i++) {
			for (int j = 0; j < anns[i].length; j++) {
				if (anns[i][j].annotationType().equals(Iri.class)) {
					if (((Iri) anns[i][j]).value().equals(uri)) {
						return i;
					}
				}
			}
		}
		throw new UnsupportedOperationException("Parameter not found: " + uri);
	}

	private Object[] getParameters(Method method) {
		Object[] parameters = getParameters();
		Annotation[][] anns = method.getParameterAnnotations();
		Object[] result = new Object[anns.length];
		for (int i = 0; i < anns.length; i++) {
			if (i < parameters.length) {
				// if no @rdf copy over parameter by position
				result[i] = parameters[i];
			}
			for (int j = 0; j < anns[i].length; j++) {
				if (anns[i][j].annotationType().equals(Iri.class)) {
					String uri = ((Iri) anns[i][j]).value();
					result[i] = parameters[getParameterIndex(uri)];
				}
			}
		}
		return result;
	}

}