/**
* Copyright 2011 multibit.org
*
* Licensed under the MIT license (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://opensource.org/licenses/mit-license.php
*
* 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 net.bither.platform.builder.mac;
import net.bither.platform.handler.GenericHandler;
import net.bither.platform.listener.GenericEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* <p>Base class to provide the following to Mac invocation handlers:</p>
* <ul>
* <li>Provision of standard template handling for simple events (no responses)</li>
* </ul>
* <p>Template keys are:</p>
* <ul>
* <li><code>H</code>: The generic handler class (e.g. {@link AboutHandlerInvocationHandler}</li>
* <li><code>E</code>: The generic event class (e.g. {@link net.bither.platform.listener.GenericAboutEvent}</li>
* </ul>
*
* @since 0.3.0
*/
public abstract class BaseMacInvocationHandler<H extends GenericHandler, E extends GenericEvent> implements InvocationHandler {
private static final Logger log = LoggerFactory.getLogger(BaseMacInvocationHandler.class);
private final H genericHandler;
private final Class<E> genericEventClass;
BaseMacInvocationHandler(H genericHandler, Class<E> genericEventClass) {
this.genericHandler = genericHandler;
this.genericEventClass = genericEventClass;
}
/**
* Handles the invocation
*
* @param object The object
* @param nativeMethod The native method
* @param objects The arguments
* @return The result of the call
* @throws Throwable If something goes wrong
*/
@Override
@SuppressWarnings("unchecked")
public Object invoke(Object object, Method nativeMethod, Object[] objects) throws Throwable {
log.debug("Invoked. NativeMethod={}, method args length={}", nativeMethod.getName(), objects.length);
// Create a uni-directional generic event based on a single parameter (the native event)
// Require an unchecked cast here to avoid this issue:
// http://blog.sarathonline.com/2010/08/maven-only-type-parameters-of-x-cannot.html
E event = (E) createGenericEvent(objects[0]);
try {
log.debug("Created event {}", genericEventClass.getSimpleName());
// Access the equivalent method on the generic handler (e.g. handleQuit(GenericQuitEvent event))
Method method = genericHandler.getClass().getMethod(nativeMethod.getName(), new Class[]{genericEventClass});
// Invoke the method passing in the event (e.g. GenericURIEvent)
log.debug("Invoking {}.{}({}) ", new Object[]{genericHandler.getClass().getSimpleName(), method.getName(), method.getParameterTypes()[0].getSimpleName()});
return method.invoke(genericHandler, event);
} catch (NoSuchMethodException e) {
log.warn("Got a NoSuchMethodException");
if (nativeMethod.getName().equals("equals") && objects.length == 1) {
return object == objects[0];
}
return null;
}
}
/**
* <p>Create a proxy instance of {@link net.bither.platform.listener.GenericEvent}
* that delegates to the native Apple event</p>
*
* @param nativeEvent The Apple native event (e.g. OpenURIEvent)
* @return The generic event acting as a proxy to the native underlying event
*/
@SuppressWarnings("unchecked")
private <E> E createGenericEvent(final Object nativeEvent) {
log.debug("Building invocation handler. Native {} -> {}", nativeEvent.getClass().getSimpleName(), genericEventClass.getSimpleName());
// The invocation handler manages all method calls against the proxy
// Relies on the proxy having the same method signatures
InvocationHandler invocationHandler = new InvocationHandler() {
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
Method nativeMethod = nativeEvent.getClass().getMethod(method.getName(), method.getParameterTypes());
log.debug("Invoking method {}.{}", nativeEvent.getClass().getSimpleName(), method.getName());
return nativeMethod.invoke(nativeEvent, objects);
}
};
log.debug("Building proxy for generic event");
// Create a proxy that delegates the call to the native instance
// when the invocation handler is called against it's methods
// Must use the application ClassLoader
return (E) Proxy.newProxyInstance(getClass().getClassLoader(),
new Class[]{genericEventClass}, invocationHandler);
}
}