Package com.google.gwt.jsio.rebind

Source Code of com.google.gwt.jsio.rebind.Task

/*
* 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.jsio.client.FieldName;
import com.google.gwt.jsio.client.Global;
import com.google.gwt.jsio.client.JSWrapper;

import java.lang.reflect.Field;

/**
* Defines one or more methods to be implemented by JSWrapperGenerator.
*/
// XXX Refactor into type-specific subtypes
class Task {
  JMethod getter;
  JMethod setter;
  JMethod binding;
  JMethod imported;
  JMethod exported;
  JMethod constructor;

  /**
   * Determines the field name to be used by the methods associated with the
   * Task.
   *
   * @param logger the TreeLogger in use by the enclosing content
   * @return the JSO property/Wrapper field name to use when generating the
   *         methods in the Task
   * @throws UnableToCompleteException
   */
  public String getFieldName(TreeLogger logger)
      throws UnableToCompleteException {
    logger = logger.branch(TreeLogger.DEBUG, "Determining field name for Task",
        null);
    if (getter != null) {
      return extractFieldName(logger, getter, false);
    } else if (setter != null) {
      return extractFieldName(logger, setter, false);
    } else if (binding != null) {
      return extractFieldName(logger, binding, true);
    } else if (exported != null) {
      return extractFieldName(logger, exported, true);
    } else if (imported != null) {
      return extractFieldName(logger, imported, true);
    } else if (constructor != null) {
      return extractFieldName(logger, constructor, true);
    } else {
      logger.log(TreeLogger.ERROR, "Unable to determine field name", null);
      throw new UnableToCompleteException();
    }
  }

  /**
   * @return the name of the method that was defined in Java source. Retuns null
   *         on failure.
   */
  public String getJavaMethodName() {
    if (getter != null) {
      return getter.getName();
    } else if (setter != null) {
      return setter.getName();
    } else if (binding != null) {
      return binding.getName();
    } else if (exported != null) {
      return exported.getName();
    } else if (imported != null) {
      return imported.getName();
    } else if (constructor != null) {
      return constructor.getName();
    }
    return null;
  }

  /**
   * Validation check to ensure that at least one method is defined within the
   * Task.
   *
   * @return <code>true</code> if there is at least one method defined for the
   *         Task.
   */
  public boolean hasMethods() {
    return (getter != null) || (setter != null) || (imported != null)
        || (exported != null) || (constructor != null) || (binding != null);
  }

  /**
   * Validates the task within the specified context.
   *
   * @return <code>true</code> if an error is detected in the task
   */
  public boolean validate(JSWrapperGenerator generator,
      FragmentGeneratorContext context) {
    TreeLogger logger = context.parentLogger.branch(TreeLogger.DEBUG,
        "Validating task " + getJavaMethodName() + "().", null);
    JClassType jsoType = context.typeOracle.findType(JavaScriptObject.class.getName());

    if ((imported != null) && ((getter != null) || (setter != null))) {
      logger.log(TreeLogger.ERROR, "Imported functions may not be combined "
          + "with bean-style accessors", null);
      return true;
    }

    // If there are no methods attached to a task, we've encountered an
    // internal error.
    if (!hasMethods()) {
      logger.log(TreeLogger.ERROR, "No methods for task.", null);
      return true;
    }

    if (constructor != null) {
      JClassType returnType = constructor.getReturnType().isClassOrInterface();
      JClassType wrapperType = context.typeOracle.findType(JSWrapper.class.getName());

      if (!(jsoType.isAssignableFrom(returnType) || constructor.getEnclosingType().isAssignableFrom(
          returnType))) {
        logger.log(TreeLogger.ERROR,
            "Methods annotated with @gwt.constructor or @gwt.global must "
                + "return a JavaScriptObject or their enclosing class.", null);
        return true;
      }

      try {
        if (wrapperType.isAssignableFrom(returnType)
            && JSWrapperGenerator.hasTag(logger, constructor, Global.class) != null) {
          logger.log(TreeLogger.ERROR,
              "Cannot place @gwt.global annotation on JSWrapper methods."
                  + " Apply to the class or interface instead.", null);
          return true;
        }
      } catch (UnableToCompleteException e) {
        // Already logged the error in hasTag, just return here.
        return true;
      }
    }

    // Sanity check that we picked up the right setter
    if ((getter != null)
        && (setter != null)
        && !getter.getReturnType().equals(
            generator.getSetterParameter(setter).getType())) {
      logger.log(TreeLogger.ERROR, "Setter " + setter.getName()
          + " has different parameter type " + "from getter "
          + getter.getName(), null);
      return true;
    }

    if (exported != null) {
      if (context.readOnly) {
        // If the interface is read-only, we couldn't add the function linkage
        logger.log(TreeLogger.ERROR, "Cannot export function "
            + exported.getName() + " in read-only wrapper.", null);
        return true;

      } else if (!context.maintainIdentity && !exported.isStatic()) {
        // If we have no __gwtPeer object, only allow static methods
        logger.log(TreeLogger.ERROR, "Cannot export instance function "
            + exported.getName() + " in noIdentity wrapper.", null);
        return true;
      }
    }

    if (binding != null) {
      if (!JPrimitiveType.VOID.equals(binding.getReturnType().isPrimitive())) {
        logger.log(TreeLogger.ERROR,
            "Binding functions must have a void type.", null);
        return true;
      }

      JParameter[] params = binding.getParameters();
      if (params.length == 0
          || !jsoType.isAssignableFrom(params[0].getType().isClassOrInterface())) {
        logger.log(TreeLogger.ERROR, "The first parameter of a binding method "
            + "must be a JavaScriptObject", null);
        return true;
      }
    }

    return false;
  }

  /**
   * Determine the correct field name to use for a method.
   */
  protected String extractFieldName(TreeLogger logger, JMethod m,
      boolean imported) throws UnableToCompleteException {
    logger = logger.branch(TreeLogger.DEBUG, "Extracting field name", null);

    FieldName fieldNameAnnotation = JSWrapperGenerator.hasTag(logger, m,
        FieldName.class);

    if (fieldNameAnnotation == null) {
      // If the method is imported and there's no overriding annotation,
      // just return the methods original name. This ensures that native
      // JS methods that look like bean getter/setters don't get munged.
      if (imported) {
        return m.getName();
      }

      // If no gwt.fieldName is specified, see if there's a naming policy
      // defined on the enclosing class.
      JClassType enclosing = m.getEnclosingType();
      com.google.gwt.jsio.client.NamePolicy namePolicyAnnotation = JSWrapperGenerator.hasTag(
          logger, enclosing, com.google.gwt.jsio.client.NamePolicy.class);
      NamePolicy policy;

      // If there is no namePolicy or it's not of the desired form, default
      // to the JavaBean-style naming policy.
      if (namePolicyAnnotation == null) {
        policy = NamePolicy.BEAN;
        logger.log(TreeLogger.DEBUG, "No useful policy metadata for class",
            null);

      } else {
        // Use the provided name to access fields within NamePolicy
        String policyName = namePolicyAnnotation.value();
        try {
          Field f = NamePolicy.class.getDeclaredField(policyName.toUpperCase());
          policy = (NamePolicy) f.get(null);

        } catch (IllegalAccessException e) {
          logger.log(TreeLogger.ERROR, "Bad gwt.namePolicy " + policyName, e);
          throw new UnableToCompleteException();

        } catch (NoSuchFieldException e) {
          // This means that the value specified is not a field, but likely
          // a class name. Try instantiating one and seeing if it's a
          // subclass of NamePolicy.
          try {
            Class<? extends NamePolicy> clazz = Class.forName(policyName).asSubclass(
                NamePolicy.class);
            policy = clazz.newInstance();
          } catch (ClassCastException ee) {
            logger.log(TreeLogger.ERROR,
                "@gwt.namePolicy is not an implementation of NamePolicy", null);
            throw new UnableToCompleteException();
          } catch (ClassNotFoundException ee) {
            logger.log(TreeLogger.ERROR, "Bad gwt.namePolicy " + policyName, ee);
            throw new UnableToCompleteException();
          } catch (IllegalAccessException ee) {
            logger.log(TreeLogger.ERROR, "Bad gwt.namePolicy " + policyName, ee);
            throw new UnableToCompleteException();
          } catch (InstantiationException ee) {
            logger.log(TreeLogger.ERROR, "Bad gwt.namePolicy " + policyName, ee);
            throw new UnableToCompleteException();
          }
        }
      }

      // Execute the conversion
      String propertyName = TaskFactory.getPropertyNameFromMethod(m);
      return policy.convert(propertyName);

    } else {
      // Use the field name specified on the method
      logger.log(TreeLogger.DEBUG, "Overriding field name based on annotation",
          null);
      return fieldNameAnnotation.value();
    }
  }
}
TOP

Related Classes of com.google.gwt.jsio.rebind.Task

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.