Package com.google.gwt.jsio.rebind

Source Code of com.google.gwt.jsio.rebind.TaskFactory$WrapperPolicy

/*
* Copyright 2008 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.jsio.rebind;

import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JMethod;
import com.google.gwt.core.ext.typeinfo.JParameter;
import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
import com.google.gwt.core.ext.typeinfo.JType;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.jsio.client.BeanProperties;
import com.google.gwt.jsio.client.Binding;
import com.google.gwt.jsio.client.Constructor;
import com.google.gwt.jsio.client.Exported;
import com.google.gwt.jsio.client.Global;
import com.google.gwt.jsio.client.Imported;
import com.google.gwt.jsio.client.JSWrapper;

import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;

/**
* Examines types to produce Tasks.
*/
public class TaskFactory {
  /**
   * Defines an extraction policy when creating Tasks.
   */
  public static interface Policy {
    /**
     * Specifies the base interface type so that it will be ignored by
     * {@link #extractMethods()}.
     */
    Collection<JMethod> getOperableMethods(TypeOracle oracle, JClassType clazz);

    /**
     * Exporting methods via a flyweight interface is done by binding an
     * instance of a type (or just the static methods of a type) to a JSO.
     */
    boolean shouldBind(TreeLogger logger, TypeOracle oracle, JMethod m)
        throws UnableToCompleteException;

    /**
     * Determines if a method should be treated as an invocation of an
     * underlying JavaScript constructor function.
     */
    boolean shouldConstruct(TreeLogger logger, TypeOracle oracle, JMethod m)
        throws UnableToCompleteException;

    /**
     * Determines if the generator should generate an export binding for the
     * method.
     */
    boolean shouldExport(TreeLogger logger, TypeOracle oracle, JMethod m)
        throws UnableToCompleteException;

    /**
     * Determines if the generator should implement a particular method. A
     * method will be implemented only if it is abstract and defined in a class
     * derived from JSWrapper
     */
    boolean shouldImplement(TreeLogger logger, TypeOracle oracle, JMethod m)
        throws UnableToCompleteException;

    /**
     * Determines if the generator should generate an import binding for the
     * method.
     */
    boolean shouldImport(TreeLogger logger, TypeOracle oracle, JMethod m)
        throws UnableToCompleteException;
  }

  /**
   * This policy only checks to see if methods are tagged with gwt.exported. All
   * other methods will be ignored under this policy.
   */
  private static class ExporterPolicy implements Policy {
    public Collection<JMethod> getOperableMethods(TypeOracle oracle,
        JClassType clazz) {
      Map<String, JMethod> toReturn = new HashMap<String, JMethod>();
      Stack<JClassType> stack = new Stack<JClassType>();

      // Start by creating a stack that will look at all supertypes of the
      // class under inspection
      while (clazz != null) {
        stack.push(clazz);
        clazz = clazz.getSuperclass();
      }

      for (JClassType searchIn : stack) {
        for (JMethod m : searchIn.getMethods()) {
          // We add a stripped declaration so that changes which don't affect
          // the overall signature will be overwritten by the methods in the
          // leaf type.
          toReturn.put(m.getReadableDeclaration(true, true, true, true, true),
              m);
        }
      }

      return toReturn.values();
    }

    public boolean shouldBind(TreeLogger logger, TypeOracle oracle, JMethod m) {
      return false;
    }

    public boolean shouldConstruct(TreeLogger logger, TypeOracle oracle,
        JMethod m) {
      return false;
    }

    public boolean shouldExport(TreeLogger logger, TypeOracle typeOracle,
        JMethod method) throws UnableToCompleteException {
      return JSWrapperGenerator.hasTag(logger, method, Exported.class) != null;
    }

    public boolean shouldImplement(TreeLogger logger, TypeOracle oracle,
        JMethod m) {
      return false;
    }

    public boolean shouldImport(TreeLogger logger, TypeOracle oracle, JMethod m) {
      return false;
    }
  }

  /**
   * A variation on WrapperPolicy that handles the different signatures of
   * flyweight-style methods. Adds binding tasks.
   */
  private static class FlyweightPolicy extends WrapperPolicy {
    @Override
    public boolean shouldBind(TreeLogger logger, TypeOracle typeOracle,
        JMethod method) throws UnableToCompleteException {

      boolean hasBindingTag = JSWrapperGenerator.hasTag(logger, method,
          Binding.class) != null;
      JParameter[] params = method.getParameters();

      return method.isAbstract()
          && hasBindingTag
          && ((params.length == 1) || (params.length == 2))
          && isJsoOrPeer(typeOracle, params[0].getType())
          && ((params.length == 1) || (params[1].getType().isClassOrInterface() != null));
    }

    @Override
    public boolean shouldImport(TreeLogger logger, TypeOracle typeOracle,
        JMethod method) throws UnableToCompleteException {
      JClassType enclosing = method.getEnclosingType();
      String methodName = method.getName();
      int arguments = method.getParameters().length;

      boolean hasBindingTag = JSWrapperGenerator.hasTag(logger, method,
          Binding.class) != null;
      boolean hasImportTag = JSWrapperGenerator.hasTag(logger, method,
          Imported.class) != null;
      boolean methodHasBeanTag = JSWrapperGenerator.hasTag(logger, method,
          BeanProperties.class) != null;
      boolean classHasBeanTag = JSWrapperGenerator.hasTag(logger, enclosing,
          BeanProperties.class) != null;

      boolean isIs = (arguments == 1)
          && (methodName.startsWith("is"))
          && (JPrimitiveType.BOOLEAN.equals(method.getReturnType().isPrimitive()))
          && isJsoOrPeer(typeOracle, method.getParameters()[0].getType());
      boolean isGetter = (arguments == 1)
          && (methodName.startsWith("get") && isJsoOrPeer(typeOracle,
              method.getParameters()[0].getType()));
      boolean isSetter = (arguments == 2)
          && (methodName.startsWith("set") && isJsoOrPeer(typeOracle,
              method.getParameters()[0].getType()));
      boolean propertyAccessor = isIs || isGetter || isSetter;

      return !(hasBindingTag || methodHasBeanTag || (propertyAccessor
          && !hasImportTag && classHasBeanTag));
    }

    protected boolean isJsoOrPeer(TypeOracle oracle, JType type) {
      JClassType jsoType = oracle.findType(JavaScriptObject.class.getName()).isClass();
      return jsoType.isAssignableFrom(type.isClass())
          || (PeeringFragmentGenerator.findPeer(oracle, type) != null);
    }
  }

  /**
   * Creates constructor, import, and property Tasks.
   */
  private static class WrapperPolicy implements Policy {
    public Collection<JMethod> getOperableMethods(TypeOracle typeOracle,
        JClassType clazz) {
      return Arrays.asList(clazz.getOverridableMethods());
    }

    public boolean shouldBind(TreeLogger logger, TypeOracle typeOracle,
        JMethod method) throws UnableToCompleteException {
      return false;
    }

    public boolean shouldConstruct(TreeLogger logger, TypeOracle typeOracle,
        JMethod method) throws UnableToCompleteException {
      boolean methodConstructorTag = JSWrapperGenerator.hasTag(logger, method,
          Constructor.class) != null;
      boolean methodGlobalTag = JSWrapperGenerator.hasTag(logger, method,
          Global.class) != null;

      return methodConstructorTag || methodGlobalTag;
    }

    public boolean shouldExport(TreeLogger logger, TypeOracle typeOracle,
        JMethod method) throws UnableToCompleteException {
      return false;
    }

    public boolean shouldImplement(TreeLogger logger, TypeOracle typeOracle,
        JMethod method) throws UnableToCompleteException {
      JClassType enclosing = method.getEnclosingType().getErasedType();
      JClassType operableType = typeOracle.findType(getOperableClassName()).getErasedType();
      // JParameterizedType asParam = enclosing.isParameterized();
      // if (asParam != null) {
      // enclosing = asParam.get
      // }

      return method.isAbstract() && !enclosing.equals(operableType);
    }

    public boolean shouldImport(TreeLogger logger, TypeOracle typeOracle,
        JMethod method) throws UnableToCompleteException {
      JClassType enclosing = method.getEnclosingType();
      String methodName = method.getName();
      int arguments = method.getParameters().length;

      boolean hasImportTag = JSWrapperGenerator.hasTag(logger, method,
          Imported.class) != null;
      boolean methodHasBeanTag = JSWrapperGenerator.hasTag(logger, method,
          BeanProperties.class) != null;
      boolean classHasBeanTag = JSWrapperGenerator.hasTag(logger, enclosing,
          BeanProperties.class) != null;
      boolean isIs = (arguments == 0)
          && (methodName.startsWith("is"))
          && (JPrimitiveType.BOOLEAN.equals(method.getReturnType().isPrimitive()));
      boolean isGetter = (arguments == 0) && (methodName.startsWith("get"));
      boolean isSetter = (arguments == 1) && (methodName.startsWith("set"));
      boolean propertyAccessor = isIs || isGetter || isSetter;

      return !(methodHasBeanTag || (propertyAccessor && !hasImportTag && classHasBeanTag));
    }

    protected String getOperableClassName() {
      return JSWrapper.class.getName();
    }
  }

  public static final Policy FLYWEIGHT_POLICY = new FlyweightPolicy();

  public static final Policy WRAPPER_POLICY = new WrapperPolicy();

  public static final Policy EXPORTER_POLICY = new ExporterPolicy();

  /**
   * Populate propertyAccessors from an array of JMethods.
   *
   * @return A Map of Strings to Tasks.
   */
  public static Map<String, Task> extractMethods(TreeLogger logger,
      TypeOracle typeOracle, JClassType clazz, Policy policy)
      throws UnableToCompleteException {
    TreeLogger parentLogger = logger.branch(TreeLogger.DEBUG,
        "Extracting methods from " + clazz.getName(), null);

    // Value to return
    final Map<String, Task> propertyAccessors = new HashMap<String, Task>();

    // Iterate over all methods that the generated subclass could override
    for (JMethod m : policy.getOperableMethods(typeOracle, clazz)) {
      final String methodName = m.getName();
      logger = parentLogger.branch(TreeLogger.DEBUG, "Examining "
          + m.toString(), null);

      // Look for methods that are to be exported by the presence of
      // the gwt.exported annotation.
      if (policy.shouldExport(logger, typeOracle, m)) {
        Task task = getPropertyPair(propertyAccessors,
            m.getReadableDeclaration());
        task.exported = m;
        logger.log(TreeLogger.DEBUG, "Added as export", null);
        continue;
      }

      if (policy.shouldBind(logger, typeOracle, m)) {
        Task task = getPropertyPair(propertyAccessors,
            m.getReadableDeclaration());
        task.binding = m;
        logger.log(TreeLogger.DEBUG, "Added as binding", null);
        continue;
      }

      // Ignore concrete methods and those methods that are not declared in
      // a subtype of JSWrapper.
      if (!policy.shouldImplement(logger, typeOracle, m)) {
        logger.log(TreeLogger.DEBUG, "Ignoring method " + m.toString(), null);
        continue;
      }

      if (policy.shouldConstruct(logger, typeOracle, m)) {
        // getReadableDeclaration is used so that overloaded methods will
        // be stored with distinct keys.
        Task task = getPropertyPair(propertyAccessors,
            m.getReadableDeclaration());
        task.constructor = m;
        logger.log(TreeLogger.DEBUG, "Using constructor/global override", null);

        // Enable bypassing of name-determination logic with the presence of the
        // @gwt.imported annotation
      } else if (policy.shouldImport(logger, typeOracle, m)) {
        // getReadableDeclaration is used so that overloaded methods will
        // be stored with distinct keys.
        Task task = getPropertyPair(propertyAccessors,
            m.getReadableDeclaration());
        task.imported = m;
        logger.log(TreeLogger.DEBUG, "Using import override", null);

        // Look for setFoo()
      } else if (methodName.startsWith("set")) {
        String propertyName = getPropertyNameFromMethod(m);
        Task task = getPropertyPair(propertyAccessors, propertyName);
        task.setter = m;
        logger.log(TreeLogger.DEBUG, "Determined this is a setter", null);

        // Look for getFoo() or isFoo()
      } else if ((methodName.startsWith("get") || methodName.startsWith("is"))) {
        String propertyName = getPropertyNameFromMethod(m);
        Task task = getPropertyPair(propertyAccessors, propertyName);
        task.getter = m;
        logger.log(TreeLogger.DEBUG, "Determined this is a getter", null);

        // We could not make a decision on what should be done with the method.
      } else {
        logger.log(TreeLogger.ERROR, "Could not decide on implementation of "
            + m.getName(), null);
        throw new UnableToCompleteException();
      }
    }

    return propertyAccessors;
  }

  /**
   * Utility method to extract the bean-style property name from a method.
   *
   * @return The property name if the method's name looks like a bean property,
   *         otherwise the method's name.
   */
  protected static String getPropertyNameFromMethod(JMethod method) {
    String methodName = method.getName();

    if (methodName.startsWith("get")) {
      return methodName.substring(3);

    } else if (methodName.startsWith("set")) {
      return methodName.substring(3);

    } else if (methodName.startsWith("is")) {
      return methodName.substring(2);

    } else {
      return methodName;
    }
  }

  /**
   * Utility method to access a Map of String, Tasks.
   *
   * @param propertyAccessors The Map to operate on
   * @param property The name of the property
   * @return A Task in the given map; created if it does not exist
   */
  protected static Task getPropertyPair(Map<String, Task> propertyAccessors,
      String property) {
    if (propertyAccessors.containsKey(property)) {
      return propertyAccessors.get(property);
    } else {
      final Task pair = new Task();
      propertyAccessors.put(property, pair);
      return pair;
    }
  }

  /**
   * Utility class.
   */
  private TaskFactory() {
  }
}
TOP

Related Classes of com.google.gwt.jsio.rebind.TaskFactory$WrapperPolicy

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.