package com.prezi.spaghetti.gradle.internal;
import com.google.common.base.Charsets;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.io.Files;
import com.prezi.spaghetti.ast.ModuleNode;
import com.prezi.spaghetti.bundle.ModuleBundle;
import com.prezi.spaghetti.bundle.ModuleFormat;
import com.prezi.spaghetti.bundle.ModuleBundleFactory;
import com.prezi.spaghetti.bundle.internal.BundleUtils;
import com.prezi.spaghetti.bundle.internal.ModuleBundleParameters;
import com.prezi.spaghetti.definition.EntityWithModuleMetaData;
import com.prezi.spaghetti.definition.ModuleConfiguration;
import com.prezi.spaghetti.generator.JavaScriptBundleProcessor;
import com.prezi.spaghetti.generator.internal.DefaultJavaScriptBundleProcessorParameters;
import com.prezi.spaghetti.generator.internal.Generators;
import com.prezi.spaghetti.generator.internal.InternalGeneratorUtils;
import com.prezi.spaghetti.packaging.internal.ExternalDependencyGenerator;
import org.gradle.api.file.ConfigurableFileCollection;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.InputDirectory;
import org.gradle.api.tasks.InputFile;
import org.gradle.api.tasks.InputFiles;
import org.gradle.api.tasks.Optional;
import org.gradle.api.tasks.OutputDirectory;
import org.gradle.api.tasks.TaskAction;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.Callable;
public class AbstractBundleModuleTask extends AbstractDefinitionAwareSpaghettiTask {
private File inputFile;
private File outputDirectory;
private String sourceBaseUrl;
private File sourceMap;
private File definitionOverride;
private File resourcesDirectoryInternal;
private final ConfigurableFileCollection prefixes = getProject().files();
private final ConfigurableFileCollection suffixes = getProject().files();
private Map<String, String> externalDependencies = Maps.newTreeMap();
@InputFile
public File getInputFile() {
return inputFile;
}
public void setInputFile(Object inputFile) {
this.inputFile = getProject().file(inputFile);
}
public void inputFile(Object inputFile) {
setInputFile(inputFile);
}
@OutputDirectory
public File getOutputDirectory() {
return outputDirectory;
}
public void setOutputDirectory(Object outputDirectory) {
this.outputDirectory = getProject().file(outputDirectory);
}
public void outputDirectory(Object outputDirectory) {
setOutputDirectory(outputDirectory);
}
@Input
@Optional
public String getSourceBaseUrl() {
return sourceBaseUrl;
}
public void setSourceBaseUrl(String sourceBaseUrl) {
this.sourceBaseUrl = sourceBaseUrl;
}
@SuppressWarnings("UnusedDeclaration")
public void sourceBaseUrl(String source) {
setSourceBaseUrl(source);
}
@InputFile
@Optional
public File getSourceMap() {
if (sourceMap == null) {
// This should probably be done with convention mapping
File defSourceMap = new File(getInputFile().getParentFile(), getInputFile().getName() + ".map");
if (defSourceMap.exists()) {
sourceMap = defSourceMap;
}
}
return sourceMap;
}
public void setSourceMap(Object sourceMap) {
this.sourceMap = getProject().file(sourceMap);
}
@SuppressWarnings("UnusedDeclaration")
public void sourceMap(Object sourceMap) {
setSourceMap(sourceMap);
}
public void setDefinitionOverride(File definitionOverride) {
this.definitionOverride = definitionOverride;
}
public File getDefinitionOverride() {
return definitionOverride;
}
protected File getOriginalDefinitionOrOverride() {
File def = getDefinitionOverride();
return def == null ? getDefinition() : def;
}
protected File getResourcesDirectoryInternal() {
return resourcesDirectoryInternal;
}
protected void setResourcesDirectoryInternal(Object resourcesDirectoryInternal) {
this.resourcesDirectoryInternal = getProject().file(resourcesDirectoryInternal);
}
@SuppressWarnings("UnusedDeclaration")
public void setResourcesDirectory(Object resourcesDir) {
setResourcesDirectoryInternal(resourcesDir);
}
@SuppressWarnings("UnusedDeclaration")
public void resourcesDirectory(Object resourcesDir) {
setResourcesDirectoryInternal(resourcesDir);
}
@InputDirectory
@Optional
public File getResourcesDirectory() {
File dir = getResourcesDirectoryInternal();
return (dir != null && dir.exists()) ? dir : null;
}
public AbstractBundleModuleTask() {
this.getConventionMapping().map("inputFile", new Callable<File>() {
@Override
public File call() throws Exception {
return new File(getProject().getBuildDir(), "module.js");
}
});
}
@InputFiles
public ConfigurableFileCollection getPrefixes() {
return prefixes;
}
public void prefixes(Object... prefixes) {
this.getPrefixes().from(prefixes);
}
public void prefix(Object... prefixes) {
this.prefixes(prefixes);
}
@InputFiles
public ConfigurableFileCollection getSuffixes() {
return suffixes;
}
public void suffixes(Object... suffixes) {
this.getSuffixes().from(suffixes);
}
public void suffix(Object... suffixes) {
this.suffixes(suffixes);
}
@Input
public Map<String, String> getExternalDependencies() {
return externalDependencies;
}
public void setExternalDependencies(Map<String, String> externalDependencies) {
this.externalDependencies = externalDependencies;
}
public void externalDependencies(Map<String, String> externalDependencies) {
this.externalDependencies.putAll(externalDependencies);
}
public void externalDependencies(Set<String> externalDependencies) {
externalDependencies(BundleUtils.parseExternalDependencies(externalDependencies));
}
public void externalDependency(String importName, String dependencyName) {
externalDependencies(ImmutableSortedMap.of(importName, dependencyName));
}
public void externalDependency(String shorthand) {
externalDependency(shorthand, shorthand);
}
@TaskAction
public final ModuleBundle bundle() throws IOException {
ModuleConfiguration config = readConfig(getOriginalDefinitionOrOverride());
String inputContents = "";
for (File prefixFile : getPrefixes()) {
inputContents += Files.asCharSource(prefixFile, Charsets.UTF_8).read();
}
inputContents += Files.asCharSource(getInputFile(), Charsets.UTF_8).read();
for (File suffixFile : getSuffixes()) {
inputContents += Files.asCharSource(suffixFile, Charsets.UTF_8).read();
}
JavaScriptBundleProcessor javaScriptBundleProcessor = Generators.getService(JavaScriptBundleProcessor.class, getLanguage());
DefaultJavaScriptBundleProcessorParameters processorParams = new DefaultJavaScriptBundleProcessorParameters(config);
List<String> importedExternalDependencyVars = ExternalDependencyGenerator.getImportedVarNames(externalDependencies.keySet());
String processedJavaScript = InternalGeneratorUtils.bundleJavaScript(javaScriptBundleProcessor.processModuleJavaScript(processorParams, inputContents), importedExternalDependencyVars);
File sourceMap = getSourceMap();
String sourceMapText = sourceMap != null ? Files.asCharSource(sourceMap, Charsets.UTF_8).read() : null;
return createBundle(config, processedJavaScript, sourceMapText, getResourcesDirectory());
}
protected ModuleBundle createBundle(ModuleConfiguration config, String javaScript, String sourceMap, File resourceDir) throws IOException {
ModuleNode module = config.getLocalModule();
File outputDir = getOutputDirectory();
getLogger().info("Creating bundle in {}", outputDir);
TreeSet<String> dependentModuleNames = Sets.newTreeSet();
for (EntityWithModuleMetaData<ModuleNode> moduleNode : config.getDirectDependentModules()) {
dependentModuleNames.add(moduleNode.getEntity().getName());
}
return ModuleBundleFactory.createDirectory(getOutputDirectory(), new ModuleBundleParameters(
module.getName(),
module.getSource().getContents(),
module.getSource().getDefinitionLanguage(),
String.valueOf(getProject().getVersion()),
ModuleFormat.UMD,
getSourceBaseUrl(),
javaScript,
sourceMap,
dependentModuleNames,
externalDependencies,
resourceDir
));
}
}