package org.webpieces.gradle.compiler;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Set;
import org.apache.commons.io.IOUtils;
import org.codehaus.groovy.tools.GroovyClass;
import org.gradle.api.file.FileCollection;
import org.gradle.api.logging.LogLevel;
import org.gradle.api.tasks.TaskAction;
import org.gradle.api.tasks.compile.AbstractCompile;
import org.webpieces.templating.api.ProdTemplateModule;
import org.webpieces.templatingdev.api.CompileCallback;
import org.webpieces.templatingdev.api.DevTemplateModule;
import org.webpieces.templatingdev.api.StubModule;
import org.webpieces.templatingdev.api.TemplateCompileConfig;
import org.webpieces.templatingdev.impl.HtmlToJavaClassCompiler;
import org.webpieces.util.net.URLEncoder;
import com.google.inject.Guice;
import com.google.inject.Injector;
import groovy.lang.GroovyClassLoader;
public class TemplateCompilerTask extends AbstractCompile {
// private TemplateCompileOptions options = new TemplateCompileOptions();
//
// @Nested
// public TemplateCompileOptions getOptions() {
// return options;
// }
//
// public void options(Action<TemplateCompileOptions> action) {
// action.execute(getOptions());
// }
//
// public void options(Closure<?> closure) {
// getProject().configure(getOptions(), closure);
// }
@TaskAction
public void compile() {
try {
TemplateCompileOptions options = getProject().getExtensions().findByType(TemplateCompileOptions.class);
compileImpl(options);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public void compileImpl(TemplateCompileOptions options) throws IOException {
File buildDir = getProject().getBuildDir();
//need to make customizable...
File groovySrcGen = new File(buildDir, "groovysrc");
System.out.println("groovy src directory="+groovySrcGen);
Charset encoding = Charset.forName(options.getEncoding());
TemplateCompileConfig config = new TemplateCompileConfig(false);
config.setFileEncoding(encoding);
config.setPluginClient(true);
config.setGroovySrcWriteDirectory(groovySrcGen);
System.out.println("custom tags="+options.getCustomTags());
config.setCustomTagsFromPlugin(options.getCustomTags());
LogLevel logLevel = getProject().getGradle().getStartParameter().getLogLevel();
File destinationDir = getDestinationDir();
System.out.println("destDir="+destinationDir);
File routeIdFile = new File(destinationDir, ProdTemplateModule.ROUTE_META_FILE);
if(routeIdFile.exists())
routeIdFile.delete();
routeIdFile.createNewFile();
System.out.println("routeId.txt file="+routeIdFile.getAbsolutePath());
FileCollection srcCollection = getSource();
Set<File> files = srcCollection.getFiles();
File firstFile = files.iterator().next();
File baseDir = findBase(firstFile);
try (FileOutputStream routeOut = new FileOutputStream(routeIdFile);
OutputStreamWriter write = new OutputStreamWriter(routeOut, encoding.name());
BufferedWriter bufWrite = new BufferedWriter(write)
) {
Injector injector = Guice.createInjector(
new StubModule(),
new DevTemplateModule(config, new PluginCompileCallback(destinationDir, bufWrite))
);
HtmlToJavaClassCompiler compiler = injector.getInstance(HtmlToJavaClassCompiler.class);
GroovyClassLoader cl = new GroovyClassLoader();
for(File f : files) {
System.out.println("file="+f);
String fullName = findFullName(baseDir, f);
System.out.println("name="+fullName);
String source = readSource(f);
compiler.compile(cl, fullName, source);
}
}
setDidWork(true);
}
private String readSource(File f) {
try {
try (FileInputStream in = new FileInputStream(f)) {
return IOUtils.toString(in);
}
} catch(IOException e) {
throw new RuntimeException(e);
}
}
private String findFullName(File baseDir, File f) {
if(f.getName().contains("_"))
throw new IllegalArgumentException("File name is invalid. It cannot contain _ in the name="+f.getAbsolutePath());
String name = f.getName().replace(".", "_");
File current = f.getParentFile();
while(current != null && !baseDir.equals(current)) {
name = current.getName()+"."+name;
current = current.getParentFile();
}
if(!current.equals(baseDir))
throw new IllegalStateException("Could not find basedir="+baseDir+" on file="+f);
return name;
}
private File findBase(File firstFile) {
File baseDir = recurse(firstFile.getParentFile());
if(baseDir == null)
throw new IllegalStateException("baseDir of src/main/java could not be found. We currently dont' work outside src/main/java yet");
return baseDir;
}
private File recurse(File firstFile) {
if(firstFile.getParentFile() == null || firstFile.getParentFile().getParentFile() == null)
return null;
String src = firstFile.getParentFile().getParentFile().getName();
String main = firstFile.getParentFile().getName();
String java = firstFile.getName();
if("src".equals(src) && "main".equals(main) && "java".equals(java))
return firstFile;
return recurse(firstFile.getParentFile());
}
private static class PluginCompileCallback implements CompileCallback {
private File destinationDir;
private BufferedWriter routeOut;
public PluginCompileCallback(File destinationDir, BufferedWriter bufWrite) {
this.destinationDir = destinationDir;
this.routeOut = bufWrite;
}
public void compiledGroovyClass(GroovyClassLoader groovyCl, GroovyClass clazz) {
String name = clazz.getName();
String path = name.replace('.', '/');
String fullPathName = path+".class";
File f = new File(destinationDir, fullPathName);
//File f = createFile(destinationDir, name);
System.out.println("file write to="+f);
try {
try (FileOutputStream str = new FileOutputStream(f)) {
IOUtils.write(clazz.getBytes(), str);
}
} catch(IOException e) {
throw new RuntimeException(e);
}
}
@Override
public void recordRouteId(String routeId, List<String> argNames, String sourceLocation) {
String argStr = "";
for(String s: argNames) {
if(s.contains(":"))
throw new RuntimeException("bug, argument should not contain : character. argNames="+argNames+" arg="+s);
argStr += ","+s;
}
if(routeId.contains(":"))
throw new RuntimeException("bug, route should not contain : character");
String encodedRouteId = URLEncoder.encode(routeId, StandardCharsets.UTF_8);
String encodedArgs = URLEncoder.encode(argStr, StandardCharsets.UTF_8);
String encodedSourceLocation = URLEncoder.encode(sourceLocation, StandardCharsets.UTF_8);
try {
routeOut.write(ProdTemplateModule.ROUTE_TYPE+"/"+encodedSourceLocation+"/"+encodedRouteId+":"+encodedArgs+":dummy\n");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public void recordPath(String relativeUrlPath, String sourceLocation) {
if(relativeUrlPath.contains(":"))
throw new RuntimeException("bug, route should not contain : character");
String encodedPath = URLEncoder.encode(relativeUrlPath, StandardCharsets.UTF_8);
String encodedSourceLocation = URLEncoder.encode(sourceLocation, StandardCharsets.UTF_8);
try {
routeOut.write(ProdTemplateModule.PATH_TYPE+"/"+encodedSourceLocation+"/"+encodedPath+"\n");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}