Package com.sencha.gxt.data.rebind

Source Code of com.sencha.gxt.data.rebind.ValueProviderCreator

/**
* Sencha GXT 3.1.0-beta - Sencha for GWT
* Copyright(c) 2007-2014, Sencha, Inc.
* licensing@sencha.com
*
* http://www.sencha.com/products/gxt/license/
*/
package com.sencha.gxt.data.rebind;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import com.google.gwt.core.ext.GeneratorContext;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.TreeLogger.Type;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JField;
import com.google.gwt.core.ext.typeinfo.JGenericType;
import com.google.gwt.core.ext.typeinfo.JMethod;
import com.google.gwt.core.ext.typeinfo.JType;
import com.google.gwt.dev.util.Name;
import com.google.gwt.editor.client.Editor.Path;
import com.google.gwt.editor.rebind.model.ModelUtils;
import com.google.gwt.thirdparty.guava.common.base.Joiner;
import com.google.gwt.user.rebind.SourceWriter;
import com.sencha.gxt.core.client.ValueProvider;
import com.sencha.gxt.core.rebind.AbstractCreator;

public class ValueProviderCreator extends AbstractCreator {
  public enum RequiredReadability {
    BOTH, SET, GET, NEITHER
  }

  protected static String cap(String str) {
    return str.substring(0, 1).toUpperCase() + str.substring(1);
  }

  private static List<String> generatePath(Path annotation, String name) {
    if (annotation == null) {
      return Collections.singletonList(name);
    }
    return Arrays.asList(annotation.value().split("\\."));
  }

  private final JClassType supertypeToImplement;

  protected final List<String> path;

  private final JGenericType valueProviderInterface = getContext().getTypeOracle().findType(
      Name.getSourceNameForClass(ValueProvider.class)).isGenericType();

  private RequiredReadability readability = RequiredReadability.NEITHER;

  public ValueProviderCreator(GeneratorContext ctx, TreeLogger l, JMethod valueProviderMethodDecl) {
    super(ctx, l);
    this.supertypeToImplement = valueProviderMethodDecl.getReturnType().isClassOrInterface();
    this.path = generatePath(valueProviderMethodDecl.getAnnotation(Path.class), valueProviderMethodDecl.getName());
  }

  public ValueProviderCreator(GeneratorContext ctx, TreeLogger l, List<String> path, JClassType baseType,
      JClassType valueType) {
    super(ctx, l);
    this.path = path;
    supertypeToImplement = getContext().getTypeOracle().getParameterizedType(valueProviderInterface,
        new JClassType[] {baseType, valueType});
  }

  @Override
  public String getInstanceExpression() {
    return getPackageName() + "." + getSimpleName() + ".INSTANCE";
  }

  public void setReadability(RequiredReadability readability) {
    this.readability = readability;
  }

  public RequiredReadability getReadability() {
    return readability;
  }

  @Override
  protected void create(SourceWriter sw) throws UnableToCompleteException {
    sw.println("public static final %1$s INSTANCE = new %1$s();", getSimpleName());
    // @Override
    sw.println("public %1$s getValue(%2$s object) {", getValueTypeName(), getObjectTypeName());
    appendGetterBody(sw, "object");
    sw.println("}");

    // @Override
    sw.println("public void setValue(%1$s object, %2$s value) {", getObjectTypeName(), getValueTypeName());
    appendSetterBody(sw, "object", "value");
    sw.println("}");

    // @Override
    sw.println("public String getPath() {");
    StringBuilder sb = new StringBuilder();
    boolean first = true;
    for (String p : path) {
      if (!first) {
        sb.append(".");
      }
      sb.append(p);

      first = false;
    }
    sw.indentln("return \"%1$s\";", sb.toString());
    sw.println("}");
  }

  protected void appendGetterBody(SourceWriter sw, String objectName)
      throws UnableToCompleteException {
    String getter = getGetterExpression(objectName);
    if (getter == null) {
      if (readability == RequiredReadability.NEITHER || readability == RequiredReadability.SET) {
        // getter is not required, but log it if someone tries to call it
        getLogger().log(
            Type.DEBUG,
            "Getter could not be found (and apparently not needed), writting a log message to indicate that this is probably an error.");
        sw.indentln("com.google.gwt.core.client.GWT.log(\"Getter was called on " + supertypeToImplement.getName()
            + ", but no getter exists.\", new RuntimeException());");
        sw.indentln("return null;");
      } else {
        getLogger().log(Type.ERROR, "No getter can be found, unable to proceed");
        throw new UnableToCompleteException();
      }
    } else {
      sw.indentln("return %1$s;", getter);
    }
  }

  protected void appendSetterBody(SourceWriter sw, String objectName, String valueName) throws UnableToCompleteException {
    String setter = getSetterExpression(objectName, valueName);
    if (setter == null) {
      if (readability == RequiredReadability.NEITHER || readability == RequiredReadability.GET) {
        // setter is not required, but log it if someone tries to call it
        getLogger().log(
            Type.DEBUG,
            "Setter could not be found (and apparently not needed), writing a log message to indicate that this is probably an error.");
        sw.indentln("com.google.gwt.core.client.GWT.log(\"Setter was called on " + supertypeToImplement.getName()
            + ", but no setter exists.\", new RuntimeException());");
      } else {
        getLogger().log(Type.ERROR, "No setter can be found, unable to proceed");
        throw new UnableToCompleteException();
      }
    } else {
      sw.indentln("%1$s;", setter);
    }
  }

  protected String getGetterExpression(String objectName) throws UnableToCompleteException {
    StringBuilder sb = new StringBuilder(objectName);
    try {
      getGetterHelper(path, sb);
    } catch (NoSuchMethodException ex) {
      return null;
    }
    return sb.toString();
  }

  protected JMethod getMethod(JClassType type, String methodName) {
    JMethod[] methods = type.getInheritableMethods();
    for (JMethod m : methods) {
      if (m.getName().equals(methodName)) {
        return m;
      }
    }
    return null;
  }

  /**
   * Gets the type of the Model this object should provide data for.
   *
   * @return the parameterized type of the model
   */
  protected JClassType getObjectType() {
    JClassType[] params = ModelUtils.findParameterizationOf(valueProviderInterface, supertypeToImplement);

    if (params[0].isTypeParameter() != null) {
      return params[0].isTypeParameter().getBaseType();
    } else {
      return params[0];
    }
  }

  /**
   * Evaluated when used so this can be subclassed, and used for generating
   * similar types
   *
   * @return the parameterized qualified name
   */
  protected final String getObjectTypeName() {
    return getObjectType().getParameterizedQualifiedSourceName();
  }

  @Override
  protected String getPackageName() {
    String pak = getObjectType().getPackage().getName();
    if (!pak.startsWith("java.") && !pak.startsWith("javax.")) {
      return pak;
    }
    //if this does start with java, we can just drop this anywhere, since java isn't likely
    //to not be on the classpath...
    return "com.sencha.gxt.core.client." + pak;
  }

  @Override
  protected String getSimpleName() {
    return getObjectType().getName().replace('.', '_') + "_" + Joiner.on("_").join(path) + "_ValueProviderImpl";
  }

  @Override
  protected JClassType getSupertype() {
    return supertypeToImplement;
  }

  /**
   * Helper method for building up chained getter expressions
   *
   * @param path the path to follow to find the next getter
   * @param sb stringbuild to append the methods to
   * @return the type returned of the last method
   * @throws NoSuchMethodException if a method cannot be found
   */
  private JClassType getGetterHelper(List<String> path, StringBuilder sb) throws NoSuchMethodException, UnableToCompleteException {
    JClassType type = getObjectType();
    for (String p : path) {
      if (type == null) {
        getLogger().log(Type.WARN, "Trying to find a method in a non-class, non-interface type.");
        throw new UnableToCompleteException();
      }

      // Pick a method to use
      // Try getter first
      JMethod method = getMethod(type, p);
      if (method == null) {
        method = getMethod(type, "get" + cap(p));
      }
      if (method == null) {
        method = getMethod(type, "is" + cap(p));
      }
      if (method == null) {
        method = getMethod(type, "has" + cap(p));
      }

      // Fall back to field
      JField field = getField(type, p);

      if (method != null) {
        sb.append(".").append(method.getName()).append("()");
        type = method.getReturnType().isClassOrInterface();
      } else if (field != null) {
        sb.append(".").append(p);
        type = field.getType().isClassOrInterface();
      } else {
        getLogger().log(Type.WARN, "Method get" + cap(p) + " could not be found");
        throw new NoSuchMethodException();
      }

    }
    return type;
  }

  private JField getField(JClassType type, String p) {
    for (JClassType superType : type.getFlattenedSupertypeHierarchy()) {
      JField field = superType.findField(p);
      if (field != null && field.isPublic()){
        return field;
      }
    }
    return null;
  }

  private String getSetterExpression(String objectName, String valueName) throws UnableToCompleteException {
    StringBuilder sb = new StringBuilder(objectName);

    // find the getter from the start of the path
    List<String> getterPath = this.path.subList(0, this.path.size() - 1);
    String lastPathElement = path.get(path.size() - 1);

    JClassType type = null;
    try {
      type = getGetterHelper(getterPath, sb);
    } catch (NoSuchMethodException ex) {
      return null;
    }
    if (type == null) {
      getLogger().log(Type.WARN, "Trying to find setter method on a non-class, non-interface type.");
      return null;
    }

    String methodName = "set" + cap(lastPathElement);

    JMethod method = getMethod(type, methodName);
    JField field = getField(type, lastPathElement);
    if (method != null && typesMatch(getValueType(), method.getParameterTypes()[0])) {
      sb.append(".").append(methodName).append("(").append(valueName).append(")");
    } else if (field != null && typesMatch(getValueType(), field.getType())) {
      sb.append(".").append(lastPathElement).append(" = ").append(valueName);
    } else {
      getLogger().log(Type.DEBUG, "Method " + methodName + " of type " + getValueType() + " could not be found.");
      return null;
    }

    return sb.toString();
  }

  private boolean typesMatch(JClassType a, JType b) {
    if (b.isPrimitive() != null) {
      return a.getQualifiedSourceName().equals(b.isPrimitive().getQualifiedBoxedSourceName());
    } else {
      assert b.isClassOrInterface() != null;

      return b.isClassOrInterface().isAssignableTo(a);
    }
  }

  private JClassType getValueType() {
    JClassType[] params = ModelUtils.findParameterizationOf(valueProviderInterface, supertypeToImplement);

    return params[1];
  }

  private String getValueTypeName() {
    return getValueType().getParameterizedQualifiedSourceName();
  }
}
TOP

Related Classes of com.sencha.gxt.data.rebind.ValueProviderCreator

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.