package xapi.dev.ui.html;
import com.google.gwt.core.ext.Generator;
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.typeinfo.JClassType;
import com.google.gwt.dev.jjs.UnifyAstView;
import java.lang.reflect.Modifier;
import javax.inject.Provider;
import xapi.annotation.common.Property;
import xapi.annotation.compile.Import;
import xapi.dev.source.ClassBuffer;
import xapi.dev.source.DomBuffer;
import xapi.dev.source.MethodBuffer;
import xapi.dev.source.PrintBuffer;
import xapi.source.X_Source;
import xapi.ui.api.StyleService;
import xapi.ui.html.api.El;
import xapi.ui.html.api.Html;
import xapi.ui.html.api.HtmlSnippet;
import xapi.util.api.ConvertsValue;
public class HtmlSnippetGenerator extends AbstractHtmlGenerator <HtmlGeneratorResult> {
int ctxCnt;
public static final HtmlGeneratorResult generateSnippetProvider(final TreeLogger logger, final UnifyAstView ast, final JClassType templateType, final JClassType modelType) throws UnableToCompleteException {
final String simpleName = templateType.getSimpleSourceName()+"_"+modelType.getSimpleSourceName()+"_ToHtml";
// Check if there is an existing type, and that it's generated hashes match our input type.
final String inputHash = toHash(ast, templateType.getQualifiedSourceName(), modelType.getQualifiedSourceName());
// Generate a new result
final HtmlSnippetGenerator ctx = new HtmlSnippetGenerator(simpleName, ast, templateType);
// Save the result, possibly to a new file if there are existing implementations using our default name
return ctx.generate(logger, ast, templateType, inputHash, simpleName, modelType);
}
@Override
public HtmlGeneratorResult newContext(final JClassType winner, final String pkgName, final String name) {
return new HtmlGeneratorResult(winner, pkgName, name);
}
private HtmlSnippetGenerator(final String className, final UnifyAstView ast, final JClassType templateType) throws UnableToCompleteException {
super(className, templateType, ast);
}
private HtmlGeneratorResult generate(final TreeLogger logger, final UnifyAstView ast, final JClassType templateType, final String inputHash, final String simpleName, final JClassType modelType) throws UnableToCompleteException {
final HtmlGeneratorResult existingType = findExisting(ast, this,
templateType.getPackage().getName(),
X_Source.qualifiedName(templateType.getPackage().getName(), simpleName));
final HtmlGeneratorResult existingResult = existingTypesUnchanged(logger, ast, existingType, inputHash);
if (existingResult != null) {
// If our inputs are unchanged, and the target type exists, just reuse it w/out regenerating
return existingResult;
}
initialize();
clsName = existingType.getFinalName();
final ClassBuffer cls = out.getClassBuffer();
final String modelName = out.getImports().addImport(modelType.getQualifiedSourceName());
cls.setSuperClass(
out.getImports().addImport(HtmlSnippet.class)
+ "<" + modelName + ">"
);
final String buffer = cls.addImport(DomBuffer.class);
final String cssService = cls.addImport(StyleService.class);
final String sig = cls.addImport(ConvertsValue.class) + "<" +
modelName+", " + buffer +
">";
final ClassBuffer provider = cls.createInnerClass("private static final class SnippetProvider")
.addInterface(cls.addImport(Provider.class) + " <" + sig + ">");
cls
.createConstructor(Modifier.PUBLIC, cssService+" cssService")
.println("super(new SnippetProvider(cssService));");
final MethodBuffer ctor = provider.createConstructor(Modifier.PRIVATE, "final "+cssService+" cssService");
provider.createField(Runnable.class, "init");
ctor.println("this.init = new "+cls.addImport(Runnable.class)+"() {")
.indent()
.println("@Override public void run() {")
.indent()
.println("init = NO_OP;");
final PrintBuffer init = new PrintBuffer();
ctor.addToEnd(init);
ctor
.outdent()
.println("}")
.outdent()
.println("};");
final MethodBuffer converter = provider.createMethod("public "+sig+" get()");
converter
.println("return new "+sig+"() {")
.indent()
.println("@Override")
.println("public " + buffer + " convert("+modelName+" from) {")
.indent()
.print(buffer+" out = new "+buffer+"(")
;
if (!Html.ROOT_ELEMENT.equals(documentType)) {
if (documentType.charAt(0)=='#') {
converter.print("from."+documentType.substring(1)+"()");
} else {
converter.print("\""+documentType+"\"");
}
}
converter.println( ");");
final DomBuffer variables = new DomBuffer().println("init.run();");
converter.addToEnd(variables);
for (final String key : renderOrder) {
for (final El el : htmlGen.getElements(key)) {
for (final Import importType : el.imports()) {
addImport(importType);
}
final String name = el.tag();
converter.println("out").indent();
if (name.length()>0) {
if (name.charAt(0)=='#') {
// The tagname is a method reference...
// TODO validate the method name, and allow for full @fully.qualified.Names::toMethods()
converter.println(".makeTag(from."+name.substring(1)+"())");
} else {
converter.println(".makeTag(\""+name+"\")");
}
}
for (final String clsName : el.className()) {
converter.println(".addClassName(\""+clsName+"\")");
}
final StringBuilder immediateStyle = new StringBuilder();
final StringBuilder sheetStyle = new StringBuilder();
final StringBuilder extraStyle = new StringBuilder();
try {
fillStyles(immediateStyle, sheetStyle, extraStyle, el.style());
} catch (final Exception e) {
logger.log(Type.ERROR, "Error calculating styles", e);
throw new UnableToCompleteException();
}
if (immediateStyle.length() > 0) {
converter.println(".setAttribute(\"style\", \""+Generator.escape(immediateStyle.toString())+"\")");
}
if (sheetStyle.length() > 0) {
init.println("cssService.addCss(\""+Generator.escape(sheetStyle.toString())+"\", 0);");
}
if (extraStyle.length() > 0) {
init.println("cssService.addCss(\""+Generator.escape(extraStyle.toString())+"\", Integer.MIN_VALUE);");
}
for (final Property prop : el.properties()) {
String val = prop.value();
if (val.startsWith("//")) {
val = val.substring(2);
} else if (!val.startsWith("\"")){
val = "\""+val+"\"";
}
converter.println(".setAttribute(\""+prop.name()+"\", "+val+ ")");
}
for (final String html : el.html()) {
final String escaped = escape(html, key, el.accessor());
if (escaped.length() > 0) {
converter.println(".println("+ escaped + ")");
}
}
converter.outdent().println(";");
}
}
converter.returnValue("out");
converter
.outdent()
.println("}")
.outdent()
.println("};");
try {
return saveGeneratedType(logger, getLogLevel(), getClass(), ast, out, existingType, inputHash);
} finally {
clear();
ast.getGeneratorContext().finish(logger);
}
}
@Override
protected Type getLogLevel() {
return Type.DEBUG;
}
}