blob: 95dabe85b25c3d694a1a03984b17ca13f4dd271f [file] [log] [blame]
/*
* Copyright 2010 Google 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 com.google.web.bindery.autobean.vm.impl;
import com.google.web.bindery.autobean.shared.AutoBean;
import com.google.web.bindery.autobean.shared.AutoBeanUtils;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* Implements an AutoBean's shim interface that intercepts calls to the backing
* object.
*
* @param <T> the interface type of the AutoBean
*/
class ShimHandler<T> implements InvocationHandler {
private final ProxyAutoBean<T> bean;
private final Method interceptor;
public ShimHandler(ProxyAutoBean<T> bean, T toWrap) {
this.bean = bean;
Method maybe = null;
for (Class<?> clazz : bean.getConfiguration().getCategories()) {
try {
maybe = clazz.getMethod("__intercept", AutoBean.class, Object.class);
break;
} catch (SecurityException expected) {
} catch (NoSuchMethodException expected) {
}
}
interceptor = maybe;
}
@Override
public boolean equals(Object couldBeShim) {
if (couldBeShim == null) {
return false;
}
// Handles the foo.equals(foo) case
if (Proxy.isProxyClass(couldBeShim.getClass())
&& this == Proxy.getInvocationHandler(couldBeShim)) {
return true;
}
return bean.getWrapped().equals(couldBeShim);
}
@Override
public int hashCode() {
return bean.getWrapped().hashCode();
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
method.setAccessible(true);
Object toReturn;
String name = method.getName();
method.setAccessible(true);
try {
if (BeanMethod.OBJECT.matches(method)) {
return method.invoke(this, args);
} else if (BeanMethod.GET.matches(method)) {
toReturn = method.invoke(bean.getWrapped(), args);
toReturn = bean.get(name, toReturn);
} else if (BeanMethod.SET.matches(method) || BeanMethod.SET_BUILDER.matches(method)) {
toReturn = method.invoke(bean.getWrapped(), args);
bean.set(name, args[0]);
} else {
// XXX How should freezing and calls work together?
toReturn = method.invoke(bean.getWrapped(), args);
bean.call(name, toReturn, args);
}
Class<?> intf = method.getReturnType();
if (!Object.class.equals(intf)) {
// XXX Need to deal with resolving generic T return types
toReturn = maybeWrap(intf, toReturn);
}
if (interceptor != null) {
toReturn = interceptor.invoke(null, bean, toReturn);
}
} catch (InvocationTargetException e) {
throw e.getCause();
}
return toReturn;
}
@Override
public String toString() {
return bean.getWrapped().toString();
}
private Object maybeWrap(Class<?> intf, Object toReturn) {
if (toReturn == null) {
return null;
}
AutoBean<?> returnBean = AutoBeanUtils.getAutoBean(toReturn);
if (returnBean != null) {
return returnBean.as();
}
if (TypeUtils.isValueType(intf) || TypeUtils.isValueType(toReturn.getClass())
|| bean.getConfiguration().getNoWrap().contains(intf)) {
return toReturn;
}
if (toReturn.getClass().isArray()) {
/*
* We can't reliably wrap arrays, but the only time we typically see an
* array is with toArray() call on a collection, since arrays aren't
* supported property types.
*/
return toReturn;
}
ProxyAutoBean<Object> newBean =
new ProxyAutoBean<Object>(bean.getFactory(), intf, bean.getConfiguration(), toReturn);
return newBean.as();
}
}