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

com.google.gwt.dev.cfg.PropertyCombinations Maven / Gradle / Ivy

The newest version!
/*
 * 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.PropertyOracle;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.dev.util.CollapsedPropertyKey;
import com.google.gwt.thirdparty.guava.common.base.Objects;
import com.google.gwt.thirdparty.guava.common.collect.Lists;
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.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;

/**
 * Generates all possible permutations of properties in a module. Each
 * permutation consists of the list of active property values associated with
 * that permutation. That list of property values is represented as an array of
 * Strings corresponding to the list of properties returned by
 * {@link Properties#getBindingProperties()}.
 */
public class PropertyCombinations implements Iterable {

  /**
   * A bundle of the ordered names of binding and configuration properties and a single set value
   * for each that together uniquely identify a particular permutation.
   */
  public static class PermutationDescription {

    private List bindingPropertyNames = Lists.newArrayList();
    private List configurationPropertyNames = Lists.newArrayList();
    private List propertyValues = Lists.newArrayList();

    @Override
    public int hashCode() {
      return Objects.hashCode(bindingPropertyNames, configurationPropertyNames, propertyValues);
    }

    @Override
    public boolean equals(Object obj) {
      if (obj instanceof PermutationDescription) {
        PermutationDescription that = (PermutationDescription) obj;
        return Objects.equal(this.bindingPropertyNames, that.bindingPropertyNames)
            && Objects.equal(this.configurationPropertyNames, that.configurationPropertyNames)
            && Objects.equal(this.propertyValues, that.propertyValues);
      }
      return false;
    }

    @Override
    public String toString() {
      return String.format(
          "PermutationDescription [bindingPropertyNames=%s, configurationPropertyNames=%s, "
          + "propertyValues=%s]", bindingPropertyNames, configurationPropertyNames, propertyValues);
    }
  }

  /**
   * Returns the list of all permutations. This method must return results in a
   * consistently sorted order over multiple invocations.
   */
  private static List allPermutationsOf(Properties properties,
      Set activeLinkerNames) {
    BindingProperty[] bindingProperties = getOrderedPropertiesOf(properties);

    List permutations = new ArrayList();
    if (bindingProperties.length > 0) {
      permute(bindingProperties, activeLinkerNames, null, 0, permutations);
    } else {
      permutations.add(new String[0]);
    }
    return permutations;
  }

  private static BindingProperty[] getOrderedPropertiesOf(Properties properties) {
    /*
     * We delete items from this set, but want to retain the original order as
     * much as possible.
     */
    Set bindingProperties = Sets.newLinkedHashSet(
        properties.getBindingProperties());

    // Accumulates the order in which the properties should be evaluated
    Map evaluationOrder = Maps.newLinkedHashMap();

    /*
     * Insert a property after all of the properties that it depends upon have
     * been inserted.
     */
    while (!bindingProperties.isEmpty()) {
      boolean changed = false;

      for (Iterator it = bindingProperties.iterator(); it.hasNext();) {
        BindingProperty prop = it.next();

        Set deps = prop.getRequiredProperties();
        if (evaluationOrder.keySet().containsAll(deps)) {
          it.remove();
          evaluationOrder.put(prop.getName(), prop);
          changed = true;
        }
      }

      if (!changed) {
        throw new IllegalStateException(
            "Cycle detected within remaining property dependencies "
                + bindingProperties.toString());
      }
    }

    return evaluationOrder.values().toArray(
        new BindingProperty[evaluationOrder.size()]);
  }

  private static void permute(BindingProperty[] properties,
      Set activeLinkerNames, String[] soFar, int whichProp,
      List permutations) {
    int lastProp = properties.length - 1;

    BindingProperty prop = properties[whichProp];

    // Find the last-one-wins Condition
    Condition winner = null;
    if (prop.getConditionalValues().size() == 1) {
      winner = prop.getRootCondition();
    } else {
      BindingProperty[] answerable = new BindingProperty[soFar.length];
      System.arraycopy(properties, 0, answerable, 0, soFar.length);
      PropertyOracle propertyOracle = new BindingProperties(answerable, soFar,
              ConfigurationProperties.EMPTY).toPropertyOracle();

      for (Condition cond : prop.getConditionalValues().keySet()) {
        try {
          if (cond.isTrue(TreeLogger.NULL, new DeferredBindingQuery(
              propertyOracle, activeLinkerNames))) {
            winner = cond;
          }
        } catch (UnableToCompleteException e) {
          throw new IllegalStateException("Should never get here for simple properties", e);
        }
      }
    }

    assert winner != null;

    String[] options = prop.getGeneratedValues(winner);
    for (int i = 0; i < options.length; i++) {
      String knownValue = options[i];

      String[] nextStep = new String[whichProp + 1];
      if (whichProp > 0) {
        System.arraycopy(soFar, 0, nextStep, 0, soFar.length);
      }
      nextStep[whichProp] = knownValue;

      if (whichProp < lastProp) {
        permute(properties, activeLinkerNames, nextStep, whichProp + 1,
            permutations);
      } else {
        // Finished this permutation.
        permutations.add(nextStep);
      }
    }
  }

  private final Properties properties;
  private final List values;

  public PropertyCombinations(Properties properties,
      Set activeLinkerNames) {
    this.properties = properties;
    this.values = allPermutationsOf(properties, activeLinkerNames);
  }

  /**
   * Copy constructor that allows the list of property values to be reset.
   */
  public PropertyCombinations(PropertyCombinations allPermutations,
      List values) {
    this.properties = allPermutations.properties;
    this.values = values;
  }

  /**
   * Return a list of PropertyPermutations that represent the hard permutations
   * that result from collapsing the soft properties in the
   * PropertyPermutation's Properties object.
   */
  public List collapseProperties() {
    // Collate property values in this map
    SortedMap> map = Maps.newTreeMap();

    BindingProperty[] propertyKeys = getOrderedProperties();
    // Loop over all possible property value permutations
    for (Iterator it = iterator(); it.hasNext();) {
      String[] propertyValues = it.next();
      assert propertyValues.length == propertyKeys.length;

      BindingProperties bindingProperties =
          new BindingProperties(propertyKeys, propertyValues, ConfigurationProperties.EMPTY);
      CollapsedPropertyKey key = new CollapsedPropertyKey(bindingProperties);

      List list = map.get(key);
      if (list == null) {
        list = Lists.newArrayList();
        map.put(key, list);
      }
      list.add(propertyValues);
    }

    // Return the collated values
    List toReturn = new ArrayList(
        map.size());
    for (List list : map.values()) {
      toReturn.add(new PropertyCombinations(this, list));
    }

    return toReturn;
  }

  /**
   * Returns the properties used to generate permutations.
   * (Parallel to {@link #getOrderedPropertyValues}.)
   */
  public BindingProperty[] getOrderedProperties() {
    return getOrderedPropertiesOf(properties);
  }

  /**
   * Returns the value of each property used to generate the given permutation.
   * (Parallel to {@link #getOrderedProperties()}.)
   */
  public String[] getOrderedPropertyValues(int permutation) {
    return values.get(permutation);
  }

  /**
   * Returns a unique description for the given permutation.
   */
  public PermutationDescription getPermutationDescription(int permutationId) {
    PermutationDescription permutationDescription = new PermutationDescription();
    for (BindingProperty bindingProperty : properties.getBindingProperties()) {
      permutationDescription.bindingPropertyNames.add(bindingProperty.getName());
    }
    for (ConfigurationProperty configurationProperty : properties.getConfigurationProperties()) {
      permutationDescription.configurationPropertyNames.add(configurationProperty.getName());
    }
    permutationDescription.propertyValues.addAll(Arrays.asList(this.values.get(permutationId)));
    return permutationDescription;
  }

  /**
   * Enumerates each permutation as an array of strings such that the index of
   * each string in the array corresponds to the property at the same index in
   * the array returned from {@link #getOrderedProperties()}.
   */
  @Override
  public Iterator iterator() {
    return values.iterator();
  }

  public int size() {
    return values.size();
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy