All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.gwt.rpc.rebind.RpcProxyCreator Maven / Gradle / Ivy

There is a newer version: 2.10.0
Show newest version
/*
 * Copyright 2009 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.rpc.rebind;

import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.impl.ArtificialRescue;
import com.google.gwt.core.client.impl.Impl;
import com.google.gwt.core.client.impl.ArtificialRescue.Rescue;
import com.google.gwt.core.ext.GeneratorContext;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JArrayType;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JEnumType;
import com.google.gwt.core.ext.typeinfo.JField;
import com.google.gwt.core.ext.typeinfo.JMethod;
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.dev.util.collect.Lists;
import com.google.gwt.rpc.client.impl.CommandToStringWriter;
import com.google.gwt.rpc.client.impl.RpcServiceProxy;
import com.google.gwt.rpc.client.impl.TypeOverrides;
import com.google.gwt.rpc.linker.RpcDataArtifact;
import com.google.gwt.user.client.rpc.SerializationStreamWriter;
import com.google.gwt.user.client.rpc.impl.RemoteServiceProxy;
import com.google.gwt.user.linker.rpc.RpcLogArtifact;
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;
import com.google.gwt.user.rebind.rpc.CustomFieldSerializerValidator;
import com.google.gwt.user.rebind.rpc.ProxyCreator;
import com.google.gwt.user.rebind.rpc.SerializableTypeOracle;
import com.google.gwt.user.rebind.rpc.SerializableTypeOracleBuilder;
import com.google.gwt.user.rebind.rpc.SerializationUtils;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Generates async proxy implementations using the RPC system.
 */
public class RpcProxyCreator extends ProxyCreator {
  private String typeOverrideName;

  public RpcProxyCreator(JClassType type) {
    super(type);
  }

  @Override
  protected String computeTypeNameExpression(JType paramType) {
    if (paramType.isClass() != null) {
      return "GWT.isScript() ? Impl.getNameOf(\"@"
          + paramType.getQualifiedSourceName() + "\") : \""
          + SerializationUtils.getRpcTypeName(paramType) + "\"";
    } else {
      /*
       * Consider the case of service methods that have interface parameters;
       * these types don't necessarily exist in the client code, so we want to
       * encode these type names in a way that can always be distinguished from
       * obfuscated type names.
       */
      return "\" " + SerializationUtils.getRpcTypeName(paramType)
          + "\"";
    }
  }

  @Override
  protected void generateProxyContructor(SourceWriter srcWriter) {
    srcWriter.println("public " + getProxySimpleName() + "() {");
    srcWriter.indent();
    srcWriter.println("super(GWT.getModuleBaseURL(),");
    srcWriter.indent();
    srcWriter.println(getRemoteServiceRelativePath() + ",");
    srcWriter.println("OVERRIDES);");
    srcWriter.outdent();
    srcWriter.outdent();
    srcWriter.println("}");
  }

  /**
   * Generate any fields required by the proxy.
   */
  @Override
  protected void generateProxyFields(SourceWriter srcWriter,
      SerializableTypeOracle serializableTypeOracle,
      String serializationPolicyStrongName, String remoteServiceInterfaceName) {
    // Initialize a field with binary name of the remote service interface
    srcWriter.println("private static final String REMOTE_SERVICE_INTERFACE_NAME = "
        + "\"" + remoteServiceInterfaceName + "\";");
    srcWriter.println("private static final " + TypeOverrides.class.getName()
        + " OVERRIDES = GWT.isScript() ? " + typeOverrideName
        + ".create() : null;");
    srcWriter.println();
  }

  @Override
  protected void generateStreamWriterOverride(SourceWriter srcWriter) {
    // Intentional no-op. Called if elideTypeNames is on, which is ignored
  }

  @Override
  protected void generateTypeHandlers(TreeLogger logger, GeneratorContext ctx,
      SerializableTypeOracle serializationSto,
      SerializableTypeOracle deserializationSto)
      throws UnableToCompleteException {
    String simpleName = serviceIntf.getSimpleSourceName()
        + "_TypeOverridesFactory";
    PrintWriter out = ctx.tryCreate(logger, serviceIntf.getPackage().getName(),
        simpleName);
    if (out == null) {
      return;
    }

    TypeOracle typeOracle = ctx.getTypeOracle();
    JClassType objectType = typeOracle.getJavaLangObject();
    Set classLiterals = new LinkedHashSet();
    Map serializerMethods = new LinkedHashMap();
    Map> fields = new LinkedHashMap>();

    StringBuilder sb = writeArtificialRescues(typeOracle, serializationSto,
        deserializationSto);

    ClassSourceFileComposerFactory composerFactory = new ClassSourceFileComposerFactory(
        serviceIntf.getPackage().getName(), simpleName);
    composerFactory.addImport(ArtificialRescue.class.getCanonicalName());
    composerFactory.addImport(GWT.class.getCanonicalName());
    composerFactory.addImport(Impl.class.getCanonicalName());
    composerFactory.addImport(Rescue.class.getCanonicalName());
    composerFactory.addImport(TypeOverrides.class.getCanonicalName());
    composerFactory.addImport(TypeOverrides.SerializeFunction.class.getCanonicalName());

    composerFactory.addAnnotationDeclaration(sb.toString());
    SourceWriter sw = composerFactory.createSourceWriter(ctx, out);

    sw.println("public static TypeOverrides create() {");
    sw.indent();
    sw.println("TypeOverrides toReturn = TypeOverrides.create();");
    for (JType type : serializationSto.getSerializableTypes()) {
      JClassType classType = type.isClass();
      if (classType == null) {
        continue;
      }

      /*
       * Figure out which fields should be serialized and if there's a CFS. This
       * is done by crawling the supertype chain until we hit Object or a type
       * with a CFS.
       */
      boolean allFieldsAreSerializable = true;
      List fieldRefs = new ArrayList();
      JMethod serializerMethod = null;
      do {
        JClassType customSerializer = SerializableTypeOracleBuilder.findCustomFieldSerializer(
            typeOracle, classType);
        serializerMethod = customSerializer == null ? null
            : CustomFieldSerializerValidator.getSerializationMethod(
                customSerializer, type.isClass());

        if (serializerMethod != null) {
          // Don't include any fields in the type
          break;
        }

        JField[] serializableFields = SerializationUtils.getSerializableFields(
            typeOracle, classType);
        allFieldsAreSerializable &= serializableFields.length == classType.getFields().length;
        for (JField field : serializableFields) {
          fieldRefs.add("@" + field.getEnclosingType().getQualifiedSourceName()
              + "::" + field.getName());
        }
        classType = classType.getSuperclass();
      } while (classType != objectType);

      if (allFieldsAreSerializable && serializerMethod == null) {
        // We can just inspect the object at runtime; best for code size
        continue;
      }

      if (serializerMethod != null || !fieldRefs.isEmpty()) {
        classLiterals.add(type);

        /*
         * toReturn.set(class_foo_Bar().getName(), serializer_foo_Bar(),
         * fields_foo_Bar());
         */
        String mangledTypeName = type.getQualifiedSourceName().replace('.', '_');
        sw.println("toReturn.set(class_" + mangledTypeName + "().getName()");
        if (serializerMethod == null) {
        } else {
          serializerMethods.put(type, serializerMethod);
          sw.indentln(",serializer_" + mangledTypeName + "()");
        }
        if (fieldRefs.isEmpty()) {
          sw.indentln(");");
        } else {
          fields.put(type, fieldRefs);
          sw.indentln(",fields_" + mangledTypeName + "());");
        }
      }
    }

    sw.println("return toReturn;");
    sw.outdent();
    sw.println("}");

    for (JType classLiteral : classLiterals) {
      sw.println("public static native Class class_"
          + classLiteral.getQualifiedSourceName().replace('.', '_') + "() /*-{");
      sw.indentln("return @" + classLiteral.getQualifiedSourceName()
          + "::class;");
      sw.println("}-*/;");
      sw.println();
    }

    for (Map.Entry entry : serializerMethods.entrySet()) {
      sw.println("public static native "
          + TypeOverrides.SerializeFunction.class.getSimpleName()
          + " serializer_"
          + entry.getKey().getQualifiedSourceName().replace('.', '_')
          + "() /*-{");
      sw.indentln("return " + entry.getValue().getJsniSignature() + ";");
      sw.println("}-*/;");
      sw.println();
    }

    for (Map.Entry> entry : fields.entrySet()) {
      sw.println("public static String[] fields_"
          + entry.getKey().getQualifiedSourceName().replace('.', '_') + "() {");
      sw.print("return new String[] {");
      for (String fieldRef : entry.getValue()) {
        sw.print("Impl.getNameOf(\"" + fieldRef + "\"),");
      }
      sw.println("};");
      sw.println("}");
      sw.println();
    }

    sw.commit(logger);

    typeOverrideName = composerFactory.getCreatedClassName();
  }

  @Override
  protected Class getProxySupertype() {
    return RpcServiceProxy.class;
  }

  @Override
  protected Class getStreamWriterClass() {
    return CommandToStringWriter.class;
  }

  @Override
  protected String writeSerializationPolicyFile(TreeLogger logger,
      GeneratorContext ctx, SerializableTypeOracle serializationSto,
      SerializableTypeOracle deserializationSto)
      throws UnableToCompleteException {

    RpcDataArtifact data = new RpcDataArtifact(
        serviceIntf.getQualifiedSourceName());

    for (JType type : deserializationSto.getSerializableTypes()) {
      if (!(type instanceof JClassType)) {
        continue;
      }

      JField[] serializableFields = SerializationUtils.getSerializableFields(
          ctx.getTypeOracle(), (JClassType) type);

      List names = Lists.create();
      for (int i = 0, j = serializableFields.length; i < j; i++) {
        names = Lists.add(names, serializableFields[i].getName());
      }

      data.setFields(SerializationUtils.getRpcTypeName(type), names);
    }

    ctx.commitArtifact(logger, data);

    return RpcLogArtifact.UNSPECIFIED_STRONGNAME;
  }

  private StringBuilder writeArtificialRescues(TypeOracle typeOracle,
      SerializableTypeOracle serializationSto,
      SerializableTypeOracle deserializationSto) {
    Set serializableTypes = new LinkedHashSet();
    Collections.addAll(serializableTypes,
        serializationSto.getSerializableTypes());
    Collections.addAll(serializableTypes,
        deserializationSto.getSerializableTypes());
    for (JMethod m : serviceIntf.getOverridableMethods()) {
      // Pick up any primitive return types, which get sent boxed
      JPrimitiveType mustBox = m.getReturnType().isPrimitive();
      if (mustBox != null) {
        serializableTypes.add(m.getReturnType());
      }
    }

    StringBuilder sb = new StringBuilder("@ArtificialRescue({");
    for (JType serializableType : serializableTypes) {

      JArrayType serializableArray = serializableType.isArray();
      JClassType serializableClass = serializableType.isClass();
      JPrimitiveType serializablePrimitive = serializableType.isPrimitive();
      if (serializableArray != null) {
        sb.append("\n@Rescue(className = \"");
        sb.append(serializableArray.getQualifiedSourceName());
        sb.append("\",\n instantiable = true),");
      } else if (serializableClass != null) {
        writeSingleRescue(typeOracle, deserializationSto, sb, serializableClass);
      } else if (serializablePrimitive != null) {
        JClassType boxedClass = typeOracle.findType(serializablePrimitive.getQualifiedBoxedSourceName());
        assert boxedClass != null : "No boxed version of "
            + serializablePrimitive.getQualifiedSourceName();
        writeSingleRescue(typeOracle, deserializationSto, sb, boxedClass);
      }
    }
    sb.append("})");
    return sb;
  }

  /**
   * Writes the rescue of a serializable type and its custom serialization
   * logic.
   */
  private void writeSingleRescue(TypeOracle typeOracle,
      SerializableTypeOracle deserializationOracle, StringBuilder sb,
      JClassType serializableClass) {
    boolean shouldDeserialize = deserializationOracle.isSerializable(serializableClass);

    // Pull the two custom serialization methods
    JClassType customSerializer;
    JMethod deserialize = null;
    JMethod instantiate = null;

    // An automatically-serializable subclass of a manually serialized class
    boolean hybridSerialization = false;

    {
      JClassType search = serializableClass;
      do {
        customSerializer = SerializableTypeOracleBuilder.findCustomFieldSerializer(
            typeOracle, search);

        if (customSerializer != null) {
          instantiate = CustomFieldSerializerValidator.getInstantiationMethod(
              customSerializer, search);

          deserialize = CustomFieldSerializerValidator.getDeserializationMethod(
              customSerializer, search);

          hybridSerialization = search != serializableClass;
          break;
        }

        search = search.getSuperclass();
      } while (search != null);
    }

    // The fields that should be preserved from being pruned
    JField[] serializableFields;
    JEnumType enumType = serializableClass.isEnum();
    if (enumType != null) {
      serializableFields = enumType.getFields();
    } else {
      serializableFields = SerializationUtils.getSerializableFields(typeOracle,
          serializableClass);
    }

    /*
     * We need to rescue the constructor if there is no instantiate method and
     * there is a custom deserialize method.
     */
    boolean rescueConstructor = instantiate == null && deserialize != null;

    /*
     * There may be either no custom serializer or a custom serializer that
     * doesn't define the instantiate method.
     */
    if (shouldDeserialize || rescueConstructor
        || (customSerializer == null && serializableFields.length > 0)) {

      /*
       * @Rescue(className="package.Foo$Inner", instantiable=true, fields={..},
       * methods={..}),
       */
      sb.append("\n@Rescue(className = \"").append(
          serializableClass.getQualifiedSourceName()).append("\"");

      sb.append(",\n instantiable = ").append(shouldDeserialize);
      sb.append(",\n fields = {");
      if (customSerializer == null || hybridSerialization) {
        for (JField field : serializableFields) {
          sb.append("\"").append(field.getName()).append("\",");
        }
      }
      sb.append("},\n methods = {");
      if (rescueConstructor) {
        sb.append("\"").append(serializableClass.getName().replace('.', '$')).append(
            "()\"");
      }
      sb.append("}),");
    }

    // Rescue the custom serialization logic if any exists
    if (customSerializer != null) {
      sb.append("\n@Rescue(className = \"").append(
          customSerializer.getQualifiedSourceName()).append("\",\n methods = {");
      if (instantiate != null) {
        String jsniSignature = instantiate.getJsniSignature();
        sb.append("\"").append(
            jsniSignature.substring(jsniSignature.lastIndexOf(':') + 1)).append(
            "\",");
      }
      if (deserialize != null) {
        String jsniSignature = deserialize.getJsniSignature();
        sb.append("\"").append(
            jsniSignature.substring(jsniSignature.lastIndexOf(':') + 1)).append(
            "\",");
      }
      sb.append("}),");
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy