/*
* XMLDocumentationHandler.java
*
* Copyright (c) 2002-2015 Alexei Drummond, Andrew Rambaut and Marc Suchard
*
* This file is part of BEAST.
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership and licensing.
*
* BEAST is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* BEAST is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with BEAST; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
package dr.xml;
import dr.app.beast.BeastParser;
import dr.app.tools.BeastParserDoc;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.*;
public class XMLDocumentationHandler {
protected Set<Class> requiredTypes = new TreeSet<Class>(ClassComparator.INSTANCE);
protected BeastParser parser = null;
private final Random random = new Random();
public XMLDocumentationHandler(BeastParser parser) {
this.parser = parser;
Iterator iterator = parser.getParsers();
while (iterator.hasNext()) {
XMLObjectParser xmlparser = (XMLObjectParser) iterator.next();
XMLSyntaxRule[] rules = xmlparser.getSyntaxRules();
if (rules != null) {
for (XMLSyntaxRule rule : rules) {
Set<Class> requiredTypesForRule = rule.getRequiredTypes();
requiredTypes.addAll(requiredTypesForRule);
}
}
}
}
private void printDocXMLTitle(PrintWriter writer, String page) {
writer.println("<head>");
writer.println(" <link rel=\"stylesheet\" href=\"../beast.css\">");
writer.println(" <title>" + page + "</title>");
writer.println("</head>");
writer.println("<h1>" + BeastParserDoc.TITTLE + "</h1>");
Calendar date = Calendar.getInstance();
SimpleDateFormat dateformatter = new SimpleDateFormat("'updated on' d MMMM yyyy zzz");
if (parser.parsers != null) {
if (parser.parsers.equalsIgnoreCase(BeastParser.RELEASE)) {
writer.println("<p>Release Version (" + dateformatter.format(date.getTime()) + ")</p>");
System.out.println("Release Version");
} else if (parser.parsers.equalsIgnoreCase(BeastParser.DEV)) {
writer.println("<p>Development Version (" + dateformatter.format(date.getTime()) + ")</p>");
System.out.println("Development Version");
}
}
writer.println("<!-- " + BeastParserDoc.AUTHORS + " -->");
writer.println("<!-- " + BeastParserDoc.LINK1 + " -->");
writer.println("<!-- " + BeastParserDoc.LINK2 + " -->");
}
public void outputElements(PrintWriter writer) {
writer.println("<html>");
printDocXMLTitle(writer, BeastParserDoc.DETAIL_HTML);
writer.println("<p>");
writer.println("The following is a list of valid elements in a beast file.<br>");
writer.println("<span class=\"required\"> </span> required<br>");
writer.println("<span class=\"optional\"> </span> optional<br>");
writer.println("</p>");
writer.println("\n");
Iterator iterator = parser.getParsers();
while (iterator.hasNext()) {
XMLObjectParser xmlParser = (XMLObjectParser) iterator.next();
writer.println(xmlParser.toHTML(this));
System.out.println(" outputting HTML for element " + xmlParser.getParserName());
}
writer.println("</body>");
writer.println("</html>");
}
/**
* Outputs an example of a particular element, using the syntax information.
* @param writer PrintWriter
* @param parser XMLObjectParser
*/
public void outputExampleXML(PrintWriter writer, XMLObjectParser parser) {
writer.println("<pre>");
if (parser.hasExample()) {
outputHTMLSafeText(writer, parser.getExample());
} else {
outputExampleXML(writer, parser, 0);
}
writer.println("</pre>");
}
public void outputHTMLSafeText(PrintWriter writer, String text) {
for (int i = 0; i < text.length(); i++) {
char c = text.charAt(i);
switch (c) {
case '<':
writer.print("<");
break;
case '>':
writer.print(">");
break;
case '&':
writer.print("&");
break;
default:
writer.print(c);
break;
}
}
}
/**
* Outputs an example of a particular element, using the syntax information.
* @param writer PrintWriter
* @param parser XMLObjectParser
* @param level int
*/
public void outputExampleXML(PrintWriter writer, XMLObjectParser parser, int level) {
outputElementRules(writer, parser.getParserName(), parser.getSyntaxRules(), level);
}
public void stochasticCollectRules(XMLSyntaxRule[] allRules, ArrayList<XMLSyntaxRule> attributeList, ArrayList<ElementRule> elementList) {
if (allRules != null) {
for (XMLSyntaxRule rule : allRules) {
if (rule instanceof AttributeRule) {
attributeList.add(rule);
} else if (rule instanceof ElementRule) {
int min = ((ElementRule) rule).getMin();
int max = Math.max(min, Math.min(5, ((ElementRule) rule).getMax()));
int numRules = min;
if (max != min) numRules = random.nextInt(max - min) + min;
for (int j = 0; j < numRules; j++) {
elementList.add((ElementRule) rule);
}
} else if (rule instanceof XORRule) {
XORRule xorRule = (XORRule) rule;
XMLSyntaxRule[] rules = xorRule.getRules();
int ruleIndex = random.nextInt(rules.length);
stochasticCollectRules(new XMLSyntaxRule[]{rules[ruleIndex]}, attributeList, elementList);
} else if (rule instanceof OrRule) {
OrRule orRule = (OrRule) rule;
XMLSyntaxRule[] rules = orRule.getRules();
int ruleIndex = random.nextInt(rules.length);
stochasticCollectRules(new XMLSyntaxRule[]{rules[ruleIndex]}, attributeList, elementList);
}
}
}
}
/**
* Outputs an example of a rule, using the syntax information.
* @param writer PrintWriter
* @param rule AttributeRule
*/
public void outputExampleXML(PrintWriter writer, AttributeRule rule) { //, int level) {
writer.print(" " + rule.getName() + "=\"");
if (rule.hasExample()) {
writer.print(rule.getExample());
} else {
outputAttributeValue(writer, rule.getAttributeClass());
}
writer.print("\"");
}
/**
* Outputs an example of a rule, using the syntax information.
* @param writer PrintWriter
* @param rule ElementRule
* @param level int
*/
public void outputExampleXML(PrintWriter writer, ElementRule rule, int level) {
if (rule.getElementClass() == null) {
if (rule.getName() == null) System.err.println(rule + " has a null name");
outputElementRules(writer, rule.getName(), rule.getRules(), level);
} else {
if (rule.hasExample()) {
writer.println(spaces(level + 1) + rule.getExample());
} else {
outputExampleXML(writer, rule.getElementClass(), level + 1);
}
}
}
/**
*
* @param writer PrintWriter
* @param name String
* @param rules XMLSyntaxRule[]
* @param level int
*/
public void outputElementRules(PrintWriter writer, String name, XMLSyntaxRule[] rules, int level) {
ArrayList<XMLSyntaxRule> attributeList = new ArrayList<XMLSyntaxRule>();
ArrayList<ElementRule> elementList = new ArrayList<ElementRule>();
stochasticCollectRules(rules, attributeList, elementList);
writer.print(spaces(level) + "<" + name);
// write out the attributes
for (XMLSyntaxRule rule : attributeList) {
outputExampleXML(writer, (AttributeRule) rule); //, level + 1);
}
if (elementList.size() > 0) {
writer.println(">");
// write out the elements
for (ElementRule rule : elementList) {
outputExampleXML(writer, rule, level + 1);
}
writer.println(spaces(level) + "</" + name + ">");
} else {
writer.println("/>");
}
}
public void outputExampleXML(PrintWriter writer, Class c, int level) {
if (c == String.class) {
writer.println(spaces(level) + "foo");
} else if (c == Double.class) {
writer.println(spaces(level) + "1.0");
} else if (c == Integer.class || c == Long.class) {
writer.println(spaces(level) + "1");
} else if (c == Boolean.class) {
writer.println(spaces(level) + "true");
} else if (c == Double[].class) {
writer.println(spaces(level) + "0.5 1.0");
} else if (c == String[].class) {
writer.println(spaces(level) + "foo bar");
} else {
if (c == null) {
throw new RuntimeException("Class is null");
}
XMLObjectParser randomParser = getRandomParser(c);
if (randomParser == null) {
writer.println(spaces(level) + "ERROR!");
} else {
if (level > 1) {
writer.println(spaces(level) + "<" + randomParser.getParserName() +
" idref=\"" + randomParser.getParserName() + (random.nextInt(10) + 1) + "\"/>");
} else {
outputExampleXML(writer, randomParser, level);
}
}
}
}
public void outputAttributeValue(PrintWriter writer, Class c) {
if (c == String.class) {
writer.print("foo");
} else if (c == Double.class) {
writer.print("1.0");
} else if (c == Integer.class || c == Long.class) {
writer.print("1");
} else if (c == Boolean.class) {
writer.print("true");
} else if (c == Double[].class) {
writer.print("0.5 1.0");
} else if (c == Integer[].class) {
writer.print("1 2 4 8");
} else if (c == String[].class) {
writer.print("foo bar");
} else {
throw new RuntimeException("Class " + c + " not allowed as attribute value");
}
}
private String spaces(int level) {
StringBuffer buffer = new StringBuffer("");
for (int i = 0; i < level; i++) {
buffer.append(' ');
}
return buffer.toString();
}
public XMLObjectParser getRandomParser(Class c) {
ArrayList<XMLObjectParser> matchingParsers = getMatchingParsers(c);
if (matchingParsers.size() == 0) return null;
return matchingParsers.get(random.nextInt(matchingParsers.size()));
}
public final ArrayList<XMLObjectParser> getMatchingParsers(Class c) {
ArrayList<XMLObjectParser> matchingParsers = new ArrayList<XMLObjectParser>();
// find all parsers that match this required type
Iterator i = parser.getParsers();
while (i.hasNext()) {
// final XMLObjectParser xmlParser = (XMLObjectParser) i.next();
// final Class returnType = xmlParser.getReturnType();
XMLObjectParser xmlParser = (XMLObjectParser) i.next();
Class returnType = xmlParser.getReturnType();
if (c.isAssignableFrom(returnType)) {
matchingParsers.add(xmlParser);
}
}
return matchingParsers;
}
/**
* Outputs all types that appear as required attributes or elements in an HTML table to the given writer.
* @param writer PrintWriter
*/
public void outputIndex(PrintWriter writer) {
writer.println("<html>");
printDocXMLTitle(writer, BeastParserDoc.INDEX_HTML);
writer.println("<p>");
writer.println("The following is a list of generic types that elements represent in a beast file.<br>");
writer.println("</p>");
// iterate through the types
//Iterator iterator = requiredTypes.iterator();
for (Class requiredType : requiredTypes) {
if (requiredType != Object.class) {
String name = ClassComparator.getName(requiredType);
System.out.println(" outputting HTML for generic type " + name);
// TreeSet<XMLObjectParser> matchingParserNames = new TreeSet<XMLObjectParser>();
ArrayList<String> matchingParserNames = new ArrayList<String>();
// find all parsers that match this required type
Iterator i = parser.getParsers();
while (i.hasNext()) {
XMLObjectParser xmlParser = (XMLObjectParser) i.next();
Class returnType = xmlParser.getReturnType();
if (returnType == null) {
System.out.println("find null Class for parser : " + xmlParser.getParserName());
} else if (requiredType.isAssignableFrom(returnType)) {
if (matchingParserNames.size() == 0) {
writer.println("<div id=\"" + name + "\"><h2>" + name + "</h2>");
writer.println("<p>");
writer.println("Elements of this type include:");
writer.println("</p>");
}
if (!matchingParserNames.contains(xmlParser.getParserName())) {
matchingParserNames.add(xmlParser.getParserName());
// writer.println(xmlParser.toHTML(this));
// writer.println("<div><a href=\"" + BeastParserDoc.INDEX_HTML + "#" + xmlParser.getParserName() + "\"> <"
// + xmlParser.getParserName() + "></a></div>");
writer.println("<div id=\"" + xmlParser.getParserName() + "\" class=\"element\">");
// writer.println(" <div class=\"elementheader\">");
writer.println(" <span class=\"elementname\"><a href=\"" + BeastParserDoc.DETAIL_HTML
+ "#" + xmlParser.getParserName() + "\"> <h3><" + xmlParser.getParserName()
+ "></h3></a></span>");
writer.println(" <div class=\"description\"><b>Description:</b><br>");
writer.println(xmlParser.getParserDescription());
writer.println(" </div>");
writer.println(" </div>");
// // print rules
// if (xmlParser.hasSyntaxRules()) {
// XMLSyntaxRule[] rules = xmlParser.getSyntaxRules();
// writer.println(" <div class=\"rules\"><b>Rule:</b>");
// for (XMLSyntaxRule rule : rules) {
// writer.println(rule.htmlRuleString(this));
// }
// writer.println(" </div>");
// }
//
// // print examples
// if (xmlParser.hasExample()) {
// writer.println("<div class=\"example\"><b>Example:</b>");
// outputExampleXML(writer, xmlParser);
// writer.println("</div>");
// }
writer.println("<p/>");
}
}
}
if (matchingParserNames.size() > 0) writer.println("</div>");
writer.println("<p/>");
// if (matchingParserNames.size() > 1 ||
// (matchingParserNames.size() == 1 && (!matchingParserNames.iterator().next().getParserName().equals(name)))) {
// // output table row containing the type and the matching parser names
// writer.println("<div id=\"" + name + "\"><h2>" + name + "</h2>");
// writer.println("<p>");
// writer.println("Elements of this type include:");
// writer.println("</p>");
// i = matchingParserNames.iterator();
// while (i.hasNext()) {
// XMLObjectParser parser = i.next();
// writer.println("<div><a href=\"" + BeastParserDoc.INDEX_HTML + "#" + parser.getParserName() + "\"> <"
// + parser.getParserName() + "></a></div>");
// writer.println("<div>" + parser.getParserDescription() + "</div>");
// if (parser.hasExample()) writer.println("<div>" + parser.getExample() + "</div>");
// for (XMLSyntaxRule rule: parser.getSyntaxRules()) {
// writer.println("<div>" + rule.ruleString() + "</div>");
// }
// }
// writer.println("</div>");
// }
}
}
writer.println("</body>");
writer.println("</html>");
}
public void outputTypes(PrintWriter writer) {
}
/*
public Set getParsersForClass(Class returnType) {
TreeSet set = new TreeSet();
return set;
}
*/
public String getHTMLForClass(Class c) {
String name = ClassComparator.getName(c);
// return "<A HREF=\"" + BeastParserDoc.DEATAIL_HTML + "#" + name + "\">" + name + "</A>";
return "<A HREF=\"" + BeastParserDoc.INDEX_HTML + "#" + name + "\">" + name + "</A>";
}
/*
class SetHash {
private HashMap table;
public SetHash() { table = new HashMap(); }
public final void put(Object key, XMLObjectParser o) {
Set set = (Set)table.get(key);
if (set != null) {
set.add(o);
} else {
TreeSet newSet = new TreeSet();
newSet.add(o);
table.put(key, newSet);
}
}
public final Set keySet() { return table.keySet(); }
public final Object[] getArray(Object key) { return getSortedSet(key).toArray(); }
public final SortedSet getSortedSet(Object key) { return (SortedSet)table.get(key); }
}*/
/*class XMLObjectParserComparator implements Comparator<XMLObjectParser> {
public int compare(XMLObjectParser c1, XMLObjectParser c2) {
final String name1 = c1.getParserName().toUpperCase();
final String name2 = c2.getParserName().toUpperCase();
return name1.compareTo(name2);
}
}*/
}