diff --git a/build.xml b/build.xml index fe4c7a3f4..b49c8fb4c 100644 --- a/build.xml +++ b/build.xml @@ -457,6 +457,26 @@ + + + + + + + + + + + + + + + diff --git a/public/java/src/org/broadinstitute/sting/commandline/CommandLineProgram.java b/public/java/src/org/broadinstitute/sting/commandline/CommandLineProgram.java index aba4fc109..d88e7030e 100644 --- a/public/java/src/org/broadinstitute/sting/commandline/CommandLineProgram.java +++ b/public/java/src/org/broadinstitute/sting/commandline/CommandLineProgram.java @@ -43,7 +43,7 @@ import java.util.Locale; public abstract class CommandLineProgram { /** The command-line program and the arguments it returned. */ - protected ParsingEngine parser = null; + public ParsingEngine parser = null; /** the default log level */ @Argument(fullName = "logging_level", @@ -144,6 +144,11 @@ public abstract class CommandLineProgram { public static int result = -1; + @SuppressWarnings("unchecked") + public static void start(CommandLineProgram clp, String[] args) throws Exception { + start(clp, args, false); + } + /** * This function is called to start processing the command line, and kick * off the execute message of the program. @@ -153,7 +158,7 @@ public abstract class CommandLineProgram { * @throws Exception when an exception occurs */ @SuppressWarnings("unchecked") - public static void start(CommandLineProgram clp, String[] args) throws Exception { + public static void start(CommandLineProgram clp, String[] args, boolean dryRun) throws Exception { try { // setup our log layout @@ -180,8 +185,9 @@ public abstract class CommandLineProgram { // - InvalidArgument in case these arguments are specified by plugins. // - MissingRequiredArgument in case the user requested help. Handle that later, once we've // determined the full complement of arguments. - parser.validate(EnumSet.of(ParsingEngine.ValidationType.MissingRequiredArgument, - ParsingEngine.ValidationType.InvalidArgument)); + if ( ! dryRun ) + parser.validate(EnumSet.of(ParsingEngine.ValidationType.MissingRequiredArgument, + ParsingEngine.ValidationType.InvalidArgument)); parser.loadArgumentsIntoObject(clp); // Initialize the logger using the loaded command line. @@ -195,36 +201,40 @@ public abstract class CommandLineProgram { if (isHelpPresent(parser)) printHelpAndExit(clp, parser); - parser.validate(); + if ( ! dryRun ) parser.validate(); } else { parser.parse(args); - if (isHelpPresent(parser)) - printHelpAndExit(clp, parser); + if ( ! dryRun ) { + if (isHelpPresent(parser)) + printHelpAndExit(clp, parser); - parser.validate(); + parser.validate(); + } parser.loadArgumentsIntoObject(clp); // Initialize the logger using the loaded command line. clp.setupLoggerLevel(layout); } - // if they specify a log location, output our data there - if (clp.toFile != null) { - FileAppender appender; - try { - appender = new FileAppender(layout, clp.toFile, false); - logger.addAppender(appender); - } catch (IOException e) { - throw new RuntimeException("Unable to re-route log output to " + clp.toFile + " make sure the destination exists"); + if ( ! dryRun ) { + // if they specify a log location, output our data there + if (clp.toFile != null) { + FileAppender appender; + try { + appender = new FileAppender(layout, clp.toFile, false); + logger.addAppender(appender); + } catch (IOException e) { + throw new RuntimeException("Unable to re-route log output to " + clp.toFile + " make sure the destination exists"); + } } + + // regardless of what happens next, generate the header information + HelpFormatter.generateHeaderInformation(clp.getApplicationDetails(), args); + + // call the execute + CommandLineProgram.result = clp.execute(); } - - // regardless of what happens next, generate the header information - HelpFormatter.generateHeaderInformation(clp.getApplicationDetails(), args); - - // call the execute - CommandLineProgram.result = clp.execute(); } catch (ArgumentException e) { clp.parser.printHelp(clp.getApplicationDetails()); diff --git a/public/java/src/org/broadinstitute/sting/commandline/ParsingEngine.java b/public/java/src/org/broadinstitute/sting/commandline/ParsingEngine.java index 8423bb2f2..0dc18e6f9 100755 --- a/public/java/src/org/broadinstitute/sting/commandline/ParsingEngine.java +++ b/public/java/src/org/broadinstitute/sting/commandline/ParsingEngine.java @@ -45,7 +45,7 @@ public class ParsingEngine { * A list of defined arguments against which command lines are matched. * Package protected for testing access. */ - ArgumentDefinitions argumentDefinitions = new ArgumentDefinitions(); + public ArgumentDefinitions argumentDefinitions = new ArgumentDefinitions(); /** * A list of matches from defined arguments to command-line text. diff --git a/public/java/src/org/broadinstitute/sting/utils/help/GATKDoclet.java b/public/java/src/org/broadinstitute/sting/utils/help/GATKDoclet.java index d73c3d6fc..bf99be641 100644 --- a/public/java/src/org/broadinstitute/sting/utils/help/GATKDoclet.java +++ b/public/java/src/org/broadinstitute/sting/utils/help/GATKDoclet.java @@ -29,10 +29,14 @@ import freemarker.template.Configuration; import freemarker.template.DefaultObjectWrapper; import freemarker.template.Template; import freemarker.template.TemplateException; +import org.broadinstitute.sting.commandline.*; +import org.broadinstitute.sting.gatk.CommandLineExecutable; +import org.broadinstitute.sting.gatk.CommandLineGATK; import org.broadinstitute.sting.gatk.walkers.Walker; import org.broadinstitute.sting.utils.Utils; import org.broadinstitute.sting.utils.classloader.JVMUtils; import org.broadinstitute.sting.utils.exceptions.ReviewedStingException; +import scala.reflect.Print; import java.io.*; import java.util.*; @@ -40,7 +44,7 @@ import java.util.*; /** * */ -public class GATKDoclet { +public class GATKDoclet extends ResourceBundleExtractorDoclet { /** * Extracts the contents of certain types of javadoc and adds them to an XML file. * @param rootDoc The documentation root. @@ -48,47 +52,164 @@ public class GATKDoclet { * @throws java.io.IOException if output can't be written. */ public static boolean start(RootDoc rootDoc) throws IOException { - /* ------------------------------------------------------------------- */ - /* You should do this ONLY ONCE in the whole application life-cycle: */ + GATKDoclet doclet = new GATKDoclet(); + //PrintStream out = doclet.loadData(rootDoc, false); + doclet.processDocs(rootDoc, null); + return true; + } - Configuration cfg = new Configuration(); - // Specify the data source where the template files come from. - // Here I set a file directory for it: - cfg.setDirectoryForTemplateLoading(new File("settings/helpTemplates/")); - // Specify how templates will see the data-model. This is an advanced topic... - // but just use this: - cfg.setObjectWrapper(new DefaultObjectWrapper()); + public static int optionLength(String option) { + return ResourceBundleExtractorDoclet.optionLength(option); + } + @Override + protected void processDocs(RootDoc rootDoc, PrintStream ignore) { + try { + /* ------------------------------------------------------------------- */ + /* You should do this ONLY ONCE in the whole application life-cycle: */ + Configuration cfg = new Configuration(); + // Specify the data source where the template files come from. + // Here I set a file directory for it: + cfg.setDirectoryForTemplateLoading(new File("settings/helpTemplates/")); + // Specify how templates will see the data-model. This is an advanced topic... + // but just use this: + cfg.setObjectWrapper(new DefaultObjectWrapper()); + + for ( ClassDoc doc : rootDoc.classes() ) { + if ( ResourceBundleExtractorDoclet.isWalker(doc) ) { + System.out.printf("Walker class %s%n", doc); + processWalkerDocs(cfg, doc); + //return; + } +// else +// System.out.printf("Excluding non-walker class %s%n", doc); + } + } catch ( FileNotFoundException e ) { + throw new RuntimeException(e); + } catch ( IOException e ) { + throw new RuntimeException(e); + } + } + + private void processWalkerDocs(Configuration cfg, ClassDoc doc) throws IOException { /* ------------------------------------------------------------------- */ /* You usually do these for many times in the application life-cycle: */ - /* Create a data-model */ // Create the root hash - Map root = new HashMap(); - // Put string ``user'' into the root - root.put("user", "Mark DePristo"); + Map root = buildWalkerDataModel(doc); /* Get or create a template */ Template temp = cfg.getTemplate("test.html"); /* Merge data-model with template */ - Writer out = new OutputStreamWriter(System.out); + Writer out = new OutputStreamWriter(new FileOutputStream(new File("testdoc/" + getClassName(doc).replace(".", "_") + ".html"))); try { temp.process(root, out); out.flush(); } catch ( TemplateException e ) { throw new ReviewedStingException("Failed to create GATK documentation", e); } - return true; } - /** - * Validate the given options against options supported by this doclet. - * @param option Option to validate. - * @return Number of potential parameters; 0 if not supported. - */ - public static int optionLength(String option) { - return 0; + + private Map buildWalkerDataModel(ClassDoc classdoc) { + Map root = new HashMap(); + + root.put("name", classdoc.name()); + + // Extract overrides from the doc tags. + StringBuilder summaryBuilder = new StringBuilder(); + for(Tag tag: classdoc.firstSentenceTags()) + summaryBuilder.append(tag.text()); + root.put("summary", summaryBuilder.toString()); + root.put("description", classdoc.commentText()); + + for(Tag tag: classdoc.tags()) { + root.put(tag.name(), tag.text()); + } + + ParsingEngine parsingEngine = createStandardGATKParsingEngine(); +// for (ArgumentDefinition argumentDefinition : parsingEngine.argumentDefinitions ) +// System.out.println(argumentDefinition); + + Map> args = new HashMap>(); + root.put("arguments", args); + args.put("required", new ArrayList()); + args.put("optional", new ArrayList()); + args.put("hidden", new ArrayList()); + args.put("depreciated", new ArrayList()); + try { + for ( ArgumentSource argumentSource : parsingEngine.extractArgumentSources(getClassForDoc(classdoc)) ) { + String kind = "optional"; + if ( argumentSource.isRequired() ) kind = "required"; + else if ( argumentSource.isHidden() ) kind = "hidden"; + else if ( argumentSource.isDeprecated() ) kind = "depreciated"; + args.get(kind).add(argumentDataModel(argumentSource.createArgumentDefinitions().get(0))); + System.out.printf("Processing %s%n", argumentSource); + +// for(FieldDoc fieldDoc: classdoc.fields()) { +// //for ( AnnotationDesc desc : fieldDoc.annotations() ) { +// System.out.printf("AnnotationDesc %s%n", desc); +// if ( implementsInterface(desc.annotationType(), Argument.class, Output.class, Input.class) ) { +// (requiredAnnotation(desc) ? requiredArgs : optionalArgs).add(dataModelForArgument(desc)); +// System.out.printf("Processing %s%n", desc); +// } else { +// System.out.printf("Skipping %s%n", desc); +// } +// } +// } + } + } catch ( ClassNotFoundException e ) { + throw new RuntimeException(e); + } + + System.out.printf("Root is %s%n", root); + return root; + } + + protected String withDefault(String val, String def) { + return val == null ? def : val; + } + + protected Map argumentDataModel(ArgumentDefinition argumentDefinition) { + Map root = new HashMap(); + root.put("shortName", withDefault(argumentDefinition.shortName, "None provided")); + root.put("required", argumentDefinition.required); + root.put("fullName", withDefault(argumentDefinition.fullName, "None provided")); + root.put("argumentType", argumentDefinition.argumentType); + root.put("doc", withDefault(argumentDefinition.doc, "None provided")); + return root; + } + + protected ParsingEngine createStandardGATKParsingEngine() { + CommandLineProgram clp = new CommandLineGATK(); + try { + CommandLineProgram.start(clp, new String[]{}, true); + return clp.parser; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + protected Map dataModelForArgument(AnnotationDesc desc) { + Map root = new HashMap(); + root.put("shortName", "None provided"); + root.put("required", false); + root.put("fullName", "None provided"); + root.put("doc", "None provided"); + + for ( AnnotationDesc.ElementValuePair keyvalue : desc.elementValues() ) { + root.put(keyvalue.element().name(), keyvalue.value().value()); + } + return root; + } + + protected boolean requiredAnnotation(AnnotationDesc desc) { + for ( AnnotationDesc.ElementValuePair keyvalue : desc.elementValues() ) { + if ( keyvalue.element().name().equals("required") ) + return keyvalue.value().toString().equals("true"); + } + return false; } } diff --git a/public/java/src/org/broadinstitute/sting/utils/help/ResourceBundleExtractorDoclet.java b/public/java/src/org/broadinstitute/sting/utils/help/ResourceBundleExtractorDoclet.java index c0afcc338..05408c2fa 100644 --- a/public/java/src/org/broadinstitute/sting/utils/help/ResourceBundleExtractorDoclet.java +++ b/public/java/src/org/broadinstitute/sting/utils/help/ResourceBundleExtractorDoclet.java @@ -30,12 +30,10 @@ import org.broadinstitute.sting.gatk.walkers.Walker; import org.broadinstitute.sting.utils.Utils; import org.broadinstitute.sting.utils.classloader.JVMUtils; import org.broadinstitute.sting.utils.exceptions.ReviewedStingException; +import sun.tools.java.ClassNotFound; import java.io.*; -import java.util.HashSet; -import java.util.Properties; -import java.util.Scanner; -import java.util.Set; +import java.util.*; /** * Extracts certain types of javadoc (specifically package and class descriptions) and makes them available @@ -48,17 +46,19 @@ public class ResourceBundleExtractorDoclet { /** * Taglet for the particular version number. */ - private static final String VERSION_TAGLET_NAME = "version"; + protected static final String VERSION_TAGLET_NAME = "version"; /** * Maintains a collection of resources in memory as they're accumulated. */ - private static final Properties resourceText = new Properties(); + protected final Properties resourceText = new Properties(); /** * Maintains a collection of classes that should really be documented. */ - private static final Set undocumentedWalkers = new HashSet(); + protected final Set undocumentedWalkers = new HashSet(); + + protected String buildTimestamp = null, versionPrefix = null, versionSuffix = null, absoluteVersion = null; /** * Extracts the contents of certain types of javadoc and adds them to an XML file. @@ -67,13 +67,26 @@ public class ResourceBundleExtractorDoclet { * @throws IOException if output can't be written. */ public static boolean start(RootDoc rootDoc) throws IOException { + ResourceBundleExtractorDoclet doclet = new ResourceBundleExtractorDoclet(); + PrintStream out = doclet.loadData(rootDoc, true); + doclet.processDocs(rootDoc, out); + return true; + } + + protected PrintStream loadData(RootDoc rootDoc, boolean overwriteResourcesFile) { PrintStream out = System.out; - String buildTimestamp = null, versionPrefix = null, versionSuffix = null, absoluteVersion = null; for(String[] options: rootDoc.options()) { if(options[0].equals("-out")) { - loadExistingResourceFile(options[1], rootDoc); - out = new PrintStream(options[1]); + try { + loadExistingResourceFile(options[1], rootDoc); + if ( overwriteResourcesFile ) + out = new PrintStream(options[1]); + } catch ( FileNotFoundException e ) { + throw new RuntimeException(e); + } catch ( IOException e ) { + throw new RuntimeException(e); + } } if(options[0].equals("-build-timestamp")) buildTimestamp = options[1]; @@ -86,7 +99,10 @@ public class ResourceBundleExtractorDoclet { } resourceText.setProperty("build.timestamp",buildTimestamp); + return out; + } + protected void processDocs(RootDoc rootDoc, PrintStream out) { // Cache packages as we see them, since there's no direct way to iterate over packages. Set packages = new HashSet(); @@ -97,13 +113,19 @@ public class ResourceBundleExtractorDoclet { if(isRequiredJavadocMissing(currentClass) && isWalker(currentClass)) undocumentedWalkers.add(currentClass.name()); - renderHelpText(getClassName(currentClass),currentClass,versionPrefix,versionSuffix,absoluteVersion); + renderHelpText(getClassName(currentClass),currentClass); } for(PackageDoc currentPackage: packages) - renderHelpText(currentPackage.name(),currentPackage,versionPrefix,versionSuffix,absoluteVersion); + renderHelpText(currentPackage.name(),currentPackage); - resourceText.store(out,"Strings displayed by the Sting help system"); + try { + resourceText.store(out,"Strings displayed by the Sting help system"); + } catch ( FileNotFoundException e ) { + throw new RuntimeException(e); + } catch ( IOException e ) { + throw new RuntimeException(e); + } // ASCII codes for making text blink final String blink = "\u001B\u005B\u0035\u006D"; @@ -111,8 +133,6 @@ public class ResourceBundleExtractorDoclet { if(undocumentedWalkers.size() > 0) Utils.warnUser(String.format("The following walkers are currently undocumented: %s%s%s", blink, Utils.join(" ",undocumentedWalkers), reset)); - - return true; } /** @@ -137,7 +157,7 @@ public class ResourceBundleExtractorDoclet { * @throws IOException if there is an I/O-related error other than FileNotFoundException * while attempting to read the resource file. */ - private static void loadExistingResourceFile( String resourceFileName, RootDoc rootDoc ) throws IOException { + private void loadExistingResourceFile( String resourceFileName, RootDoc rootDoc ) throws IOException { try { BufferedReader resourceFile = new BufferedReader(new FileReader(resourceFileName)); try { @@ -157,10 +177,21 @@ public class ResourceBundleExtractorDoclet { * @param classDoc the type of the given class. * @return True if the class of the given name is a walker. False otherwise. */ - private static boolean isWalker(ClassDoc classDoc) { + protected static boolean isWalker(ClassDoc classDoc) { + return assignableToClass(classDoc, Walker.class, true); + } + + protected static boolean implementsInterface(ProgramElementDoc classDoc, Class... interfaceClasses) { + for ( Class interfaceClass : interfaceClasses ) + if ( assignableToClass(classDoc, interfaceClass, false) ) + return true; + return false; + } + + protected static boolean assignableToClass(ProgramElementDoc classDoc, Class lhsClass, boolean requireConcrete) { try { - Class type = Class.forName(getClassName(classDoc)); - return Walker.class.isAssignableFrom(type) && JVMUtils.isConcrete(type); + Class type = getClassForDoc(classDoc); + return lhsClass.isAssignableFrom(type) && (!requireConcrete || JVMUtils.isConcrete(type)); } catch(Throwable t) { // Ignore errors. @@ -168,16 +199,20 @@ public class ResourceBundleExtractorDoclet { } } + protected static Class getClassForDoc(ProgramElementDoc doc) throws ClassNotFoundException { + return Class.forName(getClassName(doc)); + } + /** * Reconstitute the class name from the given class JavaDoc object. - * @param classDoc the Javadoc model for the given class. + * @param doc the Javadoc model for the given class. * @return The (string) class name of the given class. */ - private static String getClassName(ClassDoc classDoc) { - PackageDoc containingPackage = classDoc.containingPackage(); + protected static String getClassName(ProgramElementDoc doc) { + PackageDoc containingPackage = doc.containingPackage(); return containingPackage.name().length() > 0 ? - String.format("%s.%s",containingPackage.name(),classDoc.name()) : - String.format("%s",classDoc.name()); + String.format("%s.%s",containingPackage.name(),doc.name()) : + String.format("%s",doc.name()); } /** @@ -193,10 +228,8 @@ public class ResourceBundleExtractorDoclet { * Renders all the help text required for a given name. * @param elementName element name to use as the key * @param element Doc element to process. - * @param versionPrefix Text to add to the start of the version string. - * @param versionSuffix Text to add to the end of the version string. */ - private static void renderHelpText(String elementName, Doc element, String versionPrefix, String versionSuffix, String absoluteVersion) { + private void renderHelpText(String elementName, Doc element) { // Extract overrides from the doc tags. String name = null; String version = null; diff --git a/settings/helpTemplates/test.html b/settings/helpTemplates/test.html new file mode 100644 index 000000000..6f6c222c4 --- /dev/null +++ b/settings/helpTemplates/test.html @@ -0,0 +1,43 @@ +<#macro argumentlist myargs> + + + + + + + <#list myargs as arg> + + + + + + <#-- + + --> + +
Short nameFull nameDescription
${arg.shortName}${arg.fullName}${arg.doc}
${arg.required}
+ + + + + ${name} documentation + + +

${name}

+

Summary

+ ${summary} +

Version

+ ${version!"unknown version"} + <#if author??> +

Author

+ ${author} + +

Description

+ ${description} +

Arguments

+

Required

<@argumentlist myargs=arguments.required/> +

Optional

<@argumentlist myargs=arguments.optional/> +

Hidden

<@argumentlist myargs=arguments.hidden/> +

Depreciated

<@argumentlist myargs=arguments.depreciated/> + +