diff --git a/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalWalker.java b/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalWalker.java index cd996ff18..730d57da4 100755 --- a/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalWalker.java +++ b/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalWalker.java @@ -38,6 +38,7 @@ import org.broadinstitute.sting.gatk.refdata.rodDbSNP; import org.broadinstitute.sting.gatk.walkers.RodWalker; import org.broadinstitute.sting.playground.utils.report.ReportMarshaller; import org.broadinstitute.sting.playground.utils.report.VE2ReportFactory; +import org.broadinstitute.sting.playground.utils.report.templates.ReportFormat; import org.broadinstitute.sting.playground.utils.report.utils.Node; import org.broadinstitute.sting.utils.classloader.PackageUtils; import org.broadinstitute.sting.utils.StingException; @@ -48,6 +49,7 @@ import org.broadinstitute.sting.utils.text.XReadLines; import java.io.File; import java.io.FileNotFoundException; +import java.io.OutputStreamWriter; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.*; @@ -163,6 +165,8 @@ public class VariantEvalWalker extends RodWalker { @Argument(shortName="reportType", fullName="reportType", doc="If provided, set the template type", required=false) protected VE2ReportFactory.VE2TemplateType reportType = VE2ReportFactory.defaultReportFormat; + @Argument(shortName="reportLocation", fullName="reportLocation", doc="If provided, set the base file for reports (Required for output formats with more than one file per analysis)", required=false) + protected File outputLocation = null; Set rsIDsToExclude = null; @@ -248,6 +252,9 @@ public class VariantEvalWalker extends RodWalker { // -------------------------------------------------------------------------------------------------------------- public void initialize() { + ReportFormat.AcceptableOutputType type = (outputLocation == null) ? ReportFormat.AcceptableOutputType.STREAM : ReportFormat.AcceptableOutputType.FILE; + if (!VE2ReportFactory.isCompatibleWithOutputType(type,reportType)) + throw new StingException("The report format requested is not compatible with your output location. You specified a " + type + " output type which isn't an option for " + reportType); if ( LIST ) listModulesAndExit(); @@ -289,8 +296,7 @@ public class VariantEvalWalker extends RodWalker { private void listModulesAndExit() { List> veClasses = PackageUtils.getClassesImplementingInterface(VariantEvaluator.class); out.println("\nAvailable eval modules:"); - for (int i = 0; i < veClasses.size(); i++) - out.println("\t" + veClasses.get(i).getSimpleName()); + for (Class veClass : veClasses) out.println("\t" + veClass.getSimpleName()); out.println(); System.exit(0); } @@ -643,7 +649,14 @@ public class VariantEvalWalker extends RodWalker { } public void onTraversalDone(Integer result) { - ReportMarshaller marshaller = VE2ReportFactory.createMarhsaller(out,reportType,createExtraOutputTags()); + // our report mashaller + ReportMarshaller marshaller; + + // create the report marshaller early, so that we can fail-fast if something is wrong with the output sources + if (outputLocation == null) + marshaller = VE2ReportFactory.createMarhsaller(new OutputStreamWriter(out), reportType, createExtraOutputTags()); + else + marshaller = VE2ReportFactory.createMarhsaller(outputLocation, reportType, createExtraOutputTags()); for ( String evalName : variantEvaluationNames ) { for ( EvaluationContext group : contexts ) { VariantEvaluator eval = getEvalByName(evalName, group.evaluations); @@ -662,7 +675,7 @@ public class VariantEvalWalker extends RodWalker { * @return a list of nodes to attach to the report as tags */ private List createExtraOutputTags() { - List list = new ArrayList(); + List list = new ArrayList(); list.add(new Node("reference file",getToolkit().getArguments().referenceFile.getName(),"The reference sequence file")); for (String binding : getToolkit().getArguments().RODBindings) list.add(new Node("ROD binding",binding,"The reference sequence file")); diff --git a/java/src/org/broadinstitute/sting/playground/utils/report/ReportMarshaller.java b/java/src/org/broadinstitute/sting/playground/utils/report/ReportMarshaller.java index c52dc4421..de872109a 100644 --- a/java/src/org/broadinstitute/sting/playground/utils/report/ReportMarshaller.java +++ b/java/src/org/broadinstitute/sting/playground/utils/report/ReportMarshaller.java @@ -47,21 +47,18 @@ public class ReportMarshaller { // the aggregation of all our analyses private Node root; - private Writer writeLocation; - + private File outputFileLocation; + private Writer outputWriter; /** * create a marshaled object * * @param reportName the report name * @param template the template to use */ - public ReportMarshaller(String reportName, File filename, ReportFormat template) { - try { - init(reportName, new OutputStreamWriter(new FileOutputStream(filename))); - } catch (FileNotFoundException e) { - throw new StingException("Unable to create Writer from file " + filename,e); - } + public ReportMarshaller(String reportName, File filename, ReportFormat template, List reportTags) { temp = template; + this.outputFileLocation = filename; + createRootNode(reportName, reportTags); } /** @@ -70,37 +67,23 @@ public class ReportMarshaller { * @param reportName the report name */ public ReportMarshaller(String reportName, Writer writer, ReportFormat template, List reportTags) { - init(reportName, writer); + this.outputWriter = writer; temp = template; - for (Node n : reportTags) { - n.setTag(); - root.addChild(n); - } + createRootNode(reportName, reportTags); } /** - * create a marshaled object - * + * create the root node * @param reportName the report name + * @param reportTags the report type */ - public ReportMarshaller(String reportName, OutputStream writer, ReportFormat template, List reportTags) { - init(reportName, new PrintWriter(writer)); - temp = template; - for (Node n : reportTags) { - n.setTag(); - root.addChild(n); - } - } - - /** - * initialize the ReportMarshaller - * @param reportName the report name - * @param writer the output writer - */ - private void init(String reportName, Writer writer) { + private void createRootNode(String reportName, List reportTags) { root = new Node("report", reportName, DateFormat.getDateTimeInstance().format(new Date())); root.addChild(new Node("title", reportName, "title of the report")); - this.writeLocation = writer; + for (Node n : reportTags) { + n.setTag(); + root.addChild(n); + } } /** @@ -190,13 +173,9 @@ public class ReportMarshaller { * call the method to finalize the report */ public void close() { - try { - temp.write(writeLocation, root); - writeLocation.flush(); - writeLocation.close(); - } catch (IOException e) { - throw new StingException("IO exception", e); - } + if (outputFileLocation != null) temp.write(outputFileLocation, root); + else temp.write(outputWriter, root); + temp.close(); } /** diff --git a/java/src/org/broadinstitute/sting/playground/utils/report/VE2ReportFactory.java b/java/src/org/broadinstitute/sting/playground/utils/report/VE2ReportFactory.java index ac810b228..f16e37195 100644 --- a/java/src/org/broadinstitute/sting/playground/utils/report/VE2ReportFactory.java +++ b/java/src/org/broadinstitute/sting/playground/utils/report/VE2ReportFactory.java @@ -38,15 +38,42 @@ public class VE2ReportFactory { /** * create a report ReportMarshaller from a writer, type, and any report tags - * @param writer the output object + * @param writeTo the output location * @param type the VE2TemplateType type * @param reportTags the tags to append to each report root node * @return a list of ReportMarshallers to write data to */ - public static ReportMarshaller createMarhsaller(OutputStream writer,VE2TemplateType type, List reportTags) { + public static ReportMarshaller createMarhsaller(File writeTo,VE2TemplateType type, List reportTags) { + if (!isCompatibleWithOutputType(ReportFormat.AcceptableOutputType.FILE,type)) + throw new IllegalArgumentException("Report format " + type + " does not support an output parameter of type " + ReportFormat.AcceptableOutputType.FILE); + return new ReportMarshaller("Variant Eval 2 Report",writeTo,createByType(type.underlyingReportType),reportTags); + } + + /** + * create a report ReportMarshaller from a writer, type, and any report tags + * + * @param writer the output object + * @param type the VE2TemplateType type + * @param reportTags the tags to append to each report root node + * + * @return a list of ReportMarshallers to write data to + */ + public static ReportMarshaller createMarhsaller(Writer writer, VE2TemplateType type, List reportTags) { + if (!isCompatibleWithOutputType(ReportFormat.AcceptableOutputType.STREAM,type)) + throw new IllegalArgumentException("Report format " + type + " does not support an output parameter of type " + ReportFormat.AcceptableOutputType.STREAM); return new ReportMarshaller("Variant Eval 2 Report",writer,createByType(type.underlyingReportType),reportTags); } + /** + * check that the proposed output type + * @param output the output type we're proposing + * @param type the report format we'd like to use + */ + public static boolean isCompatibleWithOutputType( ReportFormat.AcceptableOutputType output, VE2TemplateType type) { + ReportFormat format = createByType(type.underlyingReportType); + return (format.getAcceptableOutputTypes().contains(output)); + } + /** * create a report formatter with the given type * diff --git a/java/src/org/broadinstitute/sting/playground/utils/report/templates/CSVFormat.java b/java/src/org/broadinstitute/sting/playground/utils/report/templates/CSVFormat.java index 9689b623a..f732c5b6f 100644 --- a/java/src/org/broadinstitute/sting/playground/utils/report/templates/CSVFormat.java +++ b/java/src/org/broadinstitute/sting/playground/utils/report/templates/CSVFormat.java @@ -1,11 +1,11 @@ package org.broadinstitute.sting.playground.utils.report.templates; /** - * the basic comma seperated value format + * the basic comma separated value format */ public class CSVFormat extends TableBasedFormat { - private final String DIVIDER = ","; - + private static final String DIVIDER = ","; + private static final String extension = ".csv"; /** * format the string according to our internal rules * @@ -18,12 +18,42 @@ public class CSVFormat extends TableBasedFormat { } /** - * does the output format want to display line breaks (dotted lines)? + * should we add readability marks? * - * @return true if the format uses them + * @return true if we should (line breaks, etc) */ @Override - public boolean displayDashedLineBreaks() { + public boolean addReadabilityMarks() { return false; } + + /** + * a string to prepend for header lines + * + * @return a string, blank if no string to be appended + */ + @Override + public String headerIndicator() { + return "#"; + } + + /** + * should we split the separate files by analysis + * + * @return + */ + @Override + public boolean splitFilesByAnalysis() { + return false; + } + + /** + * what extension do we want our files to have + * + * @return a string of the extension + */ + @Override + public String extension() { + return extension; + } } diff --git a/java/src/org/broadinstitute/sting/playground/utils/report/templates/GrepFormat.java b/java/src/org/broadinstitute/sting/playground/utils/report/templates/GrepFormat.java index 48ff70612..a85f8fcde 100644 --- a/java/src/org/broadinstitute/sting/playground/utils/report/templates/GrepFormat.java +++ b/java/src/org/broadinstitute/sting/playground/utils/report/templates/GrepFormat.java @@ -1,9 +1,13 @@ package org.broadinstitute.sting.playground.utils.report.templates; import org.broadinstitute.sting.playground.utils.report.utils.Node; +import org.broadinstitute.sting.utils.StingException; +import java.io.File; +import java.io.FileNotFoundException; import java.io.PrintWriter; import java.io.Writer; +import java.util.EnumSet; /** @@ -19,12 +23,36 @@ public class GrepFormat implements ReportFormat { /** * write out to the writer, given the root node - * @param baseFile the writer to write to + * @param baseFile the file to write to + * @param baseNode the root node + */ + @Override + public void write(File baseFile, Node baseNode) { + try { + stream = new PrintWriter(baseFile); + } catch (FileNotFoundException e) { + throw new StingException("Unable to write to file " + baseFile, e); + } + privateWrite(baseNode); + } + + /** + * write out to the writer, given the root node + * + * @param baseFile the file to write to * @param baseNode the root node */ @Override public void write(Writer baseFile, Node baseNode) { stream = new PrintWriter(baseFile); + privateWrite(baseNode); + } + + /** + * write out the node + * @param baseNode the base (root) node + */ + private void privateWrite(Node baseNode) { for (Node analysis : baseNode.getChildren()) { StringBuilder builder = new StringBuilder(); boolean first = true; @@ -55,4 +83,19 @@ public class GrepFormat implements ReportFormat { recursiveTraverse(child,value + ".[" + nString + "]"); } } + + @Override + public void close() { + stream.close(); + } + + /** + * return the valid outputs we support + * @return + */ + public EnumSet getAcceptableOutputTypes() { + EnumSet set = EnumSet.of(AcceptableOutputType.FILE); // always acceptable + set.add(AcceptableOutputType.STREAM); + return set; + } } diff --git a/java/src/org/broadinstitute/sting/playground/utils/report/templates/RFormat.java b/java/src/org/broadinstitute/sting/playground/utils/report/templates/RFormat.java index 4d405dfb4..99129a19c 100644 --- a/java/src/org/broadinstitute/sting/playground/utils/report/templates/RFormat.java +++ b/java/src/org/broadinstitute/sting/playground/utils/report/templates/RFormat.java @@ -18,206 +18,58 @@ import java.util.Map; * * a format for outputting R data - experimental */ -public class RFormat implements ReportFormat { - private Map> analyses = new HashMap>(); - private PrintWriter stream; - - // some reused R table strings - private String tempTableName = "temp"; - private String toTableString = " <- as.table(temp)"; - private String tableString = tempTableName + " <- matrix(c("; - private String rowNamesString = "rownames(" + tempTableName + ") <- c("; - private String colNamesString = "colnames(" + tempTableName + ") <- c("; +public class RFormat extends TableBasedFormat { + private final String DIVIDER = ","; + private static final String extension = ".csv"; /** - * write the analyses out - * @param writeTo the writer to write to - * @param baseNode the base node, containing the analyses + * format the string according to our internal rules + * + * @param str the string to format + * @return a string, properly formatted */ @Override - public void write(Writer writeTo, Node baseNode) { - getAnalyses(baseNode); - stream = new PrintWriter(writeTo); - for (String s : analyses.keySet()) { - writeAnalysis(analyses.get(s)); - outputTables(analyses.get(s)); - } + public String formatColumn(String str) { + return str+DIVIDER; } /** - * break out the analyses by type, given the base node + * should we add readability marks? * - * @param baseNode the root node + * @return true if we should (line breaks, etc) */ - private void getAnalyses(Node baseNode) { - for (Node n : baseNode.getChildren()) - if (!n.tag && n.getComplex()) { - if (!analyses.containsKey(n.getValue())) - analyses.put(n.getValue(), new ArrayList()); - analyses.get(n.getValue()).add(n); - } + @Override + public boolean addReadabilityMarks() { + return false; } /** - * write the analysis nodes out, only outputting the simple data points (non-table data) + * a string to prepend for header lines * - * @param nodes a list of nodes, of the same analysis type + * @return a string, blank if no string to be appended */ - private void writeAnalysis(List nodes) { - if (nodes == null || nodes.size() < 1 || !nodes.get(0).getName().equals("analysis")) return; - Node forTitle = nodes.get(0); - - String header = extractHeaderString(forTitle); - if (header == null) return; // a null here indicates we don't have any unique columns to display - - StringBuilder rowNameBuilder = new StringBuilder(); - StringBuilder rowValueBuilder = new StringBuilder(); - for (Node analysis : nodes) { - String dataString = dataPointNodesToValues(analysis); - if (dataString.length() > 0 && !dataString.equals(",")) { - rowNameBuilder.append("\""+getTagValues(analysis).substring(0,getTagValues(analysis).length() - 1)+"\","); - rowValueBuilder.append(dataString); - } - } - stream.print(tableString + rowValueBuilder.toString().substring(0, rowValueBuilder.toString().length() - 1) + "),"); - stream.println("ncol=" + header.split(",").length + ")"); - stream.println(rowNamesString + escapeString(rowNameBuilder.toString().substring(0, rowNameBuilder.toString().length() - 1)) + ")"); - stream.println(colNamesString + escapeString(header.substring(0, header.length() - 1)) + ")"); - - stream.println(forTitle.getValue().replace(" ", "_").replace("/","_to_") + toTableString); - stream.println(); + @Override + public String headerIndicator() { + return "#"; } /** - * output the tables: look at list of analysis nodes (all from the same analysis) and output the table + * should we split the seperate files by analysis * - * @param nodes the list of analysis nodes (of the same underlying type) + * @return */ - void outputTables(List nodes) { - Map tableRowValues = new HashMap(); - Map tableRowNames = new HashMap(); - Map tableHeaders = new HashMap(); - Map columnSizes = new HashMap(); - for (Node analysis : nodes) - - for (Node n : analysis.getChildren()) { - if (n.table) { - StringBuilder columnNamesBuilder = new StringBuilder(); - StringBuilder rowValueBuilder = new StringBuilder(); - StringBuilder rowNamesBuilder = new StringBuilder(); - - for (Node row : n.getChildren()) { - int colSize = 0; - rowNamesBuilder.append("\""+getTagValues(analysis)); - rowNamesBuilder.append(row.getValue()+"\","); - for (Node column : row.getChildren()) { - columnNamesBuilder.append("\""+column.getValue()+"\","); - if (column.getChildren().size() == 1) { - String value = column.getChildren().iterator().next().getValue()+","; - colSize++; - rowValueBuilder.append(value); - } - } - if (!columnSizes.containsKey(n.getValue())) columnSizes.put(n.getValue(),colSize); - if (!tableHeaders.containsKey(n.getValue())) { - tableHeaders.put(n.getValue(), columnNamesBuilder.toString()); - } - - } - tableRowValues.put(n.getValue(),rowValueBuilder.toString()); - tableRowNames.put(n.getValue(),rowNamesBuilder.toString()); - } - } - - // output the tables - for (String tableName : tableHeaders.keySet()) { - stream.print(tableString + tableRowValues.get(tableName).substring(0,tableRowValues.get(tableName).length()-1) + "),"); - stream.println("ncol="+columnSizes.get(tableName)+")"); - stream.println(rowNamesString + escapeString(tableRowNames.get(tableName).substring(0,tableRowNames.get(tableName).length()-1)) + ")"); - stream.println(colNamesString + escapeString(tableHeaders.get(tableName).substring(0,tableHeaders.get(tableName).length()-1)) + ")"); - - stream.println(tableName.replace(" ","_") + toTableString); - stream.println(); - } + @Override + public boolean splitFilesByAnalysis() { + return true; } /** - * get the header (tag) names + * what extension do we want our files to have * - * @param analysis the analysis node - * - * @return a string representing the tag names + * @return a string of the extension */ - private String getTagValues(Node analysis) { - StringBuilder buffer = new StringBuilder(); - for (Node s : analysis.getChildren()) - if (s.tag) buffer.append(s.getValue()+"_"); - return buffer.toString(); + @Override + public String extension() { + return extension; } - - /** - * escape any special characters for R - * @param str the string to check - * @return the escaped string - */ - String escapeString(String str) { - if (str.contains("/")) str = str.replace("/","\\/"); - return str; - } - - /** - * convert the list of nodes to a string - * - * @param analysis the analysis - * - * @return a String representing the values - */ - private String dataPointNodesToValues(Node analysis) { - StringBuilder builder = new StringBuilder(); - for (Node n : analysis.getChildren()) { - if (!n.tag && !n.table) { - if (n.getChildren().size() > 1) - throw new IllegalStateException("Simple data points shouldn't have more than one value"); - if (n.getChildren().size() == 1) - builder.append(formatColumn(n.getChildren().iterator().next().getValue())); - } - } - return builder.toString(); - } - - /** extract the header string from the base analysis node */ - private String extractHeaderString(Node analysisNode) { - StringBuilder buffer = new StringBuilder(); - // first get the tags - if (!getColumnNames(analysisNode, buffer)) - return null; - - return buffer.toString(); - } - - /** - * get the column names from the analysis node - * - * @param analysisNode the node - * @param buffer the buffer to append to - * - * @return true if there was data fields to output, false if we don't add data to the column header list - */ - private boolean getColumnNames(Node analysisNode, StringBuilder buffer) { - // now get the simple data points - boolean addedValue = false; - for (Node n : analysisNode.getChildren()) - if (!n.tag && !n.table) { - addedValue = true; - buffer.append(formatColumn(n.getValue())); - } - return addedValue; - } - - /** - * format a column string - * @param row the column - * @return the reformatted string - */ - public String formatColumn(String row) { return "\"" + row + "\",";} } diff --git a/java/src/org/broadinstitute/sting/playground/utils/report/templates/ReportFormat.java b/java/src/org/broadinstitute/sting/playground/utils/report/templates/ReportFormat.java index 113d22a52..6427a349b 100644 --- a/java/src/org/broadinstitute/sting/playground/utils/report/templates/ReportFormat.java +++ b/java/src/org/broadinstitute/sting/playground/utils/report/templates/ReportFormat.java @@ -4,6 +4,7 @@ import org.broadinstitute.sting.playground.utils.report.utils.Node; import java.io.File; import java.io.Writer; +import java.util.EnumSet; /** * @author aaron @@ -13,5 +14,9 @@ import java.io.Writer; * The basics of a report formatter */ public interface ReportFormat { - public void write(Writer baseFile, Node baseNode); + public enum AcceptableOutputType { STREAM, FILE }; + public EnumSet getAcceptableOutputTypes(); + public void write(File fileLocation, Node baseNode); + public void write(Writer writeLocation, Node baseNode); + public void close(); } diff --git a/java/src/org/broadinstitute/sting/playground/utils/report/templates/TableBasedFormat.java b/java/src/org/broadinstitute/sting/playground/utils/report/templates/TableBasedFormat.java index 17ae422e5..c94c5a706 100644 --- a/java/src/org/broadinstitute/sting/playground/utils/report/templates/TableBasedFormat.java +++ b/java/src/org/broadinstitute/sting/playground/utils/report/templates/TableBasedFormat.java @@ -1,13 +1,10 @@ package org.broadinstitute.sting.playground.utils.report.templates; import org.broadinstitute.sting.playground.utils.report.utils.Node; +import org.broadinstitute.sting.utils.StingException; -import java.io.PrintWriter; -import java.io.Writer; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.io.*; +import java.util.*; /** * an abstract class to share the basics of a table based format; many methods @@ -16,11 +13,42 @@ import java.util.Map; public abstract class TableBasedFormat implements ReportFormat { private Map> analyses = new HashMap>(); private PrintWriter stream; - + private File baseLocation; + + /** + * write the base node to the specified writer + * @param writeTo the file base to write to + * @param baseNode the root node + */ @Override - public void write(Writer writeTo, Node baseNode) { + public void write(File writeTo, Node baseNode) { + baseLocation = writeTo; + + // if there is only a single output file, create it + if (!splitFilesByAnalysis()) newStream(""); + + traverseAnalysisNodes(baseNode); + } + + /** + * write the base node to the specified writer + * @param writeLocation the writer to write to + * @param baseNode the root node + */ + public void write(Writer writeLocation, Node baseNode) { + if (splitFilesByAnalysis()) throw new StingException("Unable to write output report, we require a file input for multi-file formats"); + // if there is only a single output file, create it + stream = new PrintWriter(writeLocation); + traverseAnalysisNodes(baseNode); + stream.flush(); + } + + /** + * traverse the analysis nodes, outputting to our stream + * @param baseNode the base (root) node, with analysis nodes as children + */ + private void traverseAnalysisNodes(Node baseNode) { getAnalyses(baseNode); - stream = new PrintWriter(writeTo); for (String s : analyses.keySet()) { writeAnalysis(analyses.get(s)); outputTables(analyses.get(s)); @@ -47,24 +75,24 @@ public abstract class TableBasedFormat implements ReportFormat { private void writeAnalysis(List nodes) { if (nodes.size() < 1 || !nodes.get(0).getName().equals("analysis")) return; Node forTitle = nodes.get(0); - stream.println(niceDivider(80)); - stream.println("Analysis Name: \t" + forTitle.getValue()); - stream.println("Analysis Description: \t" + forTitle.getDescription()); - stream.println(); + newStream(forTitle.getValue()); + stream.println(headerIndicator() + "Analysis Name: \t" + forTitle.getValue()); + stream.println(headerIndicator() + "Analysis Description: \t" + forTitle.getDescription()); + if (addReadabilityMarks()) stream.println(); String header = extractHeaderString(forTitle); if (header == null) return; // a null here indicates we don't have any unique columns to display - stream.println(header); - stream.println(niceDivider(header.length())); + stream.println(trimLastChar(header)); + if (addReadabilityMarks()) stream.println(niceDivider(header.length())); for (Node analysis : nodes) { String dataString = dataPointNodesToValues(analysis); if (dataString.length() > 0 && !dataString.equals("")) { stream.print(getTagValues(analysis)); - stream.println(dataString); + stream.println(trimLastChar(dataString)); } } - stream.println(); + if (addReadabilityMarks()) stream.println(); stream.println(); } @@ -104,17 +132,21 @@ public abstract class TableBasedFormat implements ReportFormat { // output the tables for (String tableName : tableHeaders.keySet()) { - stream.println("Table Name : " + tableName); - stream.println(); - stream.println(tableHeaders.get(tableName)); - stream.println(niceDivider(tableHeaders.get(tableName).length())); + newStream(tableName); + stream.println(headerIndicator() + "Table Name : " + tableName); + stream.println(trimLastChar(tableHeaders.get(tableName))); + if (addReadabilityMarks()) stream.println(niceDivider(tableHeaders.get(tableName).length())); List rows = tableRows.get(tableName); for (String row : rows) - stream.println(row); - stream.println(); + stream.println(trimLastChar(row)); + if (addReadabilityMarks()) stream.println(); } } + public String trimLastChar(String toTrim) { + return toTrim.substring(0,toTrim.length()-1); + } + /** * get the header (tag) names * @param analysis the analysis node @@ -185,18 +217,49 @@ public abstract class TableBasedFormat implements ReportFormat { if (n.tag) buffer.append(formatColumn(n.getName())); } + /** + * this function checks whether we need to create a new stream for the specified analysis + */ + public void newStream(String analysisOrTableName) { + String name = analysisOrTableName.replaceAll("\\s+","_").replaceAll("\\/","_slash_"); + if (stream == null || splitFilesByAnalysis()) { + if (stream != null) stream.close(); + try { + stream = new PrintWriter(this.baseLocation + "." + name + this.extension()); + } catch (FileNotFoundException e) { + throw new StingException("Unable to create Report file at location " + this.baseLocation + "." + name + this.extension(), e); + } + } + } + + /** + * return the valid outputs we support + * @return + */ + public EnumSet getAcceptableOutputTypes() { + EnumSet set = EnumSet.of(AcceptableOutputType.FILE); // always acceptable + if (!splitFilesByAnalysis()) set.add(AcceptableOutputType.STREAM); + return set; + } + /** * create a correct-length divider string * @param length the length for the divider * @return a string with the divider text of length "length" */ private String niceDivider(int length) { - if (!displayDashedLineBreaks()) return ""; StringBuilder builder = new StringBuilder(); for (int x = 0; x < length; x++) builder.append("-"); return builder.toString(); } + /** + * close the output file, if open + */ + public void close() { + if (stream != null) stream.close(); + } + /** * format the string according to our internal rules * @param str the string to format @@ -205,8 +268,27 @@ public abstract class TableBasedFormat implements ReportFormat { public abstract String formatColumn(String str); /** - * does the output format want to display line breaks (dotted lines)? - * @return true if the format uses them + * should we add readability marks? + * @return true if we should (line breaks, etc) */ - public abstract boolean displayDashedLineBreaks(); + public abstract boolean addReadabilityMarks(); + + + /** + * a string to prepend for header lines + * @return a string, blank if no string to be appended + */ + public abstract String headerIndicator(); + + /** + * should we split the separate files by analysis + * @return + */ + public abstract boolean splitFilesByAnalysis(); + + /** + * what extension do we want our files to have + * @return a string of the extension + */ + public abstract String extension(); } diff --git a/java/src/org/broadinstitute/sting/playground/utils/report/templates/TableFormat.java b/java/src/org/broadinstitute/sting/playground/utils/report/templates/TableFormat.java index a29b32e82..1067058f9 100644 --- a/java/src/org/broadinstitute/sting/playground/utils/report/templates/TableFormat.java +++ b/java/src/org/broadinstitute/sting/playground/utils/report/templates/TableFormat.java @@ -36,7 +36,8 @@ package org.broadinstitute.sting.playground.utils.report.templates; */ public class TableFormat extends TableBasedFormat { private static final int COLUMN_WIDTH = 25; - + private static final String TBL = "tbl"; + /** * format the string according to our internal rules * @@ -49,13 +50,43 @@ public class TableFormat extends TableBasedFormat { } /** - * does the output format want to display line breaks (dotted lines)? + * should we add readability marks? * - * @return true if the format uses them + * @return true if we should (line breaks, etc) */ @Override - public boolean displayDashedLineBreaks() { + public boolean addReadabilityMarks() { return true; } + + /** + * a string to prepend for header lines + * + * @return a string, blank if no string to be appended + */ + @Override + public String headerIndicator() { + return ""; + } + + /** + * should we split the seperate files by analysis + * + * @return + */ + @Override + public boolean splitFilesByAnalysis() { + return false; + } + + /** + * what extension do we want our files to have + * + * @return a string of the extension + */ + @Override + public String extension() { + return TBL; + } }