package de.swm.mobile.kitchensink.generator; 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.UnableToCompleteException; import com.google.gwt.core.ext.typeinfo.JClassType; import com.google.gwt.core.ext.typeinfo.NotFoundException; import de.swm.mobile.kitchensink.client.ShowcaseAnnotations; import de.swm.mobile.kitchensink.client.ShowcaseConstants; import de.swm.mobile.kitchensink.client.base.ShowcaseDetailPage; import java.io.*; import java.util.HashSet; import java.util.Set; import static de.swm.mobile.kitchensink.client.ShowcaseAnnotations.ShowcaseSource; /** * Generate the source code, ui.xml source used in the Showcase * examples. */ public class ShowcaseGenerator extends Generator { /** * The class loader used to get resources. */ private ClassLoader classLoader = null; /** * The generator context. */ private GeneratorContext context = null; /** * The {@link TreeLogger} used to log messages. */ private TreeLogger logger = null; /** * The set of raw files that have already been generated. Raw files can be * reused by different examples, but we only generate them once. */ private Set<String> rawFiles = new HashSet<String>(); @Override public String generate( TreeLogger logger, GeneratorContext context, String typeName) throws UnableToCompleteException { this.logger = logger; this.context = context; this.classLoader = Thread.currentThread().getContextClassLoader(); // Only generate files on the first permutation if (!isFirstPass()) { return null; } // Get the Showcase ContentWidget subtypes to examine JClassType cwType = null; try { cwType = context.getTypeOracle().getType(ShowcaseDetailPage.class.getName()); } catch (NotFoundException e) { logger.log(TreeLogger.ERROR, "Cannot find ContentWidget class", e); throw new UnableToCompleteException(); } JClassType[] types = cwType.getSubtypes(); // Generate the source and raw source files for (JClassType type : types) { generateUiXMLFiles(type); generateSourceFiles(type); } return null; } /** * Set the full contents of a resource in the public directory. * * @param partialPath the path to the file relative to the public directory * @param contents the file contents */ private void createPublicResource(String partialPath, String contents) throws UnableToCompleteException { try { OutputStream outStream = context.tryCreateResource(logger, partialPath); if (outStream == null) { String message = "Attempting to generate duplicate public resource: " + partialPath + ".\nAll generated source files must have unique names."; logger.log(TreeLogger.ERROR, message); throw new UnableToCompleteException(); } outStream.write(contents.getBytes()); context.commitResource(logger, outStream); } catch (IOException e) { logger.log(TreeLogger.ERROR, "Error writing file: " + partialPath, e); throw new UnableToCompleteException(); } } /** * Erzeugt die UI.xml dateien. * */ private void generateUiXMLFiles(JClassType type) throws UnableToCompleteException { // Look for annotation if (!type.isAnnotationPresent(ShowcaseAnnotations.ShowcaseUiXML.class)) { return; } // Get the package info String pkgName = type.getPackage().getName(); String pkgPath = pkgName.replace('.', '/') + "/"; // Generate each raw source file String[] filenames = type.getAnnotation(ShowcaseAnnotations.ShowcaseUiXML.class).value(); for (String filename : filenames) { // Check if the file has already been generated. String path = pkgName + filename; if (rawFiles.contains(path)) { continue; } rawFiles.add(path); // Get the file contents String fileContents = getResourceContents(pkgPath + filename); // Make the source pretty fileContents = fileContents.replace("<", "<"); fileContents = fileContents.replace(">", ">"); fileContents = fileContents.replace("* \n */\n", "*/\n"); fileContents = "<pre>" + fileContents + "</pre>"; final StringBuilder sb = new StringBuilder(); sb.append(Templates.getPrefixXML()); sb.append(fileContents); sb.append(Templates.getSuffix()); // Save the raw source in the public directory String dstPath = ShowcaseConstants.DST_SOURCE_UIXML + filename + ".html"; createPublicResource(dstPath, sb.toString()); } } /** * Generate the formatted source code for a {@link com.google.gwt.user.client.ui.Widget}. * * @param type the {@link com.google.gwt.user.client.ui.Widget}. */ private void generateSourceFiles(JClassType type) throws UnableToCompleteException { // Get the file contents String filename = type.getQualifiedSourceName().replace('.', '/') + ".java"; String fileContents = getResourceContents(filename); // Get each data code block String formattedSource = ""; String sourceTag = "@" + ShowcaseSource.class.getSimpleName(); int srcTagIndex = fileContents.indexOf(sourceTag); if (srcTagIndex >= 0) { // Add to the formatted source formattedSource += fileContents + "\n\n"; } // Make the source pretty formattedSource = formattedSource.replace("<", "<"); formattedSource = formattedSource.replace(">", ">"); formattedSource = formattedSource.replace("* \n */\n", "*/\n"); formattedSource = "<pre>" + formattedSource + "</pre>"; final StringBuilder sb = new StringBuilder(); sb.append(Templates.getPrefixJava()); sb.append(formattedSource); sb.append(Templates.getSuffix()); // Save the source code to a file String dstPath = ShowcaseConstants.DST_SOURCE_EXAMPLE + type.getSimpleSourceName() + ".html"; createPublicResource(dstPath, sb.toString()); } /** * Get the full contents of a resource. * * @param path the path to the resource * @return the contents of the resource */ private String getResourceContents(String path) throws UnableToCompleteException { InputStream in = classLoader.getResourceAsStream(path); if (in == null) { logger.log(TreeLogger.ERROR, "Resource not found: " + path); throw new UnableToCompleteException(); } StringBuilder fileContentsBuf = new StringBuilder(); BufferedReader br = null; try { br = new BufferedReader(new InputStreamReader(in)); String temp; while ((temp = br.readLine()) != null) { fileContentsBuf.append(temp).append('\n'); } } catch (IOException e) { logger.log(TreeLogger.ERROR, "Cannot read resource", e); throw new UnableToCompleteException(); } finally { if (br != null) { try { br.close(); } catch (IOException e) { } } } // Return the file contents as a string return fileContentsBuf.toString(); } /** * Ensure that we only generate files once by creating a placeholder file, * then looking for it on subsequent generates. * * @return true if this is the first pass, false if not */ private boolean isFirstPass() { String placeholder = ShowcaseConstants.DST_SOURCE + "generated"; try { OutputStream outStream = context.tryCreateResource(logger, placeholder); if (outStream == null) { return false; } else { context.commitResource(logger, outStream); } } catch (UnableToCompleteException e) { logger.log(TreeLogger.ERROR, "Unable to generate", e); return false; } return true; } }