Package com.google.gdt.eclipse.designer.util

Source Code of com.google.gdt.eclipse.designer.util.GwtInvocationEvaluatorInterceptor

/*******************************************************************************
* Copyright 2011 Google Inc. All Rights Reserved.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* 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.gdt.eclipse.designer.util;

import com.google.common.collect.Maps;
import com.google.gdt.eclipse.designer.IExceptionConstants;

import org.eclipse.wb.core.eval.AstEvaluationEngine;
import org.eclipse.wb.core.eval.DefaultMethodInterceptor;
import org.eclipse.wb.core.eval.EvaluationContext;
import org.eclipse.wb.core.eval.InvocationEvaluatorInterceptor;
import org.eclipse.wb.internal.core.model.description.ComponentDescription;
import org.eclipse.wb.internal.core.model.description.helpers.ComponentDescriptionHelper;
import org.eclipse.wb.internal.core.model.util.PlaceholderUtils;
import org.eclipse.wb.internal.core.model.util.ScriptUtils;
import org.eclipse.wb.internal.core.utils.ast.AstEditor;
import org.eclipse.wb.internal.core.utils.ast.AstNodeUtils;
import org.eclipse.wb.internal.core.utils.exception.DesignerException;
import org.eclipse.wb.internal.core.utils.exception.FatalDesignerException;
import org.eclipse.wb.internal.core.utils.jdt.core.CodeUtils;
import org.eclipse.wb.internal.core.utils.reflect.ReflectionUtils;
import org.eclipse.wb.internal.core.utils.state.EditorState;

import org.eclipse.jdt.core.dom.ClassInstanceCreation;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.MethodInvocation;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import org.apache.commons.lang.StringUtils;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.text.MessageFormat;
import java.util.Map;

/**
* Resolves methods of <code>JavaScriptObject</code> objects.
*
* @author scheglov_ke
* @coverage gwt.util
*/
public final class GwtInvocationEvaluatorInterceptor extends InvocationEvaluatorInterceptor {
  private static final String JSO_NAME = "com.google.gwt.core.client.JavaScriptObject";

  ////////////////////////////////////////////////////////////////////////////
  //
  // InvocationEvaluatorInterceptor
  //
  ////////////////////////////////////////////////////////////////////////////
  @Override
  public Method resolveMethod(Class<?> clazz, String signature) throws Exception {
    if (isJavaScriptObject_rewrittenInterface(clazz)) {
      String implClassName = clazz.getName() + "$";
      Class<?> implClass = clazz.getClassLoader().loadClass(implClassName);
      return ReflectionUtils.getMethodBySignature(implClass, signature);
    }
    return null;
  }

  @Override
  public Object evaluateAnonymous(EvaluationContext context,
      ClassInstanceCreation expression,
      ITypeBinding typeBinding,
      ITypeBinding typeBindingConcrete,
      IMethodBinding methodBinding,
      Object[] arguments) throws Exception {
    if (AstNodeUtils.isSuccessorOf(typeBinding, "com.google.gwt.user.client.Command")) {
      return null;
    }
    if (AstNodeUtils.isSuccessorOf(typeBinding, "com.google.gwt.text.shared.Renderer")) {
      return null;
    }
    if (AstNodeUtils.isSuccessorOf(typeBinding, "com.google.gwt.cell.client.Cell")) {
      return getCellFake(context, typeBinding, methodBinding, arguments);
    }
    if (AstNodeUtils.isSuccessorOf(typeBinding, "com.google.gwt.view.client.TreeViewModel")) {
      return getTreeViewModelFake(context, typeBinding, methodBinding, arguments);
    }
    if (AstNodeUtils.isSuccessorOf(typeBinding, "com.google.gwt.user.cellview.client.Column")) {
      return getColumnFake(context, typeBinding);
    }
    if (AstNodeUtils.isSuccessorOf(typeBinding, "com.google.gwt.user.cellview.client.Header")) {
      return AstEvaluationEngine.createAnonymousInstance(context, methodBinding, arguments);
    }
    return AstEvaluationEngine.UNKNOWN;
  }

  /**
   * Support evaluating <code>com.google.gwt.cell.client.Cell</code> instances.
   *
   * @return instance with overridden "renderer" method.
   */
  private Object getCellFake(EvaluationContext context,
      ITypeBinding typeBinding,
      IMethodBinding methodBinding,
      Object[] arguments) throws Exception {
    final String renderSignature =
        MessageFormat.format(
            "render({0},{1},{2})",
            "com.google.gwt.cell.client.Cell.Context",
            "java.lang.Object",
            "com.google.gwt.safehtml.shared.SafeHtmlBuilder");
    return AstEvaluationEngine.createAnonymousInstance(
        context,
        methodBinding,
        arguments,
        new DefaultMethodInterceptor() {
          @Override
          public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy)
              throws Throwable {
            String signature = ReflectionUtils.getMethodSignature(method);
            if (renderSignature.equals(signature)) {
              Object safeHtmlBuilder = args[2];
              String toAppend = "<rendered Cell value>";
              ReflectionUtils.invokeMethod(
                  safeHtmlBuilder,
                  "appendEscaped(java.lang.String)",
                  toAppend);
              return null;
            }
            return super.intercept(obj, method, args, proxy);
          }
        });
  }

  /**
   * Support evaluating <code>com.google.gwt.view.client.TreeViewModel</code> instances.
   *
   * @return Fake <code>com.google.gwt.view.client.TreeViewModel</code> instance.
   */
  private Object getTreeViewModelFake(EvaluationContext context,
      ITypeBinding typeBinding,
      IMethodBinding methodBinding,
      Object[] arguments) throws Exception {
    ClassLoader classLoader = context.getClassLoader();
    final Object nodeInfoFake = TreeViewModelSupport.getNodeInfoFake(classLoader);
    return AstEvaluationEngine.createAnonymousInstance(
        context,
        methodBinding,
        arguments,
        new DefaultMethodInterceptor() {
          @Override
          public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy)
              throws Throwable {
            // intercept com.google.gwt.view.client.TreeViewModel.getNodeInfo(T)
            if ("getNodeInfo".equals(method.getName())) {
              Class<?>[] parameterTypes = method.getParameterTypes();
              if (parameterTypes.length == 1) {
                return nodeInfoFake;
              }
            }
            // intercept com.google.gwt.view.client.TreeViewModel.isLeaf(Object)
            if ("isLeaf".equals(method.getName())) {
              Class<?>[] parameterTypes = method.getParameterTypes();
              if (parameterTypes.length == 1
                  && "java.lang.Object".equals(ReflectionUtils.getFullyQualifiedName(
                      parameterTypes[0],
                      false))) {
                if (args[0] == nodeInfoFake) {
                  return Boolean.TRUE;
                } else {
                  return Boolean.FALSE;
                }
              }
            }
            return super.intercept(obj, method, args, proxy);
          }
        });
  }

  /**
   * Support for evaluating <code>com.google.gwt.view.client.Column</code> instances.
   *
   * @return the instance of <code>TextColumn</code>.
   */
  private Object getColumnFake(EvaluationContext context, ITypeBinding typeBinding)
      throws Exception {
    String columnText;
    {
      String columnValueTypeName = getColumnValueTypeName(typeBinding);
      String shortTypeName = CodeUtils.getShortClass(columnValueTypeName);
      columnText = "<" + StringUtils.substring(shortTypeName, 0, 20) + ">";
    }
    // create TextColumn
    ClassLoader classLoader = context.getClassLoader();
    return createTextColumn(classLoader, columnText);
  }

  /**
   * @return the instance of <code>TextColumn</code> for displaying static text;
   */
  public static Object createTextColumn(ClassLoader classLoader, final String columnText)
      throws ClassNotFoundException {
    Class<?> classTextColumn =
        classLoader.loadClass("com.google.gwt.user.cellview.client.TextColumn");
    // prepare Enhancer
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(classTextColumn);
    enhancer.setCallback(new MethodInterceptor() {
      public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy)
          throws Throwable {
        if (ReflectionUtils.getMethodSignature(method).equals("getValue(java.lang.Object)")) {
          return columnText;
        }
        return proxy.invokeSuper(obj, args);
      }
    });
    // create instance
    return enhancer.create();
  }

  /**
   * @return the name of <code>getValue()</code> return type.
   */
  private String getColumnValueTypeName(ITypeBinding columnTypeBinding) {
    ITypeBinding cellArgumentTypeBinding =
        AstNodeUtils.getTypeBindingArgument(
            columnTypeBinding,
            "com.google.gwt.user.cellview.client.Column",
            1);
    return AstNodeUtils.getFullyQualifiedName(cellArgumentTypeBinding, false);
  }

  ////////////////////////////////////////////////////////////////////////////
  //
  // Invocation
  //
  ////////////////////////////////////////////////////////////////////////////
  @Override
  public Object evaluate(EvaluationContext context,
      MethodInvocation invocation,
      IMethodBinding methodBinding,
      Class<?> clazz,
      Method method,
      Object[] argumentValues) {
    // Panel.add(Widget) throws exception, so make it fatal
    if (ReflectionUtils.getMethodSignature(method).equals(
        "add(com.google.gwt.user.client.ui.Widget)")) {
      if (method.getDeclaringClass().getName().equals("com.google.gwt.user.client.ui.Panel")) {
        FatalDesignerException e =
            new FatalDesignerException(IExceptionConstants.PANEL_ADD_INVOCATION,
                invocation.toString());
        e.setSourcePosition(invocation.getStartPosition());
        throw e;
      }
    }
    return AstEvaluationEngine.UNKNOWN;
  }

  @Override
  public Object evaluate(EvaluationContext context,
      ClassInstanceCreation expression,
      ITypeBinding typeBinding,
      Class<?> clazz,
      Constructor<?> actualConstructor,
      Object[] arguments) throws Exception {
    if (isWidget(clazz)) {
      return evaluateGWT(context, expression, clazz, actualConstructor, arguments);
    }
    return AstEvaluationEngine.UNKNOWN;
  }

  private Object evaluateGWT(EvaluationContext context,
      ClassInstanceCreation expression,
      Class<?> clazz,
      Constructor<?> actualConstructor,
      Object[] arguments) throws Exception {
    PlaceholderUtils.clear(expression);
    // ignore Google Map
    if (clazz.getName().equals("com.google.gwt.maps.client.MapWidget")) {
      PlaceholderUtils.markPlaceholder(expression);
      return createPlaceholder(clazz);
    }
    // fix arguments
    fixArguments(clazz, actualConstructor, arguments);
    // ValueLabel++
    if (clazz.getName().equals("com.google.gwt.user.client.ui.ValueLabel")) {
      return createValueLabel(actualConstructor, arguments);
    }
    if (ReflectionUtils.isSuccessorOf(clazz, "com.google.gwt.user.client.ui.DateLabel")) {
      return createDateLabel(expression, actualConstructor, arguments);
    }
    if (ReflectionUtils.isSuccessorOf(clazz, "com.google.gwt.user.client.ui.NumberLabel")) {
      return createNumberLabel(expression, actualConstructor, arguments);
    }
    // try actual constructor
    try {
      return actualConstructor.newInstance(arguments);
    } catch (Throwable e) {
      context.addException(expression, e);
      PlaceholderUtils.addException(expression, e);
    }
    // some exception happened, try default constructor (if actual was not default)
    try {
      Constructor<?> defaultConstructor =
          ReflectionUtils.getConstructorBySignature(clazz, "<init>()");
      if (defaultConstructor != null
          && !ReflectionUtils.equals(actualConstructor, defaultConstructor)) {
        return defaultConstructor.newInstance();
      }
    } catch (Throwable e) {
      context.addException(expression, e);
      PlaceholderUtils.addException(expression, e);
    }
    // still no success, use placeholder
    PlaceholderUtils.markPlaceholder(expression);
    return createPlaceholder(clazz);
  }

  /**
   * Tweaks arguments to fix know problem cases.
   */
  private void fixArguments(Class<?> clazz, Constructor<?> constructor, Object[] arguments)
      throws Exception {
    if (clazz.getName().equals("com.google.gwt.user.client.ui.Tree")) {
      String signature = ReflectionUtils.getConstructorSignature(constructor);
      if (signature.equals("<init>(com.google.gwt.user.client.ui.TreeImages)")) {
        if (arguments[0] == null) {
          arguments[0] =
              ScriptUtils.evaluate(clazz.getClassLoader(), CodeUtils.getSource(
                  "import com.google.gwt.core.client.GWT;",
                  "import com.google.gwt.user.client.ui.*;",
                  "return GWT.create(TreeImages);"));
        }
      }
    }
    // prevent "null" as "cell" in CellList
    if (clazz.getName().equals("com.google.gwt.user.cellview.client.CellList")) {
      String signature = ReflectionUtils.getConstructorSignature(constructor);
      if (signature.equals("<init>(com.google.gwt.cell.client.Cell)")) {
        if (arguments[0] == null) {
          ClassLoader classLoader = clazz.getClassLoader();
          Class<?> classTextCell = classLoader.loadClass("com.google.gwt.cell.client.TextCell");
          arguments[0] = classTextCell.newInstance();
        }
      }
    }
  }

  ////////////////////////////////////////////////////////////////////////////
  //
  // ValueLabel
  //
  ////////////////////////////////////////////////////////////////////////////
  private static Object createValueLabel(Constructor<?> actualConstructor, Object[] arguments)
      throws Exception {
    Object valueLabel = actualConstructor.newInstance(arguments);
    // set text
    {
      String rendererName;
      if (arguments[0] == null) {
        rendererName = "null";
      } else {
        rendererName = CodeUtils.getShortClass(arguments[0].getClass().getName());
      }
      String text = "ValueLabel(" + rendererName + ")";
      setValueLabelText(valueLabel, text);
    }
    // done
    return valueLabel;
  }

  private static Object createDateLabel(ClassInstanceCreation expression,
      Constructor<?> actualConstructor,
      Object[] arguments) throws Exception {
    Object valueLabel = actualConstructor.newInstance(arguments);
    setValueLabelText(valueLabel, "12/31/2010");
    return valueLabel;
  }

  private static Object createNumberLabel(ClassInstanceCreation expression,
      Constructor<?> actualConstructor,
      Object[] arguments) throws Exception {
    Object valueLabel = actualConstructor.newInstance(arguments);
    // prepare text to show
    String text;
    {
      ITypeBinding creationBinding = AstNodeUtils.getTypeBinding(expression);
      ITypeBinding typeBinding =
          AstNodeUtils.getTypeBindingArgument(
              creationBinding,
              "com.google.gwt.user.client.ui.NumberLabel",
              0);
      String typeName = AstNodeUtils.getFullyQualifiedName(typeBinding, false);
      text = MessageFormat.format("NumberLabel<{0}>", CodeUtils.getShortClass(typeName));
    }
    // set text
    setValueLabelText(valueLabel, text);
    return valueLabel;
  }

  /**
   * Sets text for given <code>ValueLabel</code> widget.
   */
  public static void setValueLabelText(Object valueLabel, String text) throws Exception {
    Object directionalTextHelper =
        ReflectionUtils.getFieldObject(valueLabel, "directionalTextHelper");
    ReflectionUtils.invokeMethod(
        directionalTextHelper,
        "setTextOrHtml(java.lang.String,boolean)",
        text,
        false);
  }

  ////////////////////////////////////////////////////////////////////////////
  //
  // Exception
  //
  ////////////////////////////////////////////////////////////////////////////
  @Override
  public Throwable rewriteException(Throwable e) {
    if (isMailExampleException(e)) {
      String userStackTrace = AstEvaluationEngine.getUserStackTrace(e);
      return new DesignerException(IExceptionConstants.MAIL_SAMPLE_GET, e, userStackTrace);
    }
    return null;
  }

  /**
   * @return <code>true</code> if given {@link Throwable} is caused by GWT Mail example.
   */
  private static boolean isMailExampleException(Throwable e) {
    StackTraceElement[] stackTrace = e.getStackTrace();
    StackTraceElement element = stackTrace[0];
    return "com.google.gwt.sample.mail.client.MailList".equals(element.getClassName())
        && "selectRow".equals(element.getMethodName());
  }

  ////////////////////////////////////////////////////////////////////////////
  //
  // Implementation
  //
  ////////////////////////////////////////////////////////////////////////////
  private static boolean isWidget(Class<?> clazz) {
    return ReflectionUtils.isSuccessorOf(clazz, "com.google.gwt.user.client.ui.Widget");
  }

  private static boolean isJavaScriptObject_rewrittenInterface(Class<?> clazz) {
    return clazz.isInterface() && ReflectionUtils.isSuccessorOf(clazz, JSO_NAME);
  }

  /**
   * @return the <code>Widget</code> to use as placeholder instead of real component that can not be
   *         created because of some exception.
   */
  private static Object createPlaceholder(Class<?> clazz) throws Exception {
    String message =
        MessageFormat.format(
            "Exception during creation of: {0}. See \"Open error log\" for details.",
            CodeUtils.getShortClass(clazz.getName()));
    // script
    String script;
    {
      AstEditor editor = EditorState.getActiveJavaInfo().getEditor();
      ComponentDescription description = ComponentDescriptionHelper.getDescription(editor, clazz);
      script = description.getParameter("placeholderScript");
    }
    // variables
    Map<String, Object> variables = Maps.newTreeMap();
    variables.put("clazz", clazz);
    variables.put("message", message);
    // execute
    ClassLoader classLoader = clazz.getClassLoader();
    return ScriptUtils.evaluate(classLoader, script, variables);
  }
}
TOP

Related Classes of com.google.gdt.eclipse.designer.util.GwtInvocationEvaluatorInterceptor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.