| /* |
| * 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(); |
| } |
| } |