blob: 48dfef11693c1b306815934f4cb2d8a14f725a5e [file] [log] [blame]
/*
* 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 java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
/**
* 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 PropertyPermutations implements Iterable<String[]> {
/**
* Returns the list of all permutations. This method must return results in a
* consistently sorted order over multiple invocations.
*/
private static List<String[]> allPermutationsOf(Properties properties,
Set<String> activeLinkerNames) {
BindingProperty[] bindingProperties = getOrderedPropertiesOf(properties);
List<String[]> permutations = new ArrayList<String[]>();
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<BindingProperty> bindingProps = new LinkedHashSet<BindingProperty>(
properties.getBindingProperties());
// Accumulates the order in which the properties should be evaluated
Map<String, BindingProperty> evaluationOrder = new LinkedHashMap<String, BindingProperty>(
bindingProps.size());
/*
* Insert a property after all of the properties that it depends upon have
* been inserted.
*/
while (!bindingProps.isEmpty()) {
boolean changed = false;
for (Iterator<BindingProperty> it = bindingProps.iterator(); it.hasNext();) {
BindingProperty prop = it.next();
Set<String> 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 "
+ bindingProps.toString());
}
}
return evaluationOrder.values().toArray(
new BindingProperty[evaluationOrder.size()]);
}
private static void permute(BindingProperty[] properties,
Set<String> activeLinkerNames, String[] soFar, int whichProp,
List<String[]> 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 StaticPropertyOracle(answerable,
soFar, new ConfigurationProperty[0]);
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.getAllowedValues(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<String[]> values;
public PropertyPermutations(Properties properties,
Set<String> activeLinkerNames) {
this.properties = properties;
this.values = allPermutationsOf(properties, activeLinkerNames);
}
public PropertyPermutations(PropertyPermutations allPermutations,
int firstPerm, int numPerms) {
this.properties = allPermutations.properties;
values = allPermutations.values.subList(firstPerm, firstPerm + numPerms);
}
/**
* Copy constructor that allows the list of property values to be reset.
*/
public PropertyPermutations(PropertyPermutations allPermutations,
List<String[]> 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<PropertyPermutations> collapseProperties() {
// Collate property values in this map
SortedMap<CollapsedPropertyKey, List<String[]>> map = new TreeMap<CollapsedPropertyKey, List<String[]>>();
// Loop over all possible property value permutations
for (Iterator<String[]> it = iterator(); it.hasNext();) {
String[] propertyValues = it.next();
assert propertyValues.length == getOrderedProperties().length;
StaticPropertyOracle oracle = new StaticPropertyOracle(
getOrderedProperties(), propertyValues, new ConfigurationProperty[0]);
CollapsedPropertyKey key = new CollapsedPropertyKey(
oracle);
List<String[]> list = map.get(key);
if (list == null) {
list = new ArrayList<String[]>();
map.put(key, list);
}
list.add(propertyValues);
}
// Return the collated values
List<PropertyPermutations> toReturn = new ArrayList<PropertyPermutations>(
map.size());
for (List<String[]> list : map.values()) {
toReturn.add(new PropertyPermutations(this, list));
}
return toReturn;
}
public BindingProperty[] getOrderedProperties() {
return getOrderedPropertiesOf(properties);
}
public String[] getOrderedPropertyValues(int permutation) {
return values.get(permutation);
}
/**
* 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()}.
*/
public Iterator<String[]> iterator() {
return values.iterator();
}
public int size() {
return values.size();
}
}