Package com.google.gwt.search.jsio.rebind

Source Code of com.google.gwt.search.jsio.rebind.JSFlyweightWrapperGenerator

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

import com.google.gwt.search.jsio.client.Binding;
import com.google.gwt.search.jsio.client.Constructor;
import com.google.gwt.search.jsio.client.Global;
import com.google.gwt.search.jsio.client.impl.JSONWrapperUtil;
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.JField;
import com.google.gwt.core.ext.typeinfo.JMethod;
import com.google.gwt.core.ext.typeinfo.JParameter;
import com.google.gwt.core.ext.typeinfo.JParameterizedType;
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.user.rebind.SourceWriter;

import java.util.Map;

/**
* Generates a flyweight-style JSIO interface.
*/
public class JSFlyweightWrapperGenerator extends JSWrapperGenerator {
  /**
   * The name of a static method that can be implemented in a class so that it
   * can receive a peer object. It must accept a JSO.
   */
  public static final String CREATE_PEER = "createPeer";

  @Override
  protected int getImportOffset() {
    return 1;
  }

  @Override
  protected TaskFactory.Policy getPolicy() {
    return TaskFactory.FLYWEIGHT_POLICY;
  }

  @Override
  protected JParameter getSetterParameter(JMethod setter) {
    return setter.getParameters()[1];
  }

  /**
   * Sets the objRef field on a FragmentGeneratorContext to refer to the correct
   * JavaScriptObject.
   */
  protected void setObjRef(FragmentGeneratorContext context, JMethod method)
      throws UnableToCompleteException {
    JParameter[] paramList = method.getParameters();
    if (paramList.length == 0) {
      context.parentLogger.branch(TreeLogger.ERROR,
          "No parameters specified for method " + method.getName()
              + "().  (First parameter must be a JavaScriptObject.)", null);
      throw new UnableToCompleteException();
    }
    JParameter param = method.getParameters()[0];
    JClassType paramType = param.getType().isClassOrInterface();
    JField f;

    if (context.typeOracle.findType(JavaScriptObject.class.getName()).equals(
        paramType)) {
      context.objRef = param.getName();

    } else if ((f = PeeringFragmentGenerator.findPeer(context.typeOracle,
        paramType)) != null) {
      context.objRef = param.getName() + ".@"
          + f.getEnclosingType().getQualifiedSourceName() + "::" + f.getName();

    } else {
      context.parentLogger.branch(TreeLogger.ERROR,
          "Invalid first parameter type for flyweight imported function. "
              + "It is not a JavaScriptObject and it lacks a jsoPeer field.",
          null);
      throw new UnableToCompleteException();
    }
  }

  protected void writeBinding(FragmentGeneratorContext context, JMethod binding)
      throws UnableToCompleteException {
    TreeLogger logger = context.parentLogger.branch(TreeLogger.DEBUG,
        "Writing binding function", null);
    context = new FragmentGeneratorContext(context);
    context.parentLogger = logger;

    SourceWriter sw = context.sw;
    TypeOracle typeOracle = context.typeOracle;
    Binding bindingAnnotation = JSWrapperGenerator.hasTag(logger, binding,
        Binding.class);

    // Write the java half to add assertions to the code. These will be elided
    // in web mode, and the method become a pure delegation, allowing it to
    // be removed completely
    sw.print("public void ");
    sw.print(binding.getName());
    sw.print("(");
    JParameter[] params = binding.getParameters();

    context.parameterName = "jso";
    sw.print(params[0].getType().getQualifiedSourceName());
    sw.print(" ");
    sw.print(context.parameterName);

    JClassType bindingType = null;
    if (params.length == 2) {
      // Infer the binding type from the second parameter of the binding
      // method.
      bindingType = params[1].getType().isClassOrInterface();
      context.objRef = "obj";

      sw.print(", ");
      sw.print(bindingType.getQualifiedSourceName());
      sw.print(" ");
      sw.print(context.objRef);
    } else if (bindingAnnotation != null
        && bindingAnnotation.value().length() > 0) {
      // Use the binding type specified in the the gwt.binding annotation.
      bindingType = typeOracle.findType(bindingAnnotation.value());
      if (bindingType == null) {
        logger.log(TreeLogger.ERROR, "Could not resolve binding type "
            + bindingType, null);
        throw new UnableToCompleteException();
      }
    }

    sw.println(") {");
    sw.indent();

    for (Task t : context.tasks) {
      if (t.imported != null) {
        String fieldName = t.getFieldName(logger);
        sw.print("assert JSONWrapperUtil.hasField(");
        sw.print(context.parameterName);
        sw.print(", \"");
        sw.print(fieldName);
        sw.print("\") : \"Backing JSO missing imported function ");
        sw.print(fieldName);
        sw.println("\";");
      }
    }

    sw.print(binding.getName());
    sw.print("Native (");
    sw.print(context.parameterName);
    if (params.length == 2) {
      sw.print(",");
      sw.print(context.objRef);
    }
    sw.println(");");
    sw.outdent();
    sw.println("}");

    // Write the native half to perform the actual binding operations
    sw.print("public native void ");
    sw.print(binding.getName());
    sw.print("Native (");
    sw.print(params[0].getType().getQualifiedSourceName());
    sw.print(" ");
    sw.print(context.parameterName);
    if (params.length == 2) {
      sw.print(", ");
      sw.print(bindingType.getQualifiedSourceName());
      sw.print(" ");
      sw.print(context.objRef);
    }
    sw.println(") /*-{");
    sw.indent();

    // A binding should have been declared void
    context.returnType = JPrimitiveType.VOID;

    if (context.maintainIdentity && params.length == 2) {
      // XXX link the Java object to the JSO?

      // Verify that the incoming object doesn't already have a wrapper object.
      // If there is a backreference, throw an exception.
      sw.print("if (");
      sw.print(context.parameterName);
      sw.print(".");
      sw.print(BACKREF);
      sw.println(") {");
      sw.indent();
//      sw.println("@com.google.gwt.jsio.client.impl.JSONWrapperUtil::throwMultipleWrapperException()();");
      sw.println("@" + JSONWrapperUtil.class.getName() + "::throwMultipleWrapperException()();");
      sw.outdent();
      sw.println("}");

      // Assign the backreference from the JSO object to the delegate
      sw.print(context.parameterName);
      sw.print(".");
      sw.print(BACKREF);
      sw.print(" = ");
      sw.print(context.objRef);
      sw.println(";");
    }

    writeEmptyFieldInitializers(context);

    if (bindingType != null) {
      // Extract the exported methods
      context.tasks = TaskFactory.extractMethods(logger, typeOracle,
          bindingType, TaskFactory.EXPORTER_POLICY).values();
      writeMethodBindings(context);
    } else {
      logger.log(TreeLogger.DEBUG,
          "Not binding methods to any particular type.", null);
    }

    sw.outdent();
    sw.println("}-*/;");
  }

  /**
   * Writes common boilerplate code for all implementations.
   */
  @Override
  protected void writeBoilerplate(final TreeLogger logger,
      final FragmentGeneratorContext context) throws UnableToCompleteException {
  }

  @Override
  protected void writeConstructor(FragmentGeneratorContext context,
      JMethod constructor) throws UnableToCompleteException {

    TreeLogger logger = context.parentLogger.branch(TreeLogger.DEBUG,
        "Writing constructor " + constructor.getName(), null);
    SourceWriter sw = context.sw;

    JParameter[] parameters = constructor.getParameters();
    if (parameters == null) {
      parameters = new JParameter[0];
    }

    // Method declaration
    sw.print("public native ");
    sw.print(constructor.getReturnType().getQualifiedSourceName());
    sw.print(" ");
    sw.print(constructor.getName());
    sw.print("(");
    for (int i = 0; i < parameters.length; i++) {
      JType returnType = parameters[i].getType();
      JParameterizedType pType = returnType.isParameterized();

      if (pType != null) {
        sw.print(pType.getRawType().getQualifiedSourceName());
      } else {
        sw.print(returnType.getQualifiedSourceName());
      }

      sw.print(" ");
      sw.print(parameters[i].getName());

      if (i < parameters.length - 1) {
        sw.print(", ");
      }
    }
    sw.print(")");
    sw.println(" /*-{");
    sw.indent();

    JType returnType = constructor.getReturnType();

    FragmentGeneratorContext subContext = new FragmentGeneratorContext(context);
    subContext.returnType = returnType;
    subContext.parameterName = "jsReturn";
    subContext.objRef = "jsReturn";

    sw.print("var ");
    sw.print(subContext.objRef);
    sw.print(" = ");

    Constructor constructorAnnotation = hasTag(logger, constructor,
        Constructor.class);
    Global globalAnnotation = hasTag(logger, constructor, Global.class);
    if (constructorAnnotation != null) {
      // If the imported method is acting as an invocation of a JavaScript
      // constructor, use the new Foo() syntax, otherwise treat is an an
      // invocation on a field on the underlying JSO.
      sw.print("new ");
      sw.print(constructorAnnotation.value());

      // Write the invocation's parameter list
      sw.print("(");
      for (int i = 0; i < parameters.length; i++) {
        // Create a sub-context to generate the wrap/unwrap logic
        JType subType = parameters[i].getType();
        FragmentGeneratorContext subParams = new FragmentGeneratorContext(
            context);
        subParams.returnType = subType;
        subParams.parameterName = parameters[i].getName();

        FragmentGenerator fragmentGenerator = context.fragmentGeneratorOracle.findFragmentGenerator(
            logger, context.typeOracle, subType);
        if (fragmentGenerator == null) {
          logger.log(TreeLogger.ERROR, "No fragment generator for "
              + returnType.getQualifiedSourceName(), null);
          throw new UnableToCompleteException();
        }

        fragmentGenerator.toJS(subParams);

        if (i < parameters.length - 1) {
          sw.print(", ");
        }
      }
      sw.print(")");

    } else if (globalAnnotation != null) {
      sw.print(globalAnnotation.value());

    } else {
      logger.log(TreeLogger.ERROR,
          "Writing a constructor, but no constructor-appropriate annotations",
          null);
      throw new UnableToCompleteException();
    }
    sw.println(";");

    writeEmptyFieldInitializers(subContext);

    sw.print("return ");
    sw.print(subContext.objRef);
    sw.println(";");

    sw.outdent();
    sw.println("}-*/;");
  }

  /**
   * This is a no-op in the flyweight style.
   */
  @Override
  protected void writeEmptyFieldInitializerMethod(final TreeLogger logger,
      final Map<String, Task> propertyAccessors,
      final FragmentGeneratorContext context) throws UnableToCompleteException {
  }

  @Override
  protected void writeGetter(FragmentGeneratorContext context, JMethod getter)
      throws UnableToCompleteException {

    context = new FragmentGeneratorContext(context);
    setObjRef(context, getter);
    context.parameterName = context.objRef + "." + context.fieldName;

    super.writeGetter(context, getter);
  }

  @Override
  protected void writeImported(FragmentGeneratorContext context,
      JMethod imported) throws UnableToCompleteException {

    context = new FragmentGeneratorContext(context);

    // It's invalid to have an imported method without a leading JSO object
    if (imported.getParameters().length > 0) {
      setObjRef(context, imported);
    } else {
      context.parentLogger.branch(TreeLogger.ERROR,
          "Imported methods in a flyweight interface must have a leading "
              + "JavaScriptObject parameter", null);
      throw new UnableToCompleteException();
    }

    super.writeImported(context, imported);
  }

  @Override
  protected void writeSetter(FragmentGeneratorContext context, JMethod setter)
      throws UnableToCompleteException {

    context = new FragmentGeneratorContext(context);
    setObjRef(context, setter);

    super.writeSetter(context, setter);
  }

  @Override
  protected void writeSingleTask(FragmentGeneratorContext context, Task task)
      throws UnableToCompleteException {
    if (task.binding != null) {
      writeBinding(context, task.binding);
    } else {
      super.writeSingleTask(context, task);
    }
  }
}
TOP

Related Classes of com.google.gwt.search.jsio.rebind.JSFlyweightWrapperGenerator

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.