| /* |
| * 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.gwt.autobean.server; |
| |
| import com.google.gwt.autobean.server.impl.TypeUtils; |
| import com.google.gwt.autobean.shared.AutoBean; |
| import com.google.gwt.autobean.shared.AutoBeanUtils; |
| |
| import java.lang.reflect.Array; |
| import java.lang.reflect.InvocationHandler; |
| 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; |
| private final T toWrap; |
| |
| public ShimHandler(ProxyAutoBean<T> bean, T toWrap) { |
| this.bean = bean; |
| this.toWrap = toWrap; |
| |
| 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(); |
| bean.checkWrapped(); |
| method.setAccessible(true); |
| if (BeanMethod.OBJECT.matches(method)) { |
| return method.invoke(this, args); |
| } else if (BeanMethod.GET.matches(method)) { |
| toReturn = method.invoke(toWrap, args); |
| toReturn = bean.get(name, toReturn); |
| } else if (BeanMethod.SET.matches(method)) { |
| bean.checkFrozen(); |
| toReturn = method.invoke(toWrap, args); |
| bean.set(name, args[0]); |
| } else { |
| // XXX How should freezing and calls work together? |
| // bean.checkFrozen(); |
| toReturn = method.invoke(toWrap, 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); |
| } |
| return toReturn; |
| } |
| |
| @Override |
| public String toString() { |
| return bean.getWrapped().toString(); |
| } |
| |
| private Object maybeWrap(Class<?> intf, Object toReturn) { |
| if (toReturn == null) { |
| return null; |
| } |
| if (TypeUtils.isValueType(intf) |
| || TypeUtils.isValueType(toReturn.getClass()) |
| || AutoBeanUtils.getAutoBean(toReturn) != null |
| || bean.getConfiguration().getNoWrap().contains(intf)) { |
| return toReturn; |
| } |
| if (toReturn.getClass().isArray()) { |
| for (int i = 0, j = Array.getLength(toReturn); i < j; i++) { |
| Object value = Array.get(toReturn, i); |
| if (value != null) { |
| Array.set(toReturn, i, maybeWrap(value.getClass(), value)); |
| } |
| } |
| return toReturn; |
| } |
| ProxyAutoBean<Object> newBean = new ProxyAutoBean<Object>( |
| bean.getFactory(), intf, bean.getConfiguration(), toReturn); |
| return newBean.as(); |
| } |
| } |