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;
}
}