/*
* RapidMiner
*
* Copyright (C) 2001-2011 by Rapid-I and the contributors
*
* Complete list of developers available at our web site:
*
* http://rapid-i.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
package com.rapidminer.gui;
/*
* RapidMiner
*
* Copyright (C) 2001-2010 by Rapid-I and the contributors
*
* Complete list of developers available at our web site:
*
* http://rapid-i.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.apache.commons.lang.StringUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import com.rapidminer.gui.tools.SwingTools;
import com.rapidminer.operator.Operator;
import com.rapidminer.operator.OperatorCreationException;
import com.rapidminer.operator.OperatorDescription;
import com.rapidminer.operator.ports.Port;
import com.rapidminer.operator.ports.Ports;
import com.rapidminer.parameter.ParameterType;
import com.rapidminer.parameter.Parameters;
import com.rapidminer.parameter.conditions.ParameterCondition;
import com.rapidminer.tools.LogService;
import com.rapidminer.tools.Tools;
import com.rapidminer.tools.documentation.ExampleProcess;
import com.rapidminer.tools.xml.XHTMLEntityResolver;
/**
* This class loads the operator's descriptions either live from the internet wiki or from the resources. The latter
* requires that a custom Bot which gets all operator description sites from the MediaWiki was executed during build
* time. Those html files retrieved there for an operator must have been parsed and saved in the resources folder in
* "doc/namespace/operatorname". If the user does not have an internet connection all operators are loaded from this
* resource. If the user does have an internet connection the operators are loaded directly from the RapidWiki site. The
* operator description is shown in the RapidMiner Help Window.
*
* @author Miguel Buescher, Sebastian Land
*
*/
public class OperatorDocLoader {
private static final long serialVersionUID = 1L;
//private static final String HOST_NAME = "http://www.rapid-i.com";
private static final String WIKI_PREFIX_FOR_IMAGES = "http://www.rapid-i.com";;
private static final String WIKI_PREFIX_FOR_OPERATORS = "http://rapid-i.com/wiki/index.php?title=";
private static final Logger logger = Logger.getLogger(OperatorDocLoader.class.getName());
private static String CORRECT_HTML_STRING_DIRTY = "<html xmlns=\"http://www.w3.org/1999/xhtml\" dir=\"ltr\" lang=\"en\">";
private static String CURRENT_OPERATOR_NAME_READ_FROM_RAPIDWIKI;
private static String ERROR_TEXT_FOR_WIKI;
private static String ERROR_TEXT_FOR_LOCAL;
private static final String RESOURCE_SUB_DIR = "com/rapidminer/resources/doc";
private static HashMap<OperatorDescription, String> OPERATOR_CACHE_MAP = new HashMap<OperatorDescription, String>();
static {
// TODO: Transfer this to resource
ERROR_TEXT_FOR_WIKI = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">" + "<html xmlns=\"http://www.w3.org/1999/xhtml\" dir=\"ltr\" lang=\"en\" xml:lang=\"en\">" + "<head>" + "<table cellpadding=0 cellspacing=0>" + "<tr><td>" + "<img src=\""
+ SwingTools.getIconPath("48/bug_yellow_error.png") + "\"/>" + "</td>" + "<td width=\"5\">" + "</td>" + "<td>" + "Could not retrieve documentation of selected operator from wiki." + "</td></tr>" + "</table>" + "</head>" + "</html>";
ERROR_TEXT_FOR_LOCAL = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">" + "<html xmlns=\"http://www.w3.org/1999/xhtml\" dir=\"ltr\" lang=\"en\" xml:lang=\"en\">" + "<head>" + "<table cellpadding=0 cellspacing=0>" + "<tr><td>" + "<img src=\""
+ SwingTools.getIconPath("48/bug_yellow_error.png") + "\"/>" + "</td>" + "<td width=\"5\">" + "</td>" + "<td>" + "Selected Operator not documented locally. Try online version." + "</td></tr>" + "</table>" + "</head>" + "</html>";
}
/**
* This is the method for loading an operator's documentation within the program.
*/
public static String loadOperatorDocumentation(final boolean online, final boolean activateCache, final OperatorDescription dirtyOpDesc) {
String toShowText;
OperatorDescription opDesc = dirtyOpDesc;
if (opDesc == null) {
// TODO: Eliminate this case
toShowText = ERROR_TEXT_FOR_WIKI;
} else {
if (activateCache && OPERATOR_CACHE_MAP.containsKey(opDesc)) {
return OPERATOR_CACHE_MAP.get(opDesc);
} else {
try {
if (online) {
toShowText = loadSelectedOperatorDocuFromWiki(opDesc);
} else {
toShowText = loadSelectedOperatorDocuLocally(opDesc);
}
} catch (Exception e) {
SwingTools.showFinalErrorMessage("rapid_doc_bot_importer_showInBrowser", e, true, e.getMessage());
toShowText = ERROR_TEXT_FOR_WIKI;
}
if (activateCache && StringUtils.isNotBlank(toShowText) && StringUtils.isNotEmpty(toShowText)) {
OPERATOR_CACHE_MAP.put(opDesc, toShowText);
}
}
}
return toShowText;
}
public static void clearOperatorCache() {
OPERATOR_CACHE_MAP.clear();
}
public static boolean hasCache(OperatorDescription opDesc) {
return OPERATOR_CACHE_MAP.containsKey(opDesc);
}
private static String customizeHTMLStringDirty(String HTMLString, String operatorIconPath) {
HTMLString = HTMLString.replaceFirst("\\<[^\\>]*>", "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">");
// customize operator-name
HTMLString = HTMLString.replaceFirst(CORRECT_HTML_STRING_DIRTY, "<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"en\" dir=\"ltr\">" + "<head>" + "<table cellpadding=0 cellspacing=0>" + "<tr><td>" + "<img src=\"" + operatorIconPath + "\" /></td>" + "<td width=\"5\">" + "</td>" + "<td>" + "<h2 class=\"firstHeading\" id=\"firstHeading\">"
+ CURRENT_OPERATOR_NAME_READ_FROM_RAPIDWIKI + "</h2>" + "</td></tr>" + "</table>" + "<hr noshade=\"true\">" + "</head>");
// customize all headlines
HTMLString = HTMLString.replaceAll("<h2>", "<h4>");
HTMLString = HTMLString.replaceAll("</h2>", "</h4>");
// replace all painted circles by icons
HTMLString = HTMLString.replaceAll("<ul>", "<ul class=\"ports\">");
// removing all <div class="visualClear"/>
HTMLString = HTMLString.replaceAll("<div class=\"visualClear\"/>", "");
// regex: \[theTag\][\w\s]*\[/theTag\]
Pattern pattern = Pattern.compile("\\</div\\>[\\s]*\\<h4\\>|" + "\\</p\\>[\\s]*\\<h4\\>|" + "\\</h4\\>[\\s]*\\<h4\\>|" + "\\</ul\\>[\\s]*\\<h4\\>|" + "\\</h4\\>[\\s]*\\</div\\>");
Matcher matcher = pattern.matcher(HTMLString);
while (matcher.find()) {
String match = matcher.group();
String replaceString = StringUtils.EMPTY;
if (match.startsWith("</div")) {
replaceString = "</div><br/><h4>";
} else if (match.startsWith("</p")) {
replaceString = "</p><br/><h4>";
} else if (match.startsWith("</h4") && !match.contains("div")) {
replaceString = "</h4><br/><h4>";
} else if (match.startsWith("</ul")) {
replaceString = "</ul><br/><h4>";
} else if (match.startsWith("</h4") && match.contains("div")) {
replaceString = "</h4><br/><div>";
}
HTMLString = HTMLString.replace(match, replaceString);
}
// replace <pre...> with <table...> because pre is not supported
HTMLString = HTMLString.replaceAll("<pre", "<table class=pre border=0 bordercolor=black style=border-style:dashed;");
// width=\"100%\"
// border-collapse:separate;
HTMLString = HTMLString.replaceAll("</pre", "</table");
return HTMLString;
}
/**
*
* @param operatorWikiName
* @param opDesc
* @return The parsed <tt>Document</tt> (not finally parsed) of the selected operator.
* @throws MalformedURLException
* @throws ParserConfigurationException
*/
private static Document parseDocumentForOperator(String operatorWikiName, OperatorDescription opDesc) throws MalformedURLException, ParserConfigurationException {
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
builderFactory.setIgnoringComments(true);
builderFactory.setIgnoringElementContentWhitespace(true);
DocumentBuilder documentBuilder = builderFactory.newDocumentBuilder();
documentBuilder.setEntityResolver(new XHTMLEntityResolver());
Document document = null;
URL url = new URL(WIKI_PREFIX_FOR_OPERATORS + operatorWikiName);
if (url != null) {
try {
document = documentBuilder.parse(url.openStream());
} catch (IOException e) {
logger.warning("Could not open " + url.toExternalForm() + ": " + e.getMessage());
} catch (SAXException e) {
logger.warning("Could not parse operator documentation: " + e.getMessage());
}
int i = 0;
if (document != null) {
Element contentElement = document.getElementById("content");
// removing content element from document
if (contentElement != null) {
contentElement.getParentNode().removeChild(contentElement);
}
// removing everything from body
NodeList bodies = document.getElementsByTagName("body");
for (int k = 0; k < bodies.getLength(); k++) {
Node body = bodies.item(k);
while (body.hasChildNodes()) {
body.removeChild(body.getFirstChild());
}
// read content element to body
if (k == 0) {
body.appendChild(contentElement);
}
}
// removing everything from head
NodeList heads = document.getElementsByTagName("head");
for (int k = 0; k < heads.getLength(); k++) {
Node head = heads.item(k);
while (head.hasChildNodes()) {
head.removeChild(head.getFirstChild());
}
}
// removing...<head/> from document
if (heads != null) {
while (i < heads.getLength()) {
Node head = heads.item(i);
head.getParentNode().removeChild(head);
}
}
// removing jump-to-nav element from document
Element jumpToNavElement = document.getElementById("jump-to-nav");
if (jumpToNavElement != null) {
jumpToNavElement.getParentNode().removeChild(jumpToNavElement);
}
// removing mw-normal-catlinks element from document
Element mwNormalCatlinksElement = document.getElementById("mw-normal-catlinks");
if (mwNormalCatlinksElement != null) {
mwNormalCatlinksElement.getParentNode().removeChild(mwNormalCatlinksElement);
}
// removing complete link navigation
Element tocElement = document.getElementById("toc");
if (tocElement != null) {
tocElement.getParentNode().removeChild(tocElement);
}
// removing everything from class printfooter
NodeList nodeListDiv = document.getElementsByTagName("div");
for (int k = 0; k < nodeListDiv.getLength(); k++) {
Element div = (Element) nodeListDiv.item(k);
if (div.getAttribute("class").equals("printfooter")) {
div.getParentNode().removeChild(div);
}
}
// removing everything from class editsection
NodeList spanList = document.getElementsByTagName("span");
for (int k = 0; k < spanList.getLength(); k++) {
Element span = (Element) spanList.item(k);
if (span.getAttribute("class").equals("editsection")) {
span.getParentNode().removeChild(span);
}
}
// Synopsis Header
boolean doIt = true;
NodeList pList = document.getElementsByTagName("p");
for (int k = 0; k < pList.getLength(); k++) {
if (doIt) {
Node p = pList.item(k);
NodeList pChildList = p.getChildNodes();
for (int j = 0; j < pChildList.getLength(); j++) {
Node pChild = pChildList.item(j);
if (pChild.getNodeType() == Node.TEXT_NODE && pChild.getNodeValue() != null && StringUtils.isNotBlank(pChild.getNodeValue()) && StringUtils.isNotEmpty(pChild.getNodeValue())) {
String pChildString = pChild.getNodeValue();
Element newPWithoutSpaces = document.createElement("p");
newPWithoutSpaces.setTextContent(pChildString);
Node synopsis = document.createTextNode("Synopsis");
Element span = document.createElement("span");
span.setAttribute("class", "mw-headline");
span.setAttribute("id", "Synopsis");
span.appendChild(synopsis);
Element h2 = document.createElement("h2");
h2.appendChild(span);
Element div = document.createElement("div");
div.setAttribute("id", "synopsis");
div.appendChild(h2);
div.appendChild(newPWithoutSpaces);
Node pChildParentParent = pChild.getParentNode().getParentNode();
Node pChildParent = pChild.getParentNode();
pChildParentParent.replaceChild(div, pChildParent);
doIt = false;
break;
}
}
} else {
break;
}
}
// removing all <br...>-Tags
NodeList brList = document.getElementsByTagName("br");
while (i < brList.getLength()) {
Node br = brList.item(i);
Node parentBrNode = br.getParentNode();
parentBrNode.removeChild(br);
}
// removing everything from script
NodeList scriptList = document.getElementsByTagName("script");
while (i < scriptList.getLength()) {
Node scriptNode = scriptList.item(i);
Node parentNode = scriptNode.getParentNode();
parentNode.removeChild(scriptNode);
}
// removing all empty <p...>-Tags
NodeList pList2 = document.getElementsByTagName("p");
int ccc = 0;
while (ccc < pList2.getLength()) {
Node p = pList2.item(ccc);
NodeList pChilds = p.getChildNodes();
int kk = 0;
while (kk < pChilds.getLength()) {
Node pChild = pChilds.item(kk);
if (pChild.getNodeType() == Node.TEXT_NODE) {
String pNodeValue = pChild.getNodeValue();
if (pNodeValue == null || StringUtils.isBlank(pNodeValue) || StringUtils.isEmpty(pNodeValue)) {
kk++;
} else {
ccc++;
break;
}
} else {
ccc++;
break;
}
if (kk == pChilds.getLength()) {
Node parentBrNode = p.getParentNode();
parentBrNode.removeChild(p);
}
}
}
// removing firstHeading element from document
Element firstHeadingElement = document.getElementById("firstHeading");
if (firstHeadingElement != null) {
CURRENT_OPERATOR_NAME_READ_FROM_RAPIDWIKI = firstHeadingElement.getFirstChild().getNodeValue();
firstHeadingElement.getParentNode().removeChild(firstHeadingElement);
}
// removing sitesub element from document
Element siteSubElement = document.getElementById("siteSub");
if (siteSubElement != null) {
siteSubElement.getParentNode().removeChild(siteSubElement);
}
// removing contentSub element from document
Element contentSubElement = document.getElementById("contentSub");
if (contentSubElement != null) {
contentSubElement.getParentNode().removeChild(contentSubElement);
}
// removing catlinks element from document
Element catlinksElement = document.getElementById("catlinks");
if (catlinksElement != null) {
catlinksElement.getParentNode().removeChild(catlinksElement);
}
// removing <a...> element from document, if they are empty
NodeList aList = document.getElementsByTagName("a");
if (aList != null) {
int k = 0;
while (k < aList.getLength()) {
Node a = aList.item(k);
Element aElement = (Element) a;
if (aElement.getAttribute("class").equals("internal")) {
a.getParentNode().removeChild(a);
} else {
Node aChild = a.getFirstChild();
if (aChild != null && (aChild.getNodeValue() != null && aChild.getNodeType() == Node.TEXT_NODE && StringUtils.isNotBlank(aChild.getNodeValue()) && StringUtils.isNotEmpty(aChild.getNodeValue()) || aChild.getNodeName() != null)) {
Element aChildElement = null;
if (aChild.getNodeName().startsWith("img")) {
aChildElement = (Element) aChild;
Element imgElement = document.createElement("img");
imgElement.setAttribute("alt", aChildElement.getAttribute("alt"));
imgElement.setAttribute("class", aChildElement.getAttribute("class"));
imgElement.setAttribute("height", aChildElement.getAttribute("height"));
imgElement.setAttribute("src", WIKI_PREFIX_FOR_IMAGES + aChildElement.getAttribute("src"));
imgElement.setAttribute("width", aChildElement.getAttribute("width"));
imgElement.setAttribute("border", "1");
Node aParent = a.getParentNode();
aParent.replaceChild(imgElement, a);
} else {
k++;
}
} else {
a.getParentNode().removeChild(a);
}
}
}
}
}
}
return document;
}
/**
* This method loads the documentation of the operator referenced by the operator description from the local
* resources if present. Otherwise an exception is thrown.
*/
private static String loadSelectedOperatorDocuLocally(OperatorDescription opDesc) throws UnsupportedEncodingException, ParserConfigurationException, URISyntaxException, IOException {
String namespace = opDesc.getProviderNamespace();
String documentationResource = "/" + RESOURCE_SUB_DIR + "/" + namespace + "/" + opDesc.getKey() + ".html";
InputStream resourceStream = OperatorDocLoader.class.getResourceAsStream(documentationResource);
if (resourceStream != null) {
BufferedReader input = new BufferedReader(new InputStreamReader(resourceStream));
try {
String contents = Tools.readOutput(input);
return contents;
} finally {
input.close();
}
} else {
try {
return makeOperatorDocumentation(opDesc.createOperatorInstance());
} catch (OperatorCreationException e) {
LogService.getRoot().log(Level.WARNING, "Failed to create operator: "+e, e);
return ERROR_TEXT_FOR_LOCAL;
}
// opDesc.createOperatorInstance();
// return opDesc.getOperatorDocumentation().getDocumentation();
//return ERROR_TEXT_FOR_LOCAL;
}
}
/**
* This loads the documentation of the operator referenced by the operator description from the Wiki in the
* internet.
*/
private static String loadSelectedOperatorDocuFromWiki(OperatorDescription opDesc) throws IOException, ParserConfigurationException, OperatorCreationException, TransformerException, URISyntaxException {
String operatorWikiName = StringUtils.EMPTY;
if (!opDesc.isDeprecated()) {
operatorWikiName = opDesc.getName().replace(" ", "_");
if (opDesc.getProvider() != null) {
String prefix = opDesc.getProvider().getPrefix();
prefix = Character.toUpperCase(prefix.charAt(0)) + prefix.substring(1);
operatorWikiName = prefix + ":" + operatorWikiName;
}
Document documentOperator = parseDocumentForOperator(operatorWikiName, opDesc);
if (documentOperator != null) {
// writing html back to string
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
// initialize StreamResult with File object to save to file
StreamResult result = new StreamResult(new StringWriter());
DOMSource source = new DOMSource(documentOperator);
transformer.transform(source, result);
String HTMLString = result.getWriter().toString();
HTMLString = customizeHTMLStringDirty(HTMLString, SwingTools.getIconPath("24/" + opDesc.getIconName()));
return HTMLString;
}
}
return loadSelectedOperatorDocuLocally(opDesc);
}
private static String makeOperatorDocumentation(Operator displayedOperator) {
OperatorDescription descr = displayedOperator.getOperatorDescription();
StringBuilder buf = new StringBuilder("<html>");
buf.append("<table cellpadding=0 cellspacing=0><tr><td>");
String iconName = "icons/24/" + displayedOperator.getOperatorDescription().getIconName();
URL resource = Tools.getResource(iconName);
if (resource != null) {
buf.append("<img src=\"" + resource + "\"/> ");
}
buf.append("</td><td style=\"padding-left:4px;\">");
buf.append("<h2>" + descr.getName());
String wikiName;
try {
wikiName = URLEncoder.encode(descr.getName(), "UTF-8");
buf.append(" <small><a href=\"http://rapid-i.com/wiki/index.php?title=").append(wikiName).append("\">(Wiki)</a></small>");
} catch (UnsupportedEncodingException e) {
LogService.getRoot().log(Level.WARNING, "Failed to URL-encode operator name: " + descr.getName() + ": " + e, e);
}
buf.append("</h2>");
buf.append("</td></tr></table>");
buf.append("<hr noshade=\"true\"/><br/>");
buf.append("<h4>Synopsis</h4>");
buf.append("<p>");
buf.append(descr.getShortDescription());
buf.append("</p>");
buf.append("</p><br/>");
buf.append("<h4>Description</h4>");
String descriptionText = descr.getLongDescriptionHTML();
if (descriptionText != null) {
if (!descriptionText.trim().startsWith("<p>")) {
buf.append("<p>");
}
buf.append(descriptionText);
if (!descriptionText.trim().endsWith("</p>")) {
buf.append("</p>");
}
buf.append("<br/>");
}
appendPortsToDocumentation(displayedOperator.getInputPorts(), "Input", null, buf);
appendPortsToDocumentation(displayedOperator.getOutputPorts(), "Output", "outPorts", buf);
Parameters parameters = displayedOperator.getParameters();
if (parameters.getKeys().size() > 0) {
buf.append("<h4>Parameters</h4><dl>");
for (String key : parameters.getKeys()) {
ParameterType type = parameters.getParameterType(key);
if (type == null) {
LogService.getRoot().warning("Unknown parameter key: " + displayedOperator.getName() + "# " + key);
continue;
}
buf.append("<dt>");
if (type.isExpert()) {
buf.append("<i>");
}
// if (type.isOptional()) {
buf.append(makeParameterHeader(type));
// } else {
// buf.append("<strong>");
// buf.append(makeParameterHeader(type));
// buf.append("</strong>");
// }
if (type.isExpert()) {
buf.append("</i>");
}
buf.append("</dt><dd style=\"padding-bottom:10px\">");
// description
buf.append(" ");
buf.append(type.getDescription() + "<br/><font color=\"#777777\" size=\"-2\">");
if (type.getDefaultValue() != null) {
if (!type.toString(type.getDefaultValue()).equals("")) {
buf.append(" Default value: ");
buf.append(type.toString(type.getDefaultValue()));
buf.append("<br/>");
}
}
if (type.isExpert()) {
buf.append("Expert parameter<br/>");
}
// conditions
if (type.getDependencyConditions().size() > 0) {
buf.append("Depends on:<ul class=\"param_dep\">");
for (ParameterCondition condition : type.getDependencyConditions()) {
buf.append("<li>");
buf.append(condition.toString());
buf.append("</li>");
}
buf.append("</ul>");
}
buf.append("</small></dd>");
}
buf.append("</dl>");
}
if (!descr.getOperatorDocumentation().getExamples().isEmpty()) {
buf.append("<h4>Examples</h4><ul>");
int i = 0;
for (ExampleProcess exampleProcess : descr.getOperatorDocumentation().getExamples()) {
buf.append("<li>");
buf.append(exampleProcess.getComment());
buf.append(makeExampleFooter(i));
buf.append("</li>");
i++;
}
buf.append("</ul>");
}
buf.append("</html>");
return buf.toString();
}
private static Object makeExampleFooter(int exampleIndex) {
return "<br/><a href=\"show_example_" + exampleIndex + "\">Show example process</a>.";
}
private static void appendPortsToDocumentation(Ports<? extends Port> ports, String title, String ulClass, StringBuilder buf) {
// buf.append("<dl><dt>Input:<dt></dt><dd>");
if (ports.getNumberOfPorts() > 0) {
buf.append("<h4>" + title + "</h4><ul class=\"ports\">");
for (Port port : ports.getAllPorts()) {
if (ulClass != null)
buf.append("<li class=\"" + ulClass + "\"><strong>");
else
buf.append("<li><strong>");
buf.append(port.getName());
buf.append("</strong>");
if (port.getDescription() != null && port.getDescription().length() > 0) {
buf.append(": ");
buf.append(port.getDescription());
}
buf.append("</li>");
}
buf.append("</ul><br/>");
}
}
private static String makeParameterHeader(ParameterType type) {
return type.getKey().replace('_', ' ');
}
}