Package com.google.gwt.dev.cfg

Source Code of com.google.gwt.dev.cfg.RuleGenerateWith

/*
* 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.dev.cfg;

import com.google.gwt.core.ext.BadPropertyValueException;
import com.google.gwt.core.ext.Generator;
import com.google.gwt.core.ext.Generator.RunsLocal;
import com.google.gwt.core.ext.GeneratorContext;
import com.google.gwt.core.ext.RebindResult;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.dev.javac.StandardGeneratorContext;
import com.google.gwt.dev.javac.typemodel.LibraryTypeOracle.UnsupportedTypeOracleAccess;
import com.google.gwt.dev.javac.typemodel.TypeOracle;
import com.google.gwt.dev.jjs.InternalCompilerException;
import com.google.gwt.thirdparty.guava.common.annotations.VisibleForTesting;
import com.google.gwt.thirdparty.guava.common.base.Objects;
import com.google.gwt.thirdparty.guava.common.collect.ImmutableSet;
import com.google.gwt.thirdparty.guava.common.collect.Maps;
import com.google.gwt.thirdparty.guava.common.collect.Sets;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

/**
* A rule to replace the type being rebound with a class whose name is determined by a generator
* class. Generators usually generate new classes during the deferred binding process, but it is not
* required.
*/
public class RuleGenerateWith extends Rule {

  public static final Set<String> ALL_PROPERTIES = ImmutableSet.of(RunsLocal.ALL);

  /**
   * Returns a Set of the names of properties that will be accessed by the given Generator.
   */
  public static Set<String> getAccessedPropertyNames(Class<? extends Generator> generatorClass) {
    RunsLocal runsLocal = generatorClass.getAnnotation(RunsLocal.class);
    return runsLocal == null ? ALL_PROPERTIES : ImmutableSet.copyOf(runsLocal.requiresProperties());
  }

  private final Set<String> accessedPropertyNames;
  private Generator generator;
  private final Class<? extends Generator> generatorClass;
  private Map<String, String> rebindProperties;
  private String rebindRequestTypeName;
  private String rebindResultTypeName;

  public RuleGenerateWith(Class<? extends Generator> generatorClass) {
    this.generatorClass = generatorClass;
    this.accessedPropertyNames = getAccessedPropertyNames(generatorClass);
  }

  /**
   * Returns whether the output of the Generator being managed by this rule is modified by or
   * whether the rules embedded condition is judging any of the properties whose names have been
   * passed.<br />
   *
   * Makes it possible for external callers to watch the changing property environment and only
   * trigger Generators within Rules whose output might have changed.
   */
  public boolean caresAboutProperties(Set<String> propertyNames) {
    // If this generator cares about all properties.
    if (accessedPropertyNames.equals(ALL_PROPERTIES)) {
      // Then if some properties were supplied, it cares about them.
      return !propertyNames.isEmpty();
    }

    // Otherwise an explicit list of cared about properties was supplied. Return whether any of the
    // supplied properties is cared about.
    return !Sets.intersection(accessedPropertyNames, propertyNames).isEmpty();
  }

  /**
   * Returns whether the output of the Generator being managed by this rule depends on access to the
   * global set of types to be able to run accurately.
   */
  public boolean contentDependsOnTypes() {
    return generatorClass.getAnnotation(RunsLocal.class) == null;
  }

  @Override
  public boolean equals(Object obj) {
    if (this == obj) {
      return true;
    }
    if (obj == null) {
      return false;
    }
    if (!(obj instanceof RuleGenerateWith)) {
      return false;
    }
    RuleGenerateWith other = (RuleGenerateWith) obj;
    if (generatorClass == null) {
      if (other.generatorClass != null) {
        return false;
      }
    } else if (!generatorClass.equals(other.generatorClass)) {
      return false;
    }
    return true;
  }

  /**
   * Generate all possible Generator output for the wrapped Generator in combination with the scope
   * of Properties and values known about by the passed GeneratorContext. Additionally generate and
   * gather runtime rebind rules for all corresponding pairs of property values and Generator
   * output.
   */
  public void generate(TreeLogger logger, Properties moduleProperties, GeneratorContext context,
      String typeName) throws UnableToCompleteException {
    Map<Map<String, String>, String> resultTypeNamesByProperties =
        computeResultTypeNamesByProperties(
            logger, moduleProperties, (StandardGeneratorContext) context, typeName);

    rebindRequestTypeName = typeName;
    for (Entry<Map<String, String>, String> entry : resultTypeNamesByProperties.entrySet()) {
      rebindProperties = entry.getKey();
      rebindResultTypeName = entry.getValue();
      runtimeRebindRuleGenerator.generate(
          generateCreateInstanceExpression(), generateMatchesExpression());
    }
  }

  /**
   * Returns the name of the class of Generator being managed here.
   */
  public String getName() {
    return generatorClass.getName();
  }

  @Override
  public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + ((generatorClass == null) ? 0 : generatorClass.hashCode());
    return result;
  }

  @Override
  public RebindResult realize(TreeLogger logger,
      StandardGeneratorContext context, String typeName)
      throws UnableToCompleteException {
    return context.runGeneratorIncrementally(logger, generatorClass, typeName);
  }

  public boolean relevantPropertiesAreFinal(Properties currentProperties,
      Properties finalProperties) {
    if (accessedPropertyNames.equals(ALL_PROPERTIES)) {
      // Generator must be assumed to depend on all properties.
      for (BindingProperty finalProperty : finalProperties.getBindingProperties()) {
        Property currentProperty = currentProperties.find(finalProperty.getName());
        if (!Objects.equal(finalProperty, currentProperty)) {
          return false;
        }
      }
      for (ConfigurationProperty finalProperty : finalProperties.getConfigurationProperties()) {
        Property currentProperty = currentProperties.find(finalProperty.getName());
        if (!Objects.equal(finalProperty, currentProperty)) {
          return false;
        }
      }
      return true;
    }

    // Generator defines a limited set of properties it cares about.
    for (String accessedPropertyName : accessedPropertyNames) {
      Property finalProperty = finalProperties.find(accessedPropertyName);
      Property currentProperty = currentProperties.find(accessedPropertyName);
      if (!Objects.equal(finalProperty, currentProperty)) {
        return false;
      }
    }
    return true;
  }

  @Override
  public String toString() {
    return "<generate-with class='" + generatorClass.getName() + "'/>";
  }

  @Override
  protected String generateCreateInstanceExpression() {
    return "return @" + rebindResultTypeName + "::new()();";
  }

  @Override
  protected String generateMatchesExpression() {
    ConditionAll rootCondition = new ConditionAll();
    Conditions conditions = rootCondition.getConditions();

    conditions.add(new ConditionWhenTypeIs(rebindRequestTypeName));
    for (Entry<String, String> propertyEntry : rebindProperties.entrySet()) {
      String propertyName = propertyEntry.getKey();
      String propertyValue = propertyEntry.getValue();
      conditions.add(new ConditionWhenPropertyIs(propertyName, propertyValue));
    }

    return "return " + rootCondition.toSource() + ";";
  }

  @VisibleForTesting
  protected Generator getGenerator() {
    if (generator == null) {
      try {
        generator = generatorClass.newInstance();
      } catch (InstantiationException e) {
        throw new InternalCompilerException(
            "Could not instantiate generator " + generatorClass.getSimpleName());
      } catch (IllegalAccessException e) {
        throw new InternalCompilerException(
            "Could not instantiate generator " + generatorClass.getSimpleName());
      }
    }
    return generator;
  }

  /**
   * Repeatedly run the contained Generator with the maximum range of combinations of Properties and
   * values. Log corresponding pairs of property values and Generator output.
   */
  private Map<Map<String, String>, String> computeResultTypeNamesByProperties(TreeLogger logger,
      Properties moduleProperties, StandardGeneratorContext context, String typeName)
      throws UnableToCompleteException {
    Map<Map<String, String>, String> resultTypeNamesByProperties = Maps.newHashMap();
    DynamicPropertyOracle dynamicPropertyOracle = new DynamicPropertyOracle(moduleProperties);

    // Maybe prime the pump.
    if (!accessedPropertyNames.equals(ALL_PROPERTIES)) {
      for (String accessedPropertyName : accessedPropertyNames) {
        try {
          dynamicPropertyOracle.getSelectionProperty(logger, accessedPropertyName);
        } catch (BadPropertyValueException e) {
          // ignore
        }
      }
    }
    boolean needsAllTypesIfRun = contentDependsOnTypes() && context.isGlobalCompile();
    TypeOracle typeModelTypeOracle =
        (com.google.gwt.dev.javac.typemodel.TypeOracle) context.getTypeOracle();

    context.setPropertyOracle(dynamicPropertyOracle);

    context.setCurrentGenerator(generatorClass);

    do {
      resultTypeNamesByProperties.clear();
      Properties accessedProperties = new Properties();

      List<BindingProperty> accessedPropertiesList =
          new ArrayList<BindingProperty>(dynamicPropertyOracle.getAccessedProperties());
      for (BindingProperty bindingProperty : accessedPropertiesList) {
        accessedProperties.addBindingProperty(bindingProperty);
      }
      PropertyPermutations permutationsOfAccessedProperties =
          new PropertyPermutations(accessedProperties, Sets.<String> newHashSet());

      for (int permutationId = 0; permutationId < permutationsOfAccessedProperties.size();
          permutationId++) {
        String[] orderedPropertyValues =
            permutationsOfAccessedProperties.getOrderedPropertyValues(permutationId);
        BindingProperty[] orderedProperties =
            permutationsOfAccessedProperties.getOrderedProperties();

        dynamicPropertyOracle.reset();
        for (int propertyIndex = 0; propertyIndex < orderedPropertyValues.length; propertyIndex++) {
          dynamicPropertyOracle.prescribePropertyValue(orderedProperties[propertyIndex].getName(),
              orderedPropertyValues[propertyIndex]);
        }

        if (!isApplicable(logger, context, typeName)) {
          continue;
        }
        if (needsAllTypesIfRun) {
          typeModelTypeOracle.ensureAllLoaded();
        }
        String resultTypeName;
        try {
          resultTypeName = getGenerator().generate(logger, context, typeName);
        } catch (UnsupportedTypeOracleAccess e) {
          logger.log(TreeLogger.ERROR, String.format(
              "TypeOracle error when running generator '%s' in an incremental compile: %s",
              getName(), e.getMessage()));
          throw new UnableToCompleteException();
        }
        if (resultTypeName != null) {
          // Some generators only run to create resource artifacts and don't actually participate
          // in the requestType->resultType rebind process.
          resultTypeNamesByProperties.put(dynamicPropertyOracle.getPrescribedPropertyValuesByName(),
              resultTypeName);
        }

        if (dynamicPropertyOracle.haveAccessedPropertiesChanged()) {
          break;
        }
      }
    } while (dynamicPropertyOracle.haveAccessedPropertiesChanged());

    return resultTypeNamesByProperties;
  }
}
TOP

Related Classes of com.google.gwt.dev.cfg.RuleGenerateWith

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.