/*
* 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.*;
import com.sun.tools.doclets.standard.Standard;
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.
* <p>
* 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 extends Standard {
/**
* 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 out the TOC
out.println("<h2>Picard Metrics Definitions</h2>");
out.println("<section>");
out.println("<p> Click on a metric to see a description of its fields.</p>");
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>");
out.println("<p>Note: Metrics labeled as percentages (with 'percent' in the full metric name or 'PCT' " +
"in the name given in the output file) are actually expressed as fractions. For example, " +
"'PCT_TARGET_BASES_20X = 0.85' should be interpreted as '85 percent of targeted bases are " +
"covered to 20X coverage or more'.</p>");
out.println("</section>");
// Now print out each class
for (final ClassDoc doc : metricsClasses.values()) {
out.println("<a id=\"" + doc.name() + "\"></a>");
out.println("<h2>" + doc.name() + "</h2>");
out.println("<section>");
out.println("<p>" + doc.commentText() + "</p>");
out.println("<table>");
out.println("<tr><th>Field</th><th>Description</th></tr>");
for (final FieldDoc field : doc.fields()) {
if (field.isPublic() && !field.isStatic()) {
out.append("<tr>");
out.append("<td>" + field.name() + "</td>");
out.append("<td>" + field.commentText() + "</td>");
out.append("</tr>");
}
}
out.println("</table>");
out.println("</section>");
}
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("-d") && arg.length == 2) {
try {
return new PrintStream(new File(arg[1], "picard-metric-definitions.html"));
} catch (FileNotFoundException fnfe) {
root.printError("Could not open destination file: " + arg[1]);
fnfe.printStackTrace();
return null;
}
}
}
root.printError("Destination file parameter -d not supplied.");
return null;
}
/**
* 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();
}
}