| /* |
| * 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(); |
| } |
| } |