/* * RapidMiner * * Copyright (C) 2001-2014 by RapidMiner and the contributors * * Complete list of developers available at our web site: * * http://rapidminer.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; import java.io.IOException; import java.io.InputStream; import java.io.StringWriter; import java.net.MalformedURLException; import java.net.URL; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.logging.Level; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.Source; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; import org.xml.sax.SAXException; import com.rapidminer.gui.renderer.RendererService; import com.rapidminer.gui.tools.SwingTools; import com.rapidminer.operator.IOObject; import com.rapidminer.operator.Operator; import com.rapidminer.operator.OperatorDescription; import com.rapidminer.operator.ports.InputPort; import com.rapidminer.operator.ports.OutputPort; import com.rapidminer.operator.ports.metadata.MetaData; import com.rapidminer.operator.ports.metadata.Precondition; import com.rapidminer.parameter.ParameterType; import com.rapidminer.parameter.Parameters; import com.rapidminer.tools.LogService; import com.rapidminer.tools.OperatorService; import com.rapidminer.tools.Tools; import com.rapidminer.tools.documentation.ExampleProcess; /** * This class handles the conversion of the operator documentation XML to an HTML page. * * @author Philipp Kersting, Marco Boeck * */ public class OperatorDocToHtmlConverter { private static final String STYLESHEET_RESOURCE = "/com/rapidminer/resources/documentationview.xslt"; /** * Tries to convert the given XML file into an HTML file using the given XSLT stylesheet. * If this fails (probably because the given XML file doesn't exist), {@link #createOfflineFallbackDocumentation(Operator)} * will be used to generate a String from the old local operator description resources. * * @param xmlStream * @param operator * @return * @throws MalformedURLException * @throws IOException */ public static String convert(InputStream xmlStream, Operator operator) throws MalformedURLException, IOException { if (operator == null) { throw new IllegalArgumentException("operator must not be null!"); } if (xmlStream == null) { LogService.getRoot().finer("Failed to load documentation, using online fallback. Reason: xmlStream is null."); return createFallbackDocumentation(operator); } StringWriter buffer = new StringWriter(); Source xmlSource = new StreamSource(xmlStream); Source xsltSource = new StreamSource(OperatorDocToHtmlConverter.class.getResourceAsStream(STYLESHEET_RESOURCE)); TransformerFactory transFact = TransformerFactory.newInstance(); String html = ""; try { Transformer trans = transFact.newTransformer(xsltSource); trans.transform(xmlSource, new StreamResult(buffer)); html = buffer.toString(); } catch (TransformerException e) { LogService.getRoot().log(Level.WARNING , "Failed to load documentation, using online fallback.", e); return createFallbackDocumentation(operator); } return html; } /** * This fallback method delivers an operator documentation. * First it tries to load the description from the Wiki and if that fails * it will return the old offline operator description. * * @param operator * @return */ private static String createFallbackDocumentation(Operator operator) { try { return createOnlineWikiFallbackDocumentation(operator); } catch (Exception e) { LogService.getRoot().finest("Failed to load online documentation, using offline fallback. Reason: " + e.getLocalizedMessage()); return createOfflineFallbackDocumentation(operator); } } /** * This is the offline fallback method to create the HTML page from the old operator description. * * @param operator * @return */ private static String createOfflineFallbackDocumentation(Operator operator) { OperatorDescription descr = operator.getOperatorDescription(); StringBuilder buf = new StringBuilder("<html><head></head><body>"); String iconName = "icons/24/" + operator.getOperatorDescription().getIconName(); URL resource = Tools.getResource(iconName); buf.append("<table> <tr> <td> "); if (resource != null) { buf.append("<img src=\"" + resource + "\"/> "); }else{ buf.append("<img src\""+SwingTools.getIconPath("48/information2.png")+"\" class=\"HeadIcon\"/>"); } buf.append("</td><td>"); buf.append ("<h2>" + operator.getOperatorDescription().getName()+"</h2></td></tr></table><hr />"); buf.append("<h3>Synopsis</h3>"); buf.append("<p>"+descr.getShortDescription() + "</p>"); buf.append("<h3>Description</h3>"); buf.append("<p>"+descr.getLongDescriptionHTML()+"</p>"); buf.append("<h3>Input</h3>"); buf.append("<table cellspacing=7>"); for (InputPort port: operator.getInputPorts().getAllPorts()){ buf.append("<tr>"); buf.append("<td>"); buf.append("<table>"); buf.append("<tr>"); buf.append("<td class=\"lilIcon\">"); //print port.getMetaData().toString() Class <?extends IOObject> typeClass = null; List<Precondition> preconditions = new LinkedList<Precondition>(port.getAllPreconditions()); for (Precondition precondition : preconditions){ if (precondition.getDescription().contains("expects")){ MetaData metaData = (precondition.getExpectedMetaData()); typeClass = metaData.getObjectClass(); } } String imgSrc = getIconNameForType(typeClass); buf.append("<img src=\""+ imgSrc +"\" class=\"typeIcon\" />"); buf.append("</td><td>"); buf.append("<b>" + port.getName() + "</b>"); if (typeClass!=null){ buf.append("<i> ("+ port.getDescription() + ")</i>"); } buf.append("</td"); buf.append("</tr>"); buf.append("</table>"); buf.append("</td"); buf.append("</tr>"); } buf.append("</table>"); buf.append("<h3>Output</h3>"); buf.append("<table cellspacing=7>"); for (OutputPort port: operator.getOutputPorts().getAllPorts()){ buf.append("<tr>"); buf.append("<td>"); buf.append("<table>"); buf.append("<tr>"); buf.append("<td class=\"lilIcon\">"); String imgSrc = SwingTools.getIconPath("24/plug.png"); buf.append("<img src=\""+ imgSrc +"\" class=\"typeIcon\" />"); buf.append("</td><td>"); buf.append("<b>" + port.getName() + "</b>"); if (!port.getDescription().equals("")){ buf.append("<i> ("+ port.getDescription() + ") </i>"); } buf.append("</td>"); buf.append("</tr>"); buf.append("</table>"); buf.append("</td>"); buf.append("</tr>"); } buf.append("</table>"); Parameters parameters = operator.getParameters(); Set<String> keys = parameters.getKeys(); if (keys.size()>0){ buf.append("<h3>Parameters</h3>"); buf.append("<table cellspacing=7>"); for (String key : keys){ ParameterType type = operator.getParameterType(key); buf.append("<tr>"); buf.append("<td>"); buf.append("<b>"); buf.append("<dt>"); buf.append(type.getKey().replace('_', ' ')); buf.append("</dt>"); buf.append("</b>"); if (type.isExpert()){ buf.append(" (Expert) "); } buf.append("</td>"); buf.append("</tr>"); buf.append("<tr>"); buf.append("<td>"); buf.append(type.getDescription()); buf.append("<b> Range: </b>"); String range = type.getRange(); if (range.split("default:") [0]!=null){ range = range.split("default:") [0]; } buf.append(range); if (type.getDefaultValue()!=null){ buf.append("<b> Default: </b>"); buf.append(type.getDefaultValueAsString()); } buf.append("</td>"); buf.append("</tr>"); } buf.append("</table>"); } if (operator.getOperatorDescription().getOperatorDocumentation().getExamples().size()>0) { if (operator.getOperatorDescription().getOperatorDocumentation().getExamples().size()==1){ buf.append("<h3>Turorial Process</h3>"); buf.append("<a href=\"l1\">ShowExampleProcess</a>"); buf.append(operator.getOperatorDescription().getOperatorDocumentation().getExamples().get(0).getComment()); } if (operator.getOperatorDescription().getOperatorDocumentation().getExamples().size()>1){ buf.append("<h3>Turorial Processes</h3>"); int i = 0; for (ExampleProcess example : operator.getOperatorDescription().getOperatorDocumentation().getExamples()){ buf.append("<a href=\"l"+i+"\">ShowExampleProcess "+i+"</a>"); buf.append(example.getComment()); } } } buf.append("</body></html>"); return buf.toString(); } /** * This is the online fallback method to create the HTML page from the wiki description. * * @param operator * @return * @throws SAXException * @throws IOException * @throws ParserConfigurationException * @throws TransformerException */ private static String createOnlineWikiFallbackDocumentation(Operator operator) throws SAXException, IOException, ParserConfigurationException, TransformerException{ return OperatorDocLoader.loadOperatorDocumentation(true, true, operator.getOperatorDescription()); } /** * Replaces underscores in the given {@link String} with a blank. * * @param string * @return */ public static String insertBlanks(String string) { return string.replace('_', ' '); } /** * Returns the name of a type in exchange for its class' name. * * @param type the class' name as String * @return the short name of the class as String */ @SuppressWarnings("unchecked") public static String getTypeNameForType(String type) { String typeName = null; if ((type == null) || type.isEmpty()) { typeName = ""; } else { Class<? extends IOObject> typeClass; try { typeClass = (Class<? extends IOObject>) Class.forName(type); typeName = " (" + RendererService.getName(typeClass) + ")"; } catch (ClassNotFoundException e) { LogService.getRoot().finer("Failed to lookup class '" + type + "'. Reason: " + e.getLocalizedMessage()); typeName = ""; } } return typeName; } /** * Returns the path to the icon that belongs to the given operator key. * * @param operatorKey the key of the operator * @return the path to icon for the given operator key */ public static String getIconNameForOperator(String operatorKey) { if (operatorKey == null) { LogService.getRoot().finer("Tried to retrieve icon name for null operatorKey!"); return null; } // operator keys in the documentation begin with "operator.", so remove that int index = operatorKey.indexOf("."); if (index != -1) { operatorKey = operatorKey.substring(index + 1); } OperatorDescription operatorDescription = OperatorService.getOperatorDescription(operatorKey); if (operatorDescription==null) { LogService.getRoot().finer("Tried to retrieve icon name for null operator with key "+operatorKey); return null; } return SwingTools.getIconPath("24/" + operatorDescription.getIconName()); } public static String getIconNameForOperatorSmall(String operatorKey) { if (operatorKey == null) { LogService.getRoot().finer("Tried to retrieve icon name for null operatorKey!"); return null; } // operator keys in the documentation begin with "operator.", so remove that int index = operatorKey.indexOf("."); if (index != -1) { operatorKey = operatorKey.substring(index + 1); } OperatorDescription operatorDescription = OperatorService.getOperatorDescription(operatorKey); if (operatorDescription==null) { LogService.getRoot().finer("Tried to retrieve icon name for null operator with key "+operatorKey); return null; } return SwingTools.getIconPath("16/" + operatorDescription.getIconName()); } public static String getOperatorNameForKey(String operatorKey) { OperatorDescription operatorDescription = OperatorService.getOperatorDescription(operatorKey); if (operatorDescription != null) { return operatorDescription.getName(); } else { return null; } } public static String getPluginNameForOperator(String operatorKey) { OperatorDescription operatorDescription; int index = operatorKey.indexOf("."); if (index != -1) { operatorKey = operatorKey.substring(index + 1); } operatorDescription = OperatorService.getOperatorDescription(operatorKey); if (operatorDescription==null) { LogService.getRoot().finer("Tried to retrieve plugin name for null operator with key "+operatorKey); return null; } return operatorDescription.getProviderName(); } /** * * Searches for a class with the given name and returns the path of the resource. * * @param clazz the class as Class. * @return the path of the resource of the corresponding icon. */ private static String getIconNameForType(Class<? extends IOObject> clazz) { String iconName; String path = null; Class<? extends IOObject> typeClass; typeClass = clazz; iconName = RendererService.getIconName(typeClass); if (iconName == null) { iconName = "plug.png"; } try { path = SwingTools.getIconPath("24/" + iconName); } catch (Exception e) { LogService.getRoot().finer("Error retrieving icon for type '" + clazz + "'! Reason: " + e.getLocalizedMessage()); } return path; } /** * Searches for a class with the given name and returns the path of the resource. * Used for the images of the ports' data types. * * @param type the class' name as String * @return the path of the resource of the corresponding icon. */ @SuppressWarnings("unchecked") public static String getIconNameForType(String type) { String iconName; if ((type == null) || type.isEmpty()) { iconName = "plug.png"; } else { Class<? extends IOObject> typeClass; try { typeClass = (Class<? extends IOObject>) Class.forName(type); iconName = RendererService.getIconName(typeClass); if (iconName == null) { iconName = "plug.png"; } } catch (ClassNotFoundException e) { LogService.getRoot().finer("Failed to lookup class '" + type + "'. Reason: " + e.getLocalizedMessage()); iconName = "plug.png"; } } String path = SwingTools.getIconPath("24/" + iconName); return path; } }