/**
* Copyright (C) 2001-2017 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.ByteArrayInputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.util.List;
import java.util.logging.Level;
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 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.OperatorCreationException;
import com.rapidminer.operator.OperatorDescription;
import com.rapidminer.parameter.ParameterType;
import com.rapidminer.parameter.ParameterTypeBoolean;
import com.rapidminer.parameter.ParameterTypeCategory;
import com.rapidminer.parameter.ParameterTypeDouble;
import com.rapidminer.parameter.ParameterTypeEnumeration;
import com.rapidminer.parameter.ParameterTypeInt;
import com.rapidminer.parameter.ParameterTypeList;
import com.rapidminer.parameter.ParameterTypeLong;
import com.rapidminer.parameter.ParameterTypeNumber;
import com.rapidminer.parameter.ParameterTypeString;
import com.rapidminer.parameter.ParameterTypeStringCategory;
import com.rapidminer.parameter.Parameters;
import com.rapidminer.tools.I18N;
import com.rapidminer.tools.LogService;
import com.rapidminer.tools.OperatorService;
import com.rapidminer.tools.Tools;
/**
* This class handles the conversion of the operator documentation XML to an HTML page.
*
* @author Philipp Kersting, Marco Boeck, Marcel Seifert
*
*/
public class OperatorDocToHtmlConverter {
private static final TransformerFactory XSLT_TRANSFORMER_FACTORY = TransformerFactory.newInstance();
public static final String STYLESHEET_RESOURCE = "/com/rapidminer/resources/documentationview.xslt";
private static final byte[] XSLT_CONTENT;
private static final int MAX_CATEROGIES_DISPLAYED_IN_HELP = 10;
static final String INTEGER_LABEL = I18N.getGUILabel("attribute_type.integer");
static final String LONG_LABEL = I18N.getGUILabel("attribute_type.long");
static final String REAL_LABEL = I18N.getGUILabel("attribute_type.real");
static final String SELECTION_LABEL = I18N.getGUILabel("attribute_type.selection");
private static final String STRING_LABEL = I18N.getGUILabel("attribute_type.string");
private static final String LIST_LABEL = I18N.getGUILabel("attribute_type.list");
private static final String ENUMERATION_LABEL = I18N.getGUILabel("attribute_type.enumeration");
private static final String BOOLEAN_LABEL = I18N.getGUILabel("attribute_type.boolean");
private static final String OTHER_LABEL = I18N.getGUILabel("attribute_type.other");
/**
* This icon will be shown as a replacement for operators without an icon. These are usually
* deprecated operators.
*/
private static final String REPLACEMENT_FOR_OPERATORS_WITHOUT_ICON = "symbol_questionmark.png";
static {
byte[] content = new byte[0];
try {
content = Tools.readInputStream(OperatorDocToHtmlConverter.class.getResourceAsStream(STYLESHEET_RESOURCE));
} catch (IOException e) {
LogService.getRoot().log(Level.SEVERE, "com.rapidminer.gui.OperatorDocToHtmlConverter.error_loading_xslt", e);
}
XSLT_CONTENT = content;
}
/**
* Applies the documentation XSLT to the specified XML source and returns the transformed HTML
* as {@link String}.
*
* @param xmlSource
* the source containing the documentation XML
* @return the transformed documentation XML as HTML
* @throws TransformerException
* in case the transformation goes wrong
* @since 6.4.0
*/
public static String applyXSLTTransformation(Source xmlSource) throws TransformerException {
StringWriter buffer = new StringWriter();
Source xsltSource = new StreamSource(new ByteArrayInputStream(XSLT_CONTENT));
Transformer trans = XSLT_TRANSFORMER_FACTORY.newTransformer(xsltSource);
trans.transform(xmlSource, new StreamResult(buffer));
return buffer.toString();
}
/**
* 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) {
if (type == null || type.isEmpty()) {
return "";
} else {
try {
Class<? extends IOObject> typeClass;
typeClass = (Class<? extends IOObject>) Class.forName(type);
return getTypeNameForType(typeClass);
} catch (ClassNotFoundException e) {
LogService.getRoot().log(Level.FINER,
"com.rapidminer.gui.OperatorDocToHtmlConverter.getTypeNameForTypeFromString",
new String[] { type, e.getLocalizedMessage() });
return "";
}
}
}
static String getTypeNameForType(Class<? extends IOObject> typeClass) {
String typeName;
if (typeClass != null) {
typeName = RendererService.getName(typeClass);
if (typeName != null && !typeName.isEmpty()) {
typeName = " (" + typeName + ")";
} else {
LogService.getRoot().log(Level.FINER,
"com.rapidminer.gui.OperatorDocToHtmlConverter.getTypeNameForTypeFromClass_no_name", typeClass);
typeName = "";
}
} else {
LogService.getRoot().log(Level.FINER,
"com.rapidminer.gui.OperatorDocToHtmlConverter.getTypeNameForTypeFromClass_null", typeClass);
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;
}
String iconName = operatorDescription.getIconName();
if (iconName != null) {
return SwingTools.getIconPath("24/" + iconName);
} else {
// Operator has no icon
return SwingTools.getIconPath("24/" + REPLACEMENT_FOR_OPERATORS_WITHOUT_ICON);
}
}
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().log(Level.FINER, "com.rapidminer.gui.OperatorDocToHtmlConverter.null_operator",
operatorKey);
return null;
}
return operatorDescription.getProviderName();
}
/**
* Gets the {@link ParameterType} of the given operator key and parameter name as an i18n
* string. This is used if no type is specified in the documentation xml.
*
* @param operatorKey
* The key of the operator
* @param parameterName
* The name of the parameter
* @return An i18n string containing the type, if one can be found. Empty string else.
*/
public static String getParameterType(String operatorKey, String parameterName) {
Operator operator = null;
int index = operatorKey.indexOf(".");
// remove operator group if existent
if (index != -1) {
operatorKey = operatorKey.substring(index + 1);
}
try {
OperatorDescription description = OperatorService.getOperatorDescription(operatorKey);
if (description == null) {
LogService.getRoot().log(Level.FINER, "com.rapidminer.gui.OperatorDocToHtmlConverter.null_operator",
operatorKey);
return "";
}
operator = description.createOperatorInstance();
if (operator == null) {
LogService.getRoot().log(Level.FINER, "com.rapidminer.gui.OperatorDocToHtmlConverter.null_operator",
operatorKey);
return "";
}
Parameters parameters = operator.getParameters();
if (parameters != null) {
ParameterType type = parameters.getParameterType(parameterName);
if (type != null) {
if (type instanceof ParameterTypeNumber) {
ParameterTypeNumber numberType = (ParameterTypeNumber) type;
if (numberType instanceof ParameterTypeInt) {
return INTEGER_LABEL;
} else if (numberType instanceof ParameterTypeLong) {
return LONG_LABEL;
} else if (numberType instanceof ParameterTypeDouble) {
return REAL_LABEL;
}
} else if (type instanceof ParameterTypeCategory || type instanceof ParameterTypeStringCategory) {
return SELECTION_LABEL;
} else if (type instanceof ParameterTypeString) {
return STRING_LABEL;
} else if (type instanceof ParameterTypeList) {
return LIST_LABEL;
} else if (type instanceof ParameterTypeEnumeration) {
return ENUMERATION_LABEL;
} else if (type instanceof ParameterTypeBoolean) {
return BOOLEAN_LABEL;
} else {
return OTHER_LABEL;
}
}
}
return "";
} catch (OperatorCreationException e) {
LogService.getRoot().finer("Tried to retrieve plugin name for null operator with key " + operatorKey);
return "";
}
}
/**
* Gets the ParameterRange of the given operator key and parameter name as a string. This is
* used if no range is specified in the documentation xml.
*
* @param operatorKey
* The key of the operator
* @param parameterName
* The name of the parameter
* @return A string containing the range, if one can be found. Empty string else.
*/
public static String getParameterRange(String operatorKey, String parameterName) {
Operator operator = null;
int index = operatorKey.indexOf(".");
// remove operator group if existent
if (index != -1) {
operatorKey = operatorKey.substring(index + 1);
}
try {
OperatorDescription description = OperatorService.getOperatorDescription(operatorKey);
if (description == null) {
LogService.getRoot().log(Level.FINER, "com.rapidminer.gui.OperatorDocToHtmlConverter.null_operator",
operatorKey);
return "";
}
operator = description.createOperatorInstance();
if (operator == null) {
LogService.getRoot().log(Level.FINER, "com.rapidminer.gui.OperatorDocToHtmlConverter.null_operator",
operatorKey);
return "";
}
Parameters parameters = operator.getParameters();
if (parameters != null) {
ParameterType type = parameters.getParameterType(parameterName);
if (type != null) {
if (type instanceof ParameterTypeNumber) {
ParameterTypeNumber numberType = (ParameterTypeNumber) type;
StringBuilder range = new StringBuilder();
if (numberType instanceof ParameterTypeInt) {
int min = ((ParameterTypeInt) numberType).getMinValueInt();
int max = ((ParameterTypeInt) numberType).getMaxValueInt();
if (min == -Integer.MAX_VALUE || min == Integer.MIN_VALUE) {
range.append("-\u221E");
} else {
range.append(min);
}
range.append(" - ");
if (max == Integer.MAX_VALUE) {
range.append("+\u221E");
} else {
range.append(max);
}
} else if (numberType instanceof ParameterTypeLong) {
long min = ((ParameterTypeLong) numberType).getMinValuelong();
long max = ((ParameterTypeLong) numberType).getMaxValuelong();
if (min == -Long.MAX_VALUE || min == Long.MIN_VALUE) {
range.append("-\u221E");
} else {
range.append(min);
}
range.append(" - ");
if (max == Long.MAX_VALUE) {
range.append("+\u221E");
} else {
range.append(max);
}
} else if (numberType instanceof ParameterTypeDouble) {
double min = numberType.getMinValue();
double max = numberType.getMaxValue();
if (min == Double.NEGATIVE_INFINITY) {
range.append("-\u221E");
} else {
range.append(min);
}
range.append(" - ");
if (max == Double.POSITIVE_INFINITY) {
range.append("+\u221E");
} else {
range.append(max);
}
}
return range.toString();
} else if (type instanceof ParameterTypeCategory) {
ParameterTypeCategory categoryType = (ParameterTypeCategory) type;
int number = categoryType.getNumberOfCategories();
StringBuilder values = new StringBuilder();
for (int i = 0; i < number && i < MAX_CATEROGIES_DISPLAYED_IN_HELP; i++) {
if (i > 0) {
values.append(", ");
}
values.append(categoryType.getCategory(i));
}
if (number > MAX_CATEROGIES_DISPLAYED_IN_HELP) {
values.append(", ...");
}
return values.toString();
} else if (type instanceof ParameterTypeStringCategory) {
ParameterTypeStringCategory stringCategoryType = (ParameterTypeStringCategory) type;
String[] caterogies = stringCategoryType.getValues();
int number = caterogies.length;
StringBuffer values = new StringBuffer();
for (int i = 0; i < number && i < MAX_CATEROGIES_DISPLAYED_IN_HELP; i++) {
if (i > 0) {
values.append(", ");
}
values.append(caterogies[i]);
}
if (number > MAX_CATEROGIES_DISPLAYED_IN_HELP) {
values.append(", ...");
}
return values.toString();
} else {
return type.getRange();
}
}
}
return "";
} catch (OperatorCreationException e) {
LogService.getRoot().log(Level.FINER, "com.rapidminer.gui.OperatorDocToHtmlConverter.null_operator",
operatorKey);
return "";
}
}
/**
* Gets the default value of the given operator key and parameter name as a string. This is used
* if no default value is specified in the documentation xml.
*
* @param operatorKey
* The key of the operator
* @param parameterName
* The name of the parameter
* @return A string containing the default value, if one can be found. Empty string else.
*/
public static String getParameterDefault(String operatorKey, String parameterName) {
Operator operator = null;
int index = operatorKey.indexOf(".");
// remove operator group if existent
if (index != -1) {
operatorKey = operatorKey.substring(index + 1);
}
try {
OperatorDescription description = OperatorService.getOperatorDescription(operatorKey);
if (description == null) {
LogService.getRoot().log(Level.FINER, "com.rapidminer.gui.OperatorDocToHtmlConverter.null_operator",
operatorKey);
return "";
}
operator = description.createOperatorInstance();
if (operator == null) {
LogService.getRoot().log(Level.FINER, "com.rapidminer.gui.OperatorDocToHtmlConverter.null_operator",
operatorKey);
return "";
}
Parameters parameters = operator.getParameters();
if (parameters != null) {
ParameterType type = parameters.getParameterType(parameterName);
if (type != null) {
if (type.getDefaultValueAsString() != null && !type.getDefaultValueAsString().trim().isEmpty()) {
return type.getDefaultValueAsString();
}
}
}
return "";
} catch (OperatorCreationException e) {
LogService.getRoot().log(Level.FINER, "com.rapidminer.gui.OperatorDocToHtmlConverter.null_operator",
operatorKey);
return "";
}
}
/**
* Returns if the given parameter is optional.
*
* @param operatorKey
* The key of the operator
* @param parameterName
* The name of the parameter
* @return {@code true} if the parameter is optional. {@code false} if the parameter is not
* optional or if no parameter exists for the given name and operator.
*/
public static boolean isParameterOptional(String operatorKey, String parameterName) {
Operator operator = null;
int index = operatorKey.indexOf(".");
if (index != -1) {
operatorKey = operatorKey.substring(index + 1);
}
try {
OperatorDescription description = OperatorService.getOperatorDescription(operatorKey);
if (description == null) {
LogService.getRoot().log(Level.FINER, "com.rapidminer.gui.OperatorDocToHtmlConverter.null_operator",
operatorKey);
return false;
}
operator = description.createOperatorInstance();
if (operator == null) {
LogService.getRoot().log(Level.FINER, "com.rapidminer.gui.OperatorDocToHtmlConverter.null_operator",
operatorKey);
return false;
}
Parameters parameters = operator.getParameters();
if (parameters != null) {
ParameterType type = parameters.getParameterType(parameterName);
if (type != null) {
return type.isOptional();
}
}
return false;
} catch (OperatorCreationException e) {
LogService.getRoot().log(Level.FINER, "com.rapidminer.gui.OperatorDocToHtmlConverter.null_operator",
operatorKey);
return false;
}
}
/**
* 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;
}
public static String getTagHtmlForKey(String operatorKey) {
// 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);
return getTagHtmlForDescription(operatorDescription);
}
static String getTagHtmlForDescription(OperatorDescription operatorDescription) {
if (operatorDescription != null) {
List<String> tags = operatorDescription.getTags();
if(tags != null && !tags.isEmpty()){
StringBuilder builder = new StringBuilder();
builder.append("<p class=\"tags\">Tags:");
for(int i=0; i<tags.size(); i++){
String tag = tags.get(i);
builder.append(" <a class=\"tags\" href=\"tag:" + tag + "\">" + tag + "</a>");
if(i != tags.size() - 1){
builder.append(",");
}
}
builder.append("</p>");
return builder.toString();
}
}
// no tags found
return "";
}
}