/* * Copyright 2010 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.core.ext.linker.impl; import com.google.gwt.core.ext.LinkerContext; import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.UnableToCompleteException; import com.google.gwt.core.ext.linker.ArtifactSet; import com.google.gwt.core.ext.linker.SelectionProperty; import com.google.gwt.dev.util.StringKey; import com.google.gwt.dev.util.collect.HashSet; import com.google.gwt.dev.util.collect.Lists; import java.util.List; import java.util.Map; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; /** * A utility class to help linkers generate a list of permutation mappings and * and then either output them to javascript code which selects the correct * permutation, or to a file which can be parsed by server side code. */ public class PermutationsUtil { /** * This represents the combination of a unique content hash (i.e. the MD5 of * the bytes to be written into the cache.html file) and a soft permutation * id. */ protected static class PermutationId extends StringKey { private final int softPermutationId; private final String strongName; public PermutationId(String strongName, int softPermutationId) { super(strongName + ":" + softPermutationId); this.strongName = strongName; this.softPermutationId = softPermutationId; } public int getSoftPermutationId() { return softPermutationId; } public String getStrongName() { return strongName; } } /** * This maps each unique permutation to the property settings for that * compilation. A single compilation can have multiple property settings if * the compiles for those settings yielded the exact same compiled output. */ protected SortedMap<PermutationId, List<Map<String, String>>> propMapsByPermutation = new TreeMap<PermutationId, List<Map<String, String>>>(); /** * Uses the internal map to insert JS to select a permutation into the * selection script. * * @param selectionScript * @param logger * @param context * @return the modified selectionScript buffer * @throws UnableToCompleteException */ public StringBuffer addPermutationsJs(StringBuffer selectionScript, TreeLogger logger, LinkerContext context) throws UnableToCompleteException { int startPos; PropertiesUtil.addPropertiesJs(selectionScript, logger, context); // Possibly add permutations startPos = selectionScript.indexOf("// __PERMUTATIONS_END__"); if (startPos != -1) { StringBuffer text = new StringBuffer(); if (propMapsByPermutation.size() == 0) { // Hosted mode link. text.append("alert(\"GWT module '" + context.getModuleName() + "' may need to be (re)compiled\");"); text.append("return;"); } else if (propMapsByPermutation.size() == 1) { // Just one distinct compilation; no need to evaluate properties text.append("strongName = '" + propMapsByPermutation.keySet().iterator().next().getStrongName() + "';"); } else { Set<String> propertiesUsed = new HashSet<String>(); for (PermutationId permutationId : propMapsByPermutation.keySet()) { for (Map<String, String> propertyMap : propMapsByPermutation.get(permutationId)) { // unflatten([v1, v2, v3], 'strongName' + ':softPermId'); // The soft perm ID is concatenated to improve string interning text.append("unflattenKeylistIntoAnswers(["); boolean needsComma = false; for (SelectionProperty p : context.getProperties()) { if (p.tryGetValue() != null) { continue; } else if (p.isDerived()) { continue; } if (needsComma) { text.append(","); } else { needsComma = true; } text.append("'" + propertyMap.get(p.getName()) + "'"); propertiesUsed.add(p.getName()); } text.append("], '").append(permutationId.getStrongName()).append( "'"); /* * For compatibility with older linkers, skip the soft permutation * if it's 0 */ if (permutationId.getSoftPermutationId() != 0) { text.append(" + ':").append(permutationId.getSoftPermutationId()).append( "'"); } text.append(");\n"); } } // strongName = answers[compute('p1')][compute('p2')]; text.append("strongName = answers["); boolean needsIndexMarkers = false; for (SelectionProperty p : context.getProperties()) { if (!propertiesUsed.contains(p.getName())) { continue; } if (needsIndexMarkers) { text.append("]["); } else { needsIndexMarkers = true; } text.append("computePropValue('" + p.getName() + "')"); } text.append("];"); } selectionScript.insert(startPos, text); } return selectionScript; } public SortedMap<PermutationId, List<Map<String, String>>> getPermutationsMap() { return propMapsByPermutation; } /** * Find all instances of {@link SelectionInformation} and add them to the * internal map of selection information. */ public void setupPermutationsMap(ArtifactSet artifacts) { propMapsByPermutation = new TreeMap<PermutationId, List<Map<String, String>>>(); for (SelectionInformation selInfo : artifacts.find(SelectionInformation.class)) { TreeMap<String, String> entries = selInfo.getPropMap(); PermutationId permutationId = new PermutationId(selInfo.getStrongName(), selInfo.getSoftPermutationId()); if (!propMapsByPermutation.containsKey(permutationId)) { propMapsByPermutation.put(permutationId, Lists.<Map<String, String>> create(entries)); } else { propMapsByPermutation.put(permutationId, Lists.add( propMapsByPermutation.get(permutationId), entries)); } } } }