/** * Sencha GXT 3.0.0b - Sencha for GWT * Copyright(c) 2007-2012, Sencha, Inc. * licensing@sencha.com * * http://www.sencha.com/products/gxt/license/ */ package com.sencha.gxt.explorer.rebind; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import com.google.gwt.core.client.GWT; import com.google.gwt.core.ext.Generator; import com.google.gwt.core.ext.GeneratorContext; 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.core.ext.typeinfo.TypeOracle; import com.google.gwt.dev.resource.Resource; import com.google.gwt.dev.resource.impl.PathPrefix; import com.google.gwt.dev.resource.impl.PathPrefixSet; import com.google.gwt.dev.resource.impl.ResourceOracleImpl; import com.google.gwt.dev.util.Name; import com.google.gwt.resources.client.ImageResource; import com.google.gwt.user.rebind.ClassSourceFileComposerFactory; import com.google.gwt.user.rebind.SourceWriter; import com.google.gwt.util.tools.Utility; import com.sencha.gxt.explorer.client.model.Category; import com.sencha.gxt.explorer.client.model.Example; import com.sencha.gxt.explorer.client.model.Source; import com.sencha.gxt.explorer.rebind.model.ExampleDetailModel; import com.sencha.gxt.explorer.rebind.model.SourceModel; import com.sencha.gxt.explorer.rebind.model.SourceModel.FileType; public class SampleGenerator extends Generator { private String javaHeader; // cssheader, xmlheader, etc private String footer; private ResourceOracleImpl sourceOracle; @Override public String generate(TreeLogger logger, GeneratorContext context, String typeName) throws UnableToCompleteException { // Get access to metadata about the type to be generated TypeOracle oracle = context.getTypeOracle(); JClassType toGenerate = oracle.findType(typeName).isClass(); // Get the name of the new type String packageName = toGenerate.getPackage().getName(); String simpleSourceName = toGenerate.getName().replace('.', '_') + "Impl"; PrintWriter pw = context.tryCreate(logger, packageName, simpleSourceName); if (pw == null) { return packageName + "." + simpleSourceName; } // Generate an HTML file resource for every example and write the source JClassType[] types = oracle.getTypes(); // Build a ResourceOracle capable of reading java files sourceOracle = new ResourceOracleImpl(logger.branch(Type.DEBUG, "Gathering sources")); // Clean up these prefixes to not have filters PathPrefixSet prefixes = ((ResourceOracleImpl) context.getResourcesOracle()).getPathPrefixes(); sourceOracle.setPathPrefixes(new PathPrefixSet()); for (PathPrefix p : prefixes.values()) { sourceOracle.getPathPrefixes().add(new PathPrefix(p.getPrefix(), null)); } ResourceOracleImpl.refresh(logger, sourceOracle); // Load the header and footer HTML content try { String slashyPackageName = getClass().getPackage().getName().replace('.', '/'); javaHeader = Utility.getFileFromClassPath(slashyPackageName + "/header.html"); footer = Utility.getFileFromClassPath(slashyPackageName + "/footer.html"); } catch (IOException e) { logger.log(Type.ERROR, "Header or Footer failed to be read", e); throw new UnableToCompleteException(); } // Find all examples, annotated with @Detail Set<ExampleDetailModel> examples = new HashSet<ExampleDetailModel>(); Map<String, List<ExampleDetailModel>> hierarchy = new HashMap<String, List<ExampleDetailModel>>(); Set<SourceModel> exampleSources = new HashSet<SourceModel>(); for (JClassType type : types) { Example.Detail detail = type.getAnnotation(Example.Detail.class); if (detail != null) { ExampleDetailModel example = new ExampleDetailModel(logger, context, type, detail); // Collect sources to be built into html exampleSources.addAll(example.getAllSources()); List<ExampleDetailModel> exampleList = hierarchy.get(detail.category()); if (exampleList == null) { exampleList = new ArrayList<ExampleDetailModel>(); hierarchy.put(detail.category(), exampleList); } examples.add(example); exampleList.add(example); } } // Sort folders, sort within those folders List<String> folders = new ArrayList<String>(hierarchy.keySet()); Collections.sort(folders); for (List<ExampleDetailModel> contents : hierarchy.values()) { Collections.sort(contents); } // Actually build source for each type for (SourceModel type : exampleSources) { TreeLogger l = logger.branch(Type.DEBUG, "Writing HTML file for " + type.getName()); // attempt to create the output file if (type.getType() == FileType.JAVA) { writeTypeToHtml(l, context, type.getJClassType()); } else { writeFileToHtml(l, context, type.getPath()); } } // Start making the class, with basic imports ClassSourceFileComposerFactory factory = new ClassSourceFileComposerFactory(packageName, simpleSourceName); factory.setSuperclass(typeName); factory.addImport(Name.getSourceNameForClass(Category.class)); factory.addImport(Name.getSourceNameForClass(ImageResource.class)); factory.addImport(Name.getSourceNameForClass(GWT.class)); factory.addImport(Name.getSourceNameForClass(Example.class)); factory.addImport(Name.getSourceNameForClass(Source.class)); factory.addImport(Name.getSourceNameForClass(Source.FileType.class)); SourceWriter sw = factory.createSourceWriter(context, pw); // Write the ctor sw.println("public %1$s() {", simpleSourceName); sw.indent(); // Declare variables that will be used sw.println("Category c;"); sw.println("ImageResource icon;"); sw.println("Example e;"); sw.println("Source dir;"); Set<String> names = new HashSet<String>(); Map<JClassType, String> bundles = new HashMap<JClassType, String>(); for (String folder : folders) { // TODO escape name sw.println("c = new Category(\"%1$s\");", folder); for (ExampleDetailModel example : hierarchy.get(folder)) { // make sure the bundle to be used exists if (!bundles.containsKey(example.getClientBundleType())) { String bundleName = getNextName("bundle", names); sw.println("%1$s %2$s = GWT.create(%1$s.class);", example.getClientBundleType().getQualifiedSourceName(), bundleName); bundles.put(example.getClientBundleType(), bundleName); } // write out the example, adding it to the current category writeExample(sw, bundles.get(example.getClientBundleType()), example); } sw.println("categories.add(c);"); } sw.outdent(); sw.println("}");// end ctor sw.commit(logger); return factory.getCreatedClassName(); } protected void writeExample(SourceWriter sw, String bundleName, ExampleDetailModel example) { sw.println("icon = %1$s.%2$s();", bundleName, example.getIconMethodName()); // TODO escape name sw.println("e = new Example(\"%1$s\", icon, new %2$s(), %3$b);", example.getName(), example.getExampleType().getQualifiedSourceName(), example.usesFitLayout()); sw.println("dir = new Source(\"Java\");"); sw.println("e.getSources().add(dir);"); for (SourceModel src : example.getJavaSources()) { sw.println("dir.addChild(new Source(\"%1$s\", GWT.getModuleBaseURL() + \"%2$s\", FileType.%3$s));", src.getName(), src.getUrl(), src.getType().name()); } for (FileType type : FileType.values()) { if (type == FileType.JAVA || type == FileType.FOLDER) { continue; } List<SourceModel> srcs = example.getOtherSources(type); if (srcs.size() != 0) { sw.println("dir = new Source(\"%1$s\");", type.name()); sw.println("e.getSources().add(dir);"); for (SourceModel src : srcs) { sw.println("dir.addChild(new Source(\"%1$s\", GWT.getModuleBaseURL() + \"%2$s\", FileType.%3$s));", src.getName(), src.getUrl(), src.getType().name()); } } } sw.println("c.addExample(e);"); } private String getNextName(String prefix, Set<String> names) { String name = prefix; if (names.contains(name)) { return name; } int suffix = 1; do { name = prefix + "_" + suffix; } while (names.contains(name)); return name; } private void writeFileToHtml(TreeLogger l, GeneratorContext ctx, String path) throws UnableToCompleteException { Resource file = ctx.getResourcesOracle().getResourceMap().get(path); if (file == null) { l.log(Type.ERROR, "File cannot be found."); throw new UnableToCompleteException(); } OutputStream stream = ctx.tryCreateResource(l, "code/" + path.replace('/', '.') + ".html"); if (stream == null) { // file already exists for this compile return; } try { InputStream input = file.openContents(); byte[] bytes = new byte[input.available()]; input.read(bytes); input.close(); // Write out the HTML file // TODO change this header stream.write(javaHeader.getBytes()); stream.write(bytes); stream.write(footer.getBytes()); stream.close(); } catch (Exception e) { l.log(Type.ERROR, "An error occured writing out a file into html", e); throw new UnableToCompleteException(); } ctx.commitResource(l, stream); } /** * Writes out the given class/interface to an HTML file, using the current * header/footer strings * * @param l * @param ctx * @param type * @throws UnableToCompleteException */ private void writeTypeToHtml(TreeLogger l, GeneratorContext ctx, JClassType type) throws UnableToCompleteException { assert type.isClassOrInterface() != null : "Can only generate source for classes or interfaces"; OutputStream stream = ctx.tryCreateResource(l, "code/" + type.getQualifiedSourceName() + ".html"); if (stream == null) { // file already exists for this compile return; } try { String name = type.getQualifiedSourceName().replace('.', '/') + ".java"; l.log(Type.DEBUG, "Reading from " + name); InputStream input = sourceOracle.getResourceMap().get(name).openContents(); BufferedReader reader = new BufferedReader(new InputStreamReader(input)); // Write out the HTML file stream.write(javaHeader.getBytes()); String line; boolean skip = false; while (null != (line = reader.readLine())) { if (line.startsWith("@Detail") || line.startsWith("@Example.Detail")) { skip = true; } else if (skip) { if (line.startsWith("public")) { skip = false; } } if (!skip) { stream.write(line.getBytes()); stream.write('\n'); } } stream.write(footer.getBytes()); stream.close(); } catch (Exception e) { l.log(Type.ERROR, "Error occured writing out a java file into html", e); throw new UnableToCompleteException(); } ctx.commitResource(l, stream); } }