/*
* The MIT License
*
* Copyright (c) 2009 The Broad Institute
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package picard.util;
import com.sun.javadoc.ClassDoc;
import com.sun.javadoc.Doc;
import com.sun.javadoc.FieldDoc;
import com.sun.javadoc.RootDoc;
import com.sun.javadoc.Tag;
import htsjdk.samtools.metrics.MetricBase;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintStream;
import java.util.SortedMap;
import java.util.TreeMap;
/**
* Doclet for use with JavaDoc that will find all classes extending MetricBase and
* output information about the metrics definitions that go along with the classes.
*
* Takes a single parameter (-f file) to tell it where to output the resulting
* documentation file in HTML format.
*
* @author Tim Fennell
*/
public class MetricsDoclet {
/**
* Entry point called by the javadoc command line tool. Loops over all the
* classes identifying metrics classes and then produces some basic information
* about each in a single HTML file.
*
* @param root the root of the javadoc object hierarchy
* @return true if completed successfully, false otherwise
*/
public static boolean start(final RootDoc root) {
// Build a set of metrics classes sorted by name
final SortedMap<String,ClassDoc> metricsClasses = new TreeMap<String,ClassDoc>();
for (final ClassDoc doc : root.classes()) {
if (isMetricsClass(doc)) {
System.out.println("Processing " + doc.qualifiedTypeName());
metricsClasses.put(doc.typeName(), doc);
}
}
// Get a print stream to write to
final PrintStream out = getOutput(root);
if (out == null) return false;
// Write the headings
out.println("<head>");
out.println(" <title>Picard Metrics Definitions</title>");
out.println(" <style>");
out.println(" body { font-family: Arial; background-color: white; font-size: 10pt; }");
out.println(" h2 { color: red; }");
out.println(" .class_description { font-style: italic; }");
out.println(" .metric_column_def { padding-bottom: 0px; margin-bottom: 8px; }");
out.println(" .metric_column_def span { font-weight: bold; color: #222222; font-style: italic; }");
out.println(" </style>");
out.println("</head>");
out.println("<h1>Picard Metrics Definitions</h1>");
// Write out the TOC
out.println("<h2>Table Of Contents</h2>");
out.println("<ol>");
for (final ClassDoc doc : metricsClasses.values()) {
out.println("<li><a href=\"#" + doc.name() + "\">" + doc.name() + "</a>: " +
firstSentence(doc) + "</li>");
}
out.println("</ol>");
// Now print out each class
for (final ClassDoc doc : metricsClasses.values()) {
out.println("<a id=\"" + doc.name() + "\"");
out.println("<h2>" + doc.name() + "</h2>");
out.println("<p class=\"class_description\">" + doc.commentText() + "</p>");
out.println("<h3>Column Definitions</h3>");
for (final FieldDoc field : doc.fields()) {
if (field.isPublic() && !field.isStatic()) {
out.append("<div class=\"metric_column_def\"><span>" + field.name() + ": </span>");
out.append(field.commentText());
out.println("</div>");
}
}
}
out.close();
return true;
}
/**
* Checks to see if the class extends MetricBase using only the JavaDoc
* metadata provided about the class.
*
* @param doc the ClassDoc representing the class to be tested
* @return true if the class is a metrics class, false otherwise
*/
protected static boolean isMetricsClass(ClassDoc doc) {
final String metricBaseFqn = MetricBase.class.getName();
if (!doc.isClass()) return false;
if (doc.qualifiedTypeName().contains("personal")) return false;
do {
doc = doc.superclass();
if (doc != null && metricBaseFqn.equals(doc.qualifiedTypeName())) return true;
}
while (doc != null);
return false;
}
/**
* Gets the file output parameter from the RootDoc and then opens an
* PrintStream to write to the file.
*/
protected static PrintStream getOutput(final RootDoc root) {
for (final String[] arg : root.options()) {
if (arg[0].equals("-f") && arg.length == 2) {
try {
return new PrintStream(new File(arg[1]));
}
catch (FileNotFoundException fnfe) {
root.printError("Could not open destination file: " + arg[1]);
fnfe.printStackTrace();
return null;
}
}
}
root.printError("Destination file parameter -f not supplied.");
return null;
}
/**
* Required method by the javadoc caller that returns the expected number of elements
* for doclet specific command line arguments.
*/
public static int optionLength(final String option) {
if(option.equals("-f")) {
return 2;
}
return 0;
}
/**
* Takes a Doc object and uses the firstSentenceTags() to recreate the first sentence
* text.
*/
protected static String firstSentence(final Doc doc) {
final Tag[] tags = doc.firstSentenceTags();
final StringBuilder builder = new StringBuilder(128);
for (final Tag tag : tags) {
builder.append(tag.text());
}
return builder.toString();
}
}