/* * 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.operator; import java.text.MessageFormat; import java.util.ResourceBundle; import com.rapidminer.NoBugError; import com.rapidminer.tools.I18N; import com.rapidminer.tools.Tools; /** * Exception class whose instances are thrown due to a user error, for example * missing files or wrong operator architecture. <br> * In order to create a UserError, do the following: * <ul> * <li>Open the file <code>UserErrorMessages.properties</code> in the * <code>resources</code> directory. Look for an appropriate message. If you * find one, remember its id. If not, create a new one in the correct * group</li> * <li>The entry must include name, short message and long message. The name * and long message will be presented to the user literally. The short message * will be parsed by <code>java.text.MessageFormat</code>. Especially, any * occurrence of curly brackets will be replaced. Be careful with quotes; it * might be a good idea to read the documentation of MessageFormat first.</li> * <li>Create a UserError by using this id. If the UserError is created because * of another exception, e.g. a FileNotFoundException, this exception should be * passed to the UserError in the constructor.</li> * </ul> * <b>Attention!</b><br> * Although the current UserErrorMessages.properties only contain numbers, * the current implementation supports arbitrary strings as identifiers. * Writers of Extensions are encouraged to use Strings for separating their * self defined errors from the errors defined in the core. * You should prepend the namespace of the extension like this: * error.<extensions namespace>.error_id.short = ... * * @author Simon Fischer, Ingo Mierswa, Sebastian Land */ public class UserError extends OperatorException implements NoBugError { private static final long serialVersionUID = -8441036860570180869L; private static ResourceBundle messages = I18N.getUserErrorMessagesBundle(); private static final MessageFormat formatter = new MessageFormat(""); private String errorIdentifier = null; private final int code; private transient Operator operator; /** * Creates a new UserError. * * @param operator * The {@link Operator} in which the exception occured. * @param cause * The exception that caused the user error. May be null. Using * this makes debugging a lot easier. * @param code * The error code referring to a message in the file * <code>UserErrorMessages.properties</code> * @param arguments * Arguments for the short message. */ public UserError(Operator operator, Throwable cause, int code, Object ... arguments) { super(getErrorMessage(code, arguments), cause); this.code = code; this.operator = operator; } /** Convenience constructor for messages with no arguments and cause. */ public UserError(Operator operator, Throwable cause, int code) { this(operator, code, new Object[0], cause); } public UserError(Operator operator, int code, Object ... arguments) { this(operator, null, code, arguments); } /** Convenience constructor for messages with no arguments. */ public UserError(Operator operator, int code) { this(operator, null, code, new Object[0]); } public UserError(Operator operator, Throwable cause, String errorId, Object ... arguments) { super(getErrorMessage(errorId, arguments), cause); this.code = -1; this.errorIdentifier = errorId; this.operator = operator; } /** Convenience constructor for messages with no arguments and cause. * This constructor is in fact equivalent to the call of the above constructor but * must kept for compatibility issues for existing compiled extensions. */ public UserError(Operator operator, Throwable cause, String errorId) { this(operator, cause, errorId, new Object[0]); } public UserError(Operator operator, String errorId, Object ... arguments) { this(operator, null, errorId, arguments); } /** Convenience constructor for messages with no arguments. */ public UserError(Operator operator, String errorId) { this(operator, null, errorId, new Object[0]); } public String getDetails() { if (errorIdentifier == null) return getResourceString(code, "long", "Description missing."); else return getResourceString(errorIdentifier, "long", "Description missing."); } public String getErrorName() { if (errorIdentifier == null) return getResourceString(code, "name", "Unnamed error."); else return getResourceString(errorIdentifier, "name", "Unnamed error."); } public int getCode() { return code; } public Operator getOperator() { return operator; } public void setOperator(Operator operator) { this.operator = operator; } public static String getErrorMessage(int code, Object[] arguments) { String message = getResourceString(code, "short", "No message."); try { formatter.applyPattern(message); String formatted = formatter.format(arguments); return formatted; } catch (Throwable t) { return message; } } public static String getErrorMessage(String identifier, Object[] arguments) { String message = getResourceString(identifier, "short", "No message."); try { formatter.applyPattern(message); String formatted = formatter.format(arguments); return formatted; } catch (Throwable t) { return message; } } /** * Returns a resource message for the given error code. * * @param key * one out of "name", "short", * "long" */ public static String getResourceString(int code, String key, String deflt) { return getResourceString(code + "", key, deflt); } /** * This returns a resource message of the internationalized error messages * identified by an id. Compared to the legacy method {@link #getResourceString(int, String, String)} * this supports a more detailed identifier. * This makes it easier to ensure extensions don't reuse already defined core errors. It is common sense to * add the extensions namespace identifier as second part of the key, just after error. * For example: error.rmx_web.operator.unusable = This operator {0} is unusable. * @param id The identifier of the error. "error." will be automatically prepended- * @param key The part of the error description that should be shown. * @param deflt The default if no resource bundle is available. */ public static String getResourceString(String id, String key, String deflt) { if (messages == null) return deflt; try { return messages.getString("error." + id + "." + key); } catch (java.util.MissingResourceException e) { return deflt; } } public String getHTMLMessage() { // return "<html>Error in: <b>" + getOperator() + "</b><br>" + Tools.escapeXML(getMessage()) + "<hr>" + Tools.escapeXML(getDetails()) + "</html>"; return "<html>Error in: <b>" + getOperator() + "</b><br>" + Tools.escapeXML(getMessage()) + "</html>"; } }