| /* |
| * 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.shell; |
| |
| import com.google.gwt.core.ext.BadPropertyValueException; |
| import com.google.gwt.core.ext.DefaultConfigurationProperty; |
| import com.google.gwt.core.ext.DefaultSelectionProperty; |
| import com.google.gwt.core.ext.PropertyOracle; |
| import com.google.gwt.core.ext.SelectionProperty; |
| import com.google.gwt.core.ext.TreeLogger; |
| import com.google.gwt.core.ext.UnableToCompleteException; |
| import com.google.gwt.dev.cfg.BindingProperty; |
| import com.google.gwt.dev.cfg.Condition; |
| import com.google.gwt.dev.cfg.ConfigurationProperty; |
| import com.google.gwt.dev.cfg.DeferredBindingQuery; |
| import com.google.gwt.dev.cfg.Properties; |
| import com.google.gwt.dev.cfg.Property; |
| |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.SortedSet; |
| import java.util.TreeSet; |
| |
| /** |
| * Implements a {@link PropertyOracle} in terms of a module space, which makes |
| * it possible to execute property providers. |
| */ |
| public class ModuleSpacePropertyOracle implements PropertyOracle { |
| |
| private final Set<String> activeLinkerNames; |
| |
| private final Map<String, String> prevAnswers = new HashMap<String, String>(); |
| |
| private final Properties props; |
| |
| private final ModuleSpace space; |
| |
| /** |
| * Create a property oracle that computes its properties from a module. |
| */ |
| public ModuleSpacePropertyOracle(Properties props, |
| Set<String> activeLinkerNames, ModuleSpace space) { |
| this.space = space; |
| this.activeLinkerNames = activeLinkerNames; |
| this.props = props; |
| } |
| |
| public com.google.gwt.core.ext.ConfigurationProperty getConfigurationProperty( |
| String propertyName) throws BadPropertyValueException { |
| Property prop = getProperty(propertyName); |
| if (prop instanceof ConfigurationProperty) { |
| final ConfigurationProperty cprop = (ConfigurationProperty) prop; |
| final String name = cprop.getName(); |
| final List<String> values = cprop.getValues(); |
| return new DefaultConfigurationProperty(name, values); |
| } else { |
| throw new BadPropertyValueException(propertyName); |
| } |
| } |
| |
| /** |
| * Executes JavaScript to find the property value. |
| */ |
| @Deprecated |
| public String getPropertyValue(TreeLogger logger, String propertyName) |
| throws BadPropertyValueException { |
| Property prop = getProperty(propertyName); |
| |
| // Check if this property has already been queried for; if so, return |
| // the same answer. This is necessary to match Production Mode behavior |
| // since property providers are only called once. We cache even values that |
| // cause exceptions to be thrown to make sure we are consistent even |
| // in throwing exceptions for the same property. |
| if (prevAnswers.containsKey(propertyName)) { |
| return prevAnswers.get(propertyName); |
| } else { |
| String value; |
| if (prop instanceof ConfigurationProperty) { |
| value = ((ConfigurationProperty) prop).getValue(); |
| } else if (prop instanceof BindingProperty) { |
| value = computePropertyValue(logger, propertyName, |
| (BindingProperty) prop); |
| } else { |
| throw new BadPropertyValueException(propertyName); |
| } |
| prevAnswers.put(propertyName, value); |
| return value; |
| } |
| } |
| |
| /** |
| * Returns the list of possible values for a property. |
| */ |
| @Deprecated |
| public String[] getPropertyValueSet(TreeLogger logger, String propertyName) |
| throws BadPropertyValueException { |
| Property prop = getProperty(propertyName); |
| if (prop instanceof BindingProperty) { |
| return ((BindingProperty) prop).getDefinedValues(); |
| } |
| throw new BadPropertyValueException(propertyName); |
| } |
| |
| public SelectionProperty getSelectionProperty(TreeLogger logger, |
| String propertyName) throws BadPropertyValueException { |
| Property prop = getProperty(propertyName); |
| if (prop instanceof BindingProperty) { |
| final BindingProperty cprop = (BindingProperty) prop; |
| final String name = cprop.getName(); |
| final String value; |
| if (prevAnswers.containsKey(propertyName)) { |
| value = prevAnswers.get(propertyName); |
| } else { |
| value = computePropertyValue(logger, propertyName, cprop); |
| prevAnswers.put(propertyName, value); |
| } |
| final String fallback = cprop.getFallback(); |
| final SortedSet<String> possibleValues = new TreeSet<String>(); |
| for (String v : cprop.getDefinedValues()) { |
| possibleValues.add(v); |
| } |
| return new DefaultSelectionProperty(value, fallback, name, possibleValues, |
| cprop.getFallbackValuesMap()); |
| } else { |
| throw new BadPropertyValueException(propertyName); |
| } |
| } |
| |
| private Condition computeActiveCondition(TreeLogger logger, |
| BindingProperty prop) throws BadPropertyValueException { |
| // Last-one-wins |
| Condition winner = null; |
| for (Condition cond : prop.getConditionalValues().keySet()) { |
| try { |
| if (cond.isTrue(logger, new DeferredBindingQuery(this, |
| activeLinkerNames))) { |
| winner = cond; |
| } |
| } catch (UnableToCompleteException e) { |
| BadPropertyValueException t = new BadPropertyValueException( |
| prop.getName()); |
| t.initCause(e); |
| throw t; |
| } |
| } |
| assert winner != null : "No active Condition for " + prop.getName(); |
| return winner; |
| } |
| |
| /** |
| * Returns the value of the specified property. |
| * |
| * @throws BadPropertyValueException if the property value could not be |
| * computed, or if the returned result is not a legal value for this |
| * property. |
| */ |
| private String computePropertyValue(TreeLogger logger, String propertyName, |
| BindingProperty prop) throws BadPropertyValueException { |
| |
| String value = prop.getConstrainedValue(); |
| if (value != null) { |
| // If there is only one legal value, use that. |
| return value; |
| } |
| |
| Condition winner = computeActiveCondition(logger, prop); |
| String[] values = prop.getAllowedValues(winner); |
| if (values.length == 1) { |
| return values[0]; |
| } |
| |
| // Invokes the script function. |
| // |
| try { |
| // Invoke the property provider function in JavaScript. |
| // |
| value = (String) space.invokeNativeObject("__gwt_getProperty", null, |
| new Class[] {String.class}, new Object[] {prop.getName()}); |
| } catch (Throwable e) { |
| // Treat as an unknown value. |
| // |
| String msg = "Error while executing the JavaScript provider for property '" |
| + propertyName + "'"; |
| logger.log(TreeLogger.ERROR, msg, e); |
| throw new BadPropertyValueException(propertyName, "<failed to compute>"); |
| } |
| |
| // value may be null if the provider returned an unknown property value. |
| if (Arrays.asList(values).contains(value)) { |
| return value; |
| } else { |
| // Bad value due to the provider returning an unknown value. |
| // The fact that the provider returned an invalid value will also |
| // have been reported to the JS bad property value handler function. |
| throw new BadPropertyValueException(propertyName, value); |
| } |
| } |
| |
| /** |
| * Returns a Property given its name, handling error conditions. |
| * |
| * @throws BadPropertyValueException |
| */ |
| private Property getProperty(String propertyName) |
| throws BadPropertyValueException { |
| if (propertyName == null) { |
| throw new NullPointerException("propertyName"); |
| } |
| |
| Property prop = props.find(propertyName); |
| if (prop == null) { |
| // Don't know this property; that's not good. |
| // |
| throw new BadPropertyValueException(propertyName); |
| } |
| return prop; |
| } |
| } |