package com.googlecode.mgwt.linker.linker; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.SortedSet; import java.util.TreeMap; import com.google.gwt.core.ext.LinkerContext; import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.TreeLogger.Type; import com.google.gwt.core.ext.UnableToCompleteException; import com.google.gwt.core.ext.linker.AbstractLinker; import com.google.gwt.core.ext.linker.ArtifactSet; import com.google.gwt.core.ext.linker.ConfigurationProperty; import com.google.gwt.core.ext.linker.EmittedArtifact; import com.google.gwt.core.ext.linker.LinkerOrder; import com.google.gwt.core.ext.linker.Shardable; import com.google.gwt.core.ext.linker.SyntheticArtifact; import com.google.gwt.core.ext.linker.impl.SelectionInformation; import com.googlecode.mgwt.linker.server.BindingProperty; @LinkerOrder(LinkerOrder.Order.POST) @Shardable public class PermutationMapLinker extends AbstractLinker { public static final String EXTERNAL_FILES_CONFIGURATION_PROPERTY_NAME = "html5manifestlinker_files"; public static final String PERMUTATION_MANIFEST_FILE_ENDING = ".manifest"; public static final String PERMUTATION_FILE_ENDING = ".perm.xml"; public static final String MANIFEST_MAP_FILE_NAME = "manifest.map"; private XMLPermutationProvider xmlPermutationProvider; public PermutationMapLinker() { xmlPermutationProvider = new XMLPermutationProvider(); manifestWriter = new ManifestWriter(); } private ManifestWriter manifestWriter; @Override public String getDescription() { return "PermutationMapLinker"; } @Override public ArtifactSet link(TreeLogger logger, LinkerContext context, ArtifactSet artifacts, boolean onePermutation) throws UnableToCompleteException { if (onePermutation) { Map<String, Set<BindingProperty>> permutationMap = buildPermutationMap(logger, context, artifacts); Set<Entry<String, Set<BindingProperty>>> entrySet = permutationMap.entrySet(); // since we are in onePermutation there should be just one // strongName // better make sure.. if (permutationMap.size() != 1) { logger.log(Type.ERROR, "There should be only one permutation right now, but there were: '" + permutationMap.size() + "'"); throw new UnableToCompleteException(); } Entry<String, Set<BindingProperty>> next = entrySet.iterator().next(); String strongName = next.getKey(); Set<BindingProperty> bindingProperties = next.getValue(); // all artifacts for this compilation Set<String> artifactsForCompilation = getArtifactsForCompilation(logger, context, artifacts); ArtifactSet toReturn = new ArtifactSet(artifacts); PermutationArtifact permutationArtifact = new PermutationArtifact(PermutationMapLinker.class, strongName, artifactsForCompilation, bindingProperties); toReturn.add(permutationArtifact); return toReturn; } ArtifactSet toReturn = new ArtifactSet(artifacts); Map<String, Set<BindingProperty>> map = buildPermutationMap(logger, context, artifacts); if (map.size() == 0) { // hosted mode return toReturn; } Map<String, PermutationArtifact> permutationArtifactAsMap = getPermutationArtifactAsMap(artifacts); Set<String> externalFiles = getExternalFiles(logger, context); Set<String> allPermutationFiles = getAllPermutationFiles(permutationArtifactAsMap); // get all artifacts Set<String> allArtifacts = getArtifactsForCompilation(logger, context, artifacts); for (Entry<String, PermutationArtifact> entry : permutationArtifactAsMap.entrySet()) { PermutationArtifact permutationArtifact = entry.getValue(); // make a copy of all artifacts HashSet<String> filesForCurrentPermutation = new HashSet<String>(allArtifacts); // remove all permutations filesForCurrentPermutation.removeAll(allPermutationFiles); // add files of the one permutation we are interested in // leaving the common stuff for all permutations in... filesForCurrentPermutation.addAll(entry.getValue().getPermutationFiles()); String permXml = buildPermXml(logger, permutationArtifact, filesForCurrentPermutation, externalFiles); // emit permutation information file SyntheticArtifact emitString = emitString(logger, permXml, permutationArtifact.getPermutationName() + PERMUTATION_FILE_ENDING); toReturn.add(emitString); // build manifest String maniFest = buildManiFest(entry.getKey(), filesForCurrentPermutation, externalFiles); toReturn.add(emitString(logger, maniFest, entry.getKey() + PERMUTATION_MANIFEST_FILE_ENDING)); } toReturn.add(createPermutationMap(logger, map)); return toReturn; } protected String buildPermXml(TreeLogger logger, PermutationArtifact permutationArtifact, Set<String> gwtCompiledFiles, Set<String> otherResources) throws UnableToCompleteException { HashSet<String> namesForPermXml = new HashSet<String>(gwtCompiledFiles); namesForPermXml.addAll(otherResources); try { return xmlPermutationProvider.writePermutationInformation(permutationArtifact.getPermutationName(), permutationArtifact.getBindingProperties(), namesForPermXml); } catch (XMLPermutationProviderException e) { logger.log(Type.ERROR, "can not build xml for permutation file", e); throw new UnableToCompleteException(); } } /** * @param permutationArtifactAsMap * @return */ protected Set<String> getAllPermutationFiles(Map<String, PermutationArtifact> permutationArtifactAsMap) { Set<String> allPermutationFiles = new HashSet<String>(); for (Entry<String, PermutationArtifact> entry : permutationArtifactAsMap.entrySet()) { allPermutationFiles.addAll(entry.getValue().getPermutationFiles()); } return allPermutationFiles; } protected Map<String, PermutationArtifact> getPermutationArtifactAsMap(ArtifactSet artifacts) { Map<String, PermutationArtifact> hashMap = new HashMap<String, PermutationArtifact>(); for (PermutationArtifact permutationArtifact : artifacts.find(PermutationArtifact.class)) { hashMap.put(permutationArtifact.getPermutationName(), permutationArtifact); } return hashMap; } protected boolean shouldArtifactBeInManifest(String pathName) { if (pathName.endsWith("symbolMap") || pathName.endsWith(".xml.gz") || pathName.endsWith("rpc.log") || pathName.endsWith("gwt.rpc") || pathName.endsWith("manifest.txt") || pathName.startsWith("rpcPolicyManifest") || pathName.startsWith("soycReport") || pathName.endsWith(".cssmap")) { return false; } // TODO reg exp return true; } protected Set<String> getArtifactsForCompilation(TreeLogger logger, LinkerContext context, ArtifactSet artifacts) { Set<String> artifactNames = new HashSet<String>(); for (EmittedArtifact artifact : artifacts.find(EmittedArtifact.class)) { String pathName = artifact.getPartialPath(); if (shouldArtifactBeInManifest(pathName)) { artifactNames.add(context.getModuleName() + "/" + pathName); } } return artifactNames; } protected String buildManiFest(String moduleName, Set<String> cacheResources, Set<String> externalFiles) { return manifestWriter.writeManifest(externalFiles, cacheResources); } protected Set<String> getExternalFiles(TreeLogger logger, LinkerContext context) { HashSet<String> set = new HashSet<String>(); SortedSet<ConfigurationProperty> properties = context.getConfigurationProperties(); for (ConfigurationProperty configurationProperty : properties) { String name = configurationProperty.getName(); if (EXTERNAL_FILES_CONFIGURATION_PROPERTY_NAME.equals(name)) { for (String value : configurationProperty.getValues()) { set.add(value); } } } return set; } protected EmittedArtifact createPermutationMap(TreeLogger logger, Map<String, Set<BindingProperty>> map) throws UnableToCompleteException { try { String string = xmlPermutationProvider.serializeMap(map); return emitString(logger, string, MANIFEST_MAP_FILE_NAME); } catch (XMLPermutationProviderException e) { logger.log(Type.ERROR, "can not build manifest map", e); throw new UnableToCompleteException(); } } protected Map<String, Set<BindingProperty>> buildPermutationMap(TreeLogger logger, LinkerContext context, ArtifactSet artifacts) throws UnableToCompleteException { HashMap<String, Set<BindingProperty>> map = new HashMap<String, Set<BindingProperty>>(); for (SelectionInformation result : artifacts.find(SelectionInformation.class)) { Set<BindingProperty> list = new HashSet<BindingProperty>(); map.put(result.getStrongName(), list); TreeMap<String, String> propMap = result.getPropMap(); Set<Entry<String, String>> set = propMap.entrySet(); for (Entry<String, String> entry : set) { BindingProperty bindingProperty = new BindingProperty(entry.getKey(), entry.getValue()); list.add(bindingProperty); } } return map; } }