# HG changeset patch # User Severin Gehwolf # Date 1378457476 -7200 # Node ID 4afcf12749157510e2e3885178f2aef622f43dbc # Parent ff7b684c65ef968b3b19877a685c7aea8a89996d Validator improvements. Reviewed-by: omajid Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2013-September/008182.html Contributed-by: Giovanni Astarita diff -r ff7b684c65ef -r 4afcf1274915 integration-tests/src/test/java/com/redhat/thermostat/itest/PluginTest.java --- a/integration-tests/src/test/java/com/redhat/thermostat/itest/PluginTest.java Wed Sep 11 12:44:35 2013 -0400 +++ b/integration-tests/src/test/java/com/redhat/thermostat/itest/PluginTest.java Fri Sep 06 10:51:16 2013 +0200 @@ -121,22 +121,22 @@ " " + " " + command + "" + " " + description + "" + - " " + - " shell" + - " cli" + - " " + " " + " " + " " + + " " + + " shell" + + " cli" + + " " + " " + - " bar" + + " " + + " bar" + + " 0.1.0" + + " " + " " + - " " + - " foo" + - " " + " " + " " + ""; @@ -191,11 +191,11 @@ " " + " " + command + "" + " " + - " bar" + + " " + + " bar" + + " 0.1.0" + + " " + " " + - " " + - " foo" + - " " + " " + " " + ""; diff -r ff7b684c65ef -r 4afcf1274915 launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginCommandInfoSource.java --- a/launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginCommandInfoSource.java Wed Sep 11 12:44:35 2013 -0400 +++ b/launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginCommandInfoSource.java Fri Sep 06 10:51:16 2013 +0200 @@ -40,7 +40,6 @@ import java.io.FileNotFoundException; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; @@ -55,6 +54,7 @@ import com.redhat.thermostat.launcher.internal.PluginConfiguration.CommandExtensions; import com.redhat.thermostat.launcher.internal.PluginConfiguration.NewCommand; import com.redhat.thermostat.plugin.validator.PluginConfigurationValidatorException; +import com.redhat.thermostat.plugin.validator.ValidationErrorsFormatter; /** * Searches for plugins under $THERMOSTAT_HOME/plugins/ and @@ -95,10 +95,15 @@ File configurationFile = new File(pluginDir, PLUGIN_CONFIG_FILE); PluginConfiguration pluginConfig = parser.parse(configurationFile); loadNewAndExtendedCommands(internalJarRoot, pluginDir, pluginConfig); + } catch (PluginConfigurationParseException exception) { logger.log(Level.WARNING, "unable to parse plugin configuration", exception); - } catch (PluginConfigurationValidatorException exception) { - logger.log(Level.INFO, "unable to validate " + exception.getFilePath() + " file\n"); + + } catch (PluginConfigurationValidatorException pcve) { + ValidationErrorsFormatter formatter = new ValidationErrorsFormatter(); + logger.log(Level.INFO, formatter.format(pcve.getAllErrors())); + logger.log(Level.INFO, "unable to validate " + pcve.getXmlFile().getAbsolutePath() + " file\n"); + } catch (FileNotFoundException exception) { logger.log(Level.INFO, "file not found", exception); } diff -r ff7b684c65ef -r 4afcf1274915 launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginConfigurationParser.java --- a/launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginConfigurationParser.java Wed Sep 11 12:44:35 2013 -0400 +++ b/launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginConfigurationParser.java Fri Sep 06 10:51:16 2013 +0200 @@ -205,7 +205,7 @@ public PluginConfiguration parse(File configurationFile) throws FileNotFoundException, PluginConfigurationValidatorException { PluginValidator validator = new PluginValidator(); - validator.validate(configurationFile, false); + validator.validate(configurationFile); PluginConfiguration config = null; try (FileInputStream fis = new FileInputStream(configurationFile)) { config = parse(configurationFile.getParentFile().getName(), fis); diff -r ff7b684c65ef -r 4afcf1274915 thermostat-plugin-validator/src/main/java/com/redhat/thermostat/plugin/validator/PluginConfigurationValidatorException.java --- a/thermostat-plugin-validator/src/main/java/com/redhat/thermostat/plugin/validator/PluginConfigurationValidatorException.java Wed Sep 11 12:44:35 2013 -0400 +++ b/thermostat-plugin-validator/src/main/java/com/redhat/thermostat/plugin/validator/PluginConfigurationValidatorException.java Fri Sep 06 10:51:16 2013 +0200 @@ -36,34 +36,113 @@ package com.redhat.thermostat.plugin.validator; +import java.io.File; +import java.util.ArrayList; +import java.util.List; +/** + * Thrown if there were schema validation issues or other problems in a + * thermostat-plugin.xml + * + */ public class PluginConfigurationValidatorException extends Exception { - + private static final long serialVersionUID = 1L; - private String filePath; - + private final File xmlFile; + private final List warnings; + private final List errors; + private final List fatals; + /** - * Constructor of PluginConfigurationValidatorException - * @param filePath The absolute path to the file which failed to validate. - * @param message the detailed message + * Constructor. + * + * Calls + * {@link #PluginConfigurationValidatorException(String, File, List, List, List, Throwable)} + * with a null cause. */ - public PluginConfigurationValidatorException(String filePath, String message) { - super(message); - this.filePath = filePath; + public PluginConfigurationValidatorException(String message, File xmlFile, + List errors, List warnings, + List fatals) { + this(message, xmlFile, errors, warnings, fatals, null); } - + /** - * Constructor of PluginConfigurationValidatorException - * @param filePath The absolute path to the file which failed to validate. - * @param message the detailed message + * Constructor. + * + * @param message + * A descriptive message. + * @param xmlFile + * The thermostat-plugin.xml file which caused this exception to + * be thrown. + * @param errors + * The list of schema validation errors. + * @param warnings + * The list of (validation) warnings. + * @param fatals + * The list of fatal (validation) errors. + * @param cause + * The underlying exception. May be null. + */ + public PluginConfigurationValidatorException(String message, File xmlFile, + List errors, List warnings, + List fatals, Throwable cause) { + super(message); + this.xmlFile = xmlFile; + this.warnings = warnings; + this.errors = errors; + this.fatals = fatals; + } + + /** + * + * @return The list of all validation issues. */ - public PluginConfigurationValidatorException(String filePath, String message, Throwable cause) { - super(message, cause); - this.filePath = filePath; + public List getAllErrors() { + List errorsList = new ArrayList<>(); + errorsList.addAll(warnings); + errorsList.addAll(errors); + errorsList.addAll(fatals); + return errorsList; + } + + /** + * + * @return The thermostat-plugin.xml file which failed validation. + */ + public File getXmlFile() { + return xmlFile; + } + + /** + * Conditions that are not errors or fatal errors as defined by the XML + * recommendation. + * + * @return The list of (validation) warnings. + */ + public List getWarnings() { + return warnings; } - - public String getFilePath() { - return filePath; + + /** + * Each validation issue corresponds to the definition of "error" in section + * 1.2 of the W3C XML 1.0 Recommendation. + * + * @return The list of violations of schema validity constraints. + */ + public List getErrors() { + return errors; } - + + /** + * Each validation issue corresponds to the definition of "fatal error" in + * section 1.2 of the W3C XML 1.0 Recommendation. + * + * @return The list of fatal (i.e. non-recoverable) errors. Fatal issues may + * occur if the thermostat-plugin.xml violates well-formedness + * constraints. + */ + public List getFatals() { + return fatals; + } + } diff -r ff7b684c65ef -r 4afcf1274915 thermostat-plugin-validator/src/main/java/com/redhat/thermostat/plugin/validator/PluginValidator.java --- a/thermostat-plugin-validator/src/main/java/com/redhat/thermostat/plugin/validator/PluginValidator.java Wed Sep 11 12:44:35 2013 -0400 +++ b/thermostat-plugin-validator/src/main/java/com/redhat/thermostat/plugin/validator/PluginValidator.java Fri Sep 06 10:51:16 2013 +0200 @@ -52,27 +52,61 @@ import com.redhat.thermostat.plugin.validator.internal.ConfigurationValidatorErrorHandler; - +/** + * Validates thermostat-plugin.xml files against its schema. + * + */ public class PluginValidator { - - public void validate(File pluginXml, boolean isCommand) throws PluginConfigurationValidatorException, FileNotFoundException { + + /** + * The method validates a thermostat-plugin.xml file against the thermostat + * plug-in schema + * + * @param pluginXml + * The file to be validated + * @throws PluginConfigurationValidatorException + * If any kind of schema validation error occurs. This exception + * contains details about the issues which occurred. + * @throws FileNotFoundException + * if the file does not exist, is a directory rather than a + * regular file, or for some other reason cannot be opened for + * reading. + * + * @see PluginConfigurationValidatorException + * @see ValidationErrorsFormatter + */ + public void validate(File pluginXml) throws PluginConfigurationValidatorException, FileNotFoundException { URL schemaUrl = PluginValidator.class.getResource("/thermostat-plugin.xsd"); SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); + ConfigurationValidatorErrorHandler handler = new ConfigurationValidatorErrorHandler(pluginXml); try (FileInputStream fis = new FileInputStream(pluginXml)) { Schema schema = schemaFactory.newSchema(schemaUrl); Validator validator = schema.newValidator(); - validator.setErrorHandler(new ConfigurationValidatorErrorHandler(pluginXml, isCommand)); + validator.setErrorHandler(handler); validator.validate(new StreamSource(fis)); } catch (SAXException exception) { - throw new PluginConfigurationValidatorException - (pluginXml.getAbsolutePath(), exception.getLocalizedMessage(), exception); + throw new PluginConfigurationValidatorException( + "thermostat-plugin.xml not well-formed XML", pluginXml, + handler.getErrors(), handler.getWarnings(), + handler.getFatalErrors(), exception); } catch (FileNotFoundException fnfe) { throw fnfe; } catch (IOException ioe) { - throw new PluginConfigurationValidatorException(ioe.getLocalizedMessage(), ioe.getMessage()); - } + throw new PluginConfigurationValidatorException( + ioe.getLocalizedMessage(), pluginXml, handler.getErrors(), + handler.getWarnings(), handler.getFatalErrors(), ioe); + } + + // We've registered an exception handler, so validation issues do not throw + // exceptions unless the xml file itself is not valid. Let's ask the handler + // if there were issues so we can throw an exception accordingly. + if (handler.hasValidationIssues()) { + throw new PluginConfigurationValidatorException( + "Failed to validate thermostat-plugin.xml", pluginXml, + handler.getErrors(), handler.getWarnings(), + handler.getFatalErrors()); + } } - } diff -r ff7b684c65ef -r 4afcf1274915 thermostat-plugin-validator/src/main/java/com/redhat/thermostat/plugin/validator/ValidationErrorsFormatter.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/thermostat-plugin-validator/src/main/java/com/redhat/thermostat/plugin/validator/ValidationErrorsFormatter.java Fri Sep 06 10:51:16 2013 +0200 @@ -0,0 +1,139 @@ +/* + * Copyright 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * . + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.plugin.validator; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.redhat.thermostat.plugin.validator.locale.LocaleResources; +import com.redhat.thermostat.shared.locale.Translate; + +/** + * Formats XML validation issues to strings. + * + * @see PluginConfigurationValidatorException + */ +public class ValidationErrorsFormatter { + + private enum ErrorType { + WARNING, + ERROR, + FATALERROR; + } + + private Map translateKeys; + private static final Translate translator = LocaleResources.createLocalizer(); + + public ValidationErrorsFormatter() { + + translateKeys = new HashMap<>(); + translateKeys.put(ErrorType.ERROR, LocaleResources.VALIDATION_ERROR); + translateKeys.put(ErrorType.WARNING, LocaleResources.VALIDATION_WARNING); + translateKeys.put(ErrorType.FATALERROR, LocaleResources.VALIDATION_FATAL_ERROR); + + } + + public String format(List list) { + StringBuilder outputBuilder = new StringBuilder(); + for (ValidationIssue ave : list) { + outputBuilder.append(formatError(ave)); + } + return outputBuilder.toString(); + } + + private StringBuilder formatError(ValidationIssue ave) { + StringBuilder builder = new StringBuilder(); + + String LS = System.getProperty("line.separator"); + String firstLine = null; + String secondLine = null; + String thirdLine = null; + String errorLine = null; + String pointer = ""; + String absolutePath = ave.getXmlFile().getAbsolutePath(); + + try { + BufferedReader br = new BufferedReader(new FileReader(absolutePath)); + for (int i = 1; i < ave.getLineNumber()-3; i++) { + br.readLine(); + } + firstLine = br.readLine(); + secondLine = br.readLine(); + thirdLine = br.readLine(); + errorLine = br.readLine(); + + for (int j = 1; j < ave.getColumnNumber()-1; j++) { + pointer = pointer.concat(" "); + } + pointer = pointer.concat("^"); + br.close(); + } catch (IOException exception) { + // if br fails to close + } + + builder.append(translator.localize( + translateKeys.get(ErrorType.valueOf(ave.getClass().getSimpleName().toUpperCase())), + absolutePath, + Integer.toString(ave.getLineNumber()), + Integer.toString(ave.getColumnNumber())).getContents()); + + builder.append(formatMessage(ave.getMessage())).append(LS).append(LS); + builder.append(firstLine).append(LS); + builder.append(secondLine).append(LS); + builder.append(thirdLine).append(LS); + builder.append(errorLine).append(LS); + builder.append(pointer).append(LS); + + return builder; + } + + private String formatMessage(String message) { + String[] arguments = message.split("\"http://icedtea.classpath.org/thermostat/plugins/v1.0\":"); + int size = arguments.length; + String output = ""; + + for (int i = 0; i < size; i++) { + output=output.concat(arguments[i]); + } + return output; + } + +} diff -r ff7b684c65ef -r 4afcf1274915 thermostat-plugin-validator/src/main/java/com/redhat/thermostat/plugin/validator/ValidationIssue.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/thermostat-plugin-validator/src/main/java/com/redhat/thermostat/plugin/validator/ValidationIssue.java Fri Sep 06 10:51:16 2013 +0200 @@ -0,0 +1,71 @@ +/* + * Copyright 2012, 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * . + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.plugin.validator; + +import java.io.File; + +/** + * Represents XML schema validation issues. + * + */ +public interface ValidationIssue { + + /** + * + * @return The line number where the issue occurred. + */ + public int getLineNumber(); + + /** + * + * @return The column number where the issue occurred. + */ + public int getColumnNumber(); + + /** + * + * @return A message describing the issue. + */ + public String getMessage(); + + /** + * + * @return The thermostat-plugin.xml file which caused the issue. + */ + public File getXmlFile(); + +} diff -r ff7b684c65ef -r 4afcf1274915 thermostat-plugin-validator/src/main/java/com/redhat/thermostat/plugin/validator/internal/AbstractValidationError.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/thermostat-plugin-validator/src/main/java/com/redhat/thermostat/plugin/validator/internal/AbstractValidationError.java Fri Sep 06 10:51:16 2013 +0200 @@ -0,0 +1,98 @@ +/* + * Copyright 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * . + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.plugin.validator.internal; + +import java.io.File; + +import com.redhat.thermostat.plugin.validator.ValidationIssue; + +/** + * + * Exception thrown on XML validation errors of thermostat-plugin.xml files. + * + */ +public abstract class AbstractValidationError implements ValidationIssue { + + private int lineNumber; + private int columnNumber; + private String message; + private File xmlFile; + + public AbstractValidationError(int lineNumber, int columnNumber, String message, File xmlFile) { + this.lineNumber = lineNumber; + this.columnNumber = columnNumber; + this.message = message; + this.xmlFile = xmlFile; + } + + @Override + public int getLineNumber() { + return lineNumber; + } + + @Override + public int getColumnNumber() { + return columnNumber; + } + + @Override + public String getMessage() { + return message; + } + + @Override + public File getXmlFile() { + return xmlFile; + } + + public String toString() { + String LS = System.getProperty("line.separator"); + StringBuilder builder = new StringBuilder(); + + builder.append("[").append(getName()).append("]").append(LS); + builder.append(" Message: ").append(getMessage()).append(LS); + builder.append(" File: ").append(getXmlFile().getPath()).append(LS); + builder.append(" Line number: ").append(getLineNumber()).append(LS); + builder.append(" Column number: ").append(getColumnNumber()).append(LS); + + return builder.toString(); + + } + + public abstract String getName(); + +} diff -r ff7b684c65ef -r 4afcf1274915 thermostat-plugin-validator/src/main/java/com/redhat/thermostat/plugin/validator/internal/ConfigurationValidatorErrorHandler.java --- a/thermostat-plugin-validator/src/main/java/com/redhat/thermostat/plugin/validator/internal/ConfigurationValidatorErrorHandler.java Wed Sep 11 12:44:35 2013 -0400 +++ b/thermostat-plugin-validator/src/main/java/com/redhat/thermostat/plugin/validator/internal/ConfigurationValidatorErrorHandler.java Fri Sep 06 10:51:16 2013 +0200 @@ -36,127 +36,77 @@ package com.redhat.thermostat.plugin.validator.internal; -import java.io.BufferedReader; import java.io.File; -import java.io.FileReader; -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; -import java.util.logging.Logger; +import java.util.ArrayList; +import java.util.List; import org.xml.sax.ErrorHandler; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; -import com.redhat.thermostat.plugin.validator.locale.LocaleResources; -import com.redhat.thermostat.shared.locale.Translate; -import com.redhat.thermostat.common.utils.LoggingUtils; +import com.redhat.thermostat.plugin.validator.ValidationIssue; public class ConfigurationValidatorErrorHandler implements ErrorHandler { - private static final Logger logger = LoggingUtils.getLogger(ConfigurationValidatorErrorHandler.class); - private int warningsErrorsCounter = 0; - private static final Translate translator = LocaleResources.createLocalizer(); private final File pluginXML; - private boolean isCommand; - - private enum ErrorType { - WARNING, - ERROR, - FATAL_ERROR; - } - - public ConfigurationValidatorErrorHandler(File pluginXML, boolean isCommand) { + private final List warnings; + private final List errors; + private final List fatalErrors; + + public ConfigurationValidatorErrorHandler(File pluginXML) { this.pluginXML = pluginXML; - this.isCommand = isCommand; + warnings = new ArrayList<>(); + errors = new ArrayList<>(); + fatalErrors = new ArrayList<>(); } @Override public void warning(SAXParseException exception) throws SAXException { - warningsErrorsCounter++; - printInfo(exception, ErrorType.WARNING); + Warning newWarning = new Warning(exception.getLineNumber(), + exception.getColumnNumber(), + exception.getLocalizedMessage(), + pluginXML); + warnings.add(newWarning); } @Override public void error(SAXParseException exception) throws SAXParseException { - warningsErrorsCounter++; - printInfo(exception, ErrorType.ERROR); + Error newError = new Error(exception.getLineNumber(), + exception.getColumnNumber(), + exception.getLocalizedMessage(), + pluginXML); + errors.add(newError); } @Override public void fatalError(SAXParseException exception) throws SAXParseException { - if (warningsErrorsCounter == 0) { - printInfo(exception, ErrorType.FATAL_ERROR); - logger.info("XML not well formed"); + // Fatal errors will be reported just when no validation warnings and errors happened. + // In this way we avoid wrong messages of bad form for files that have wrong tags not closed properly + if (errors.size() == 0 && warnings.size() == 0) { + FatalError newFatalError = new FatalError(exception.getLineNumber(), + exception.getColumnNumber(), + exception.getLocalizedMessage(), + pluginXML); + fatalErrors.add(newFatalError); } } - private void printInfo(SAXParseException e, ErrorType type) { - int columnNumber = e.getColumnNumber(); - int lineNumber = e.getLineNumber(); - - StringBuilder builder = new StringBuilder(); - - String LS = System.getProperty("line.separator"); - String firstLine = null; - String secondLine = null; - String thirdLine = null; - String errorLine = null; - String pointer = ""; - String absolutePath = pluginXML.getAbsolutePath(); - - Map translateKeys = new HashMap<>(); - translateKeys.put(ErrorType.ERROR, LocaleResources.VALIDATION_ERROR); - translateKeys.put(ErrorType.WARNING, LocaleResources.VALIDATION_WARNING); - translateKeys.put(ErrorType.FATAL_ERROR, LocaleResources.VALIDATION_FATAL_ERROR); - - try { - BufferedReader br = new BufferedReader(new FileReader(absolutePath)); - for (int i = 1; i < lineNumber-3; i++) { - br.readLine(); - } - firstLine = br.readLine(); - secondLine = br.readLine(); - thirdLine = br.readLine(); - errorLine = br.readLine(); - - for (int j = 1; j < columnNumber-1; j++) { - pointer = pointer.concat(" "); - } - pointer = pointer.concat("^"); - br.close(); - } catch (IOException exception) { - // if br fails to close - } - - builder.append(translator.localize( - translateKeys.get(type), - absolutePath, - Integer.toString(lineNumber), - Integer.toString(columnNumber)).getContents()); - - builder.append(formatMessage(e.getLocalizedMessage())).append(LS).append(LS); - builder.append(firstLine).append(LS); - builder.append(secondLine).append(LS); - builder.append(thirdLine).append(LS); - builder.append(errorLine).append(LS); - builder.append(pointer).append(LS); - - if (isCommand) { - System.out.println(builder.toString()); - } else { - logger.info(builder.toString()); - } - + public List getWarnings() { + return warnings; + } + + public List getErrors() { + return errors; } - - private static String formatMessage(String message) { - String[] arguments = message.split("\"http://icedtea.classpath.org/thermostat/plugins/v1.0\":"); - int size = arguments.length; - String output = ""; - for (int i = 0; i < size; i++) { - output=output.concat(arguments[i]); - } - return output; + + public List getFatalErrors() { + return fatalErrors; } + + public boolean hasValidationIssues() { + return errors.size() > 0 || + warnings.size() > 0 || + fatalErrors.size() > 0; + } + } diff -r ff7b684c65ef -r 4afcf1274915 thermostat-plugin-validator/src/main/java/com/redhat/thermostat/plugin/validator/internal/Error.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/thermostat-plugin-validator/src/main/java/com/redhat/thermostat/plugin/validator/internal/Error.java Fri Sep 06 10:51:16 2013 +0200 @@ -0,0 +1,52 @@ +/* + * Copyright 2012, 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * . + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.plugin.validator.internal; + +import java.io.File; + +class Error extends AbstractValidationError { + + Error(int lineNumber, int columnNumber, String message, File xmlFile) { + super(lineNumber, columnNumber, message, xmlFile); + } + + @Override + public String getName() { + return this.getClass().getSimpleName(); + } + +} diff -r ff7b684c65ef -r 4afcf1274915 thermostat-plugin-validator/src/main/java/com/redhat/thermostat/plugin/validator/internal/FatalError.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/thermostat-plugin-validator/src/main/java/com/redhat/thermostat/plugin/validator/internal/FatalError.java Fri Sep 06 10:51:16 2013 +0200 @@ -0,0 +1,51 @@ +/* + * Copyright 2012, 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * . + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.plugin.validator.internal; + +import java.io.File; + +class FatalError extends AbstractValidationError { + + FatalError(int lineNumber, int columnNumber, String message, File xmlFile) { + super(lineNumber, columnNumber, message, xmlFile); + } + + @Override + public String getName() { + return this.getClass().getSimpleName(); + } +} \ No newline at end of file diff -r ff7b684c65ef -r 4afcf1274915 thermostat-plugin-validator/src/main/java/com/redhat/thermostat/plugin/validator/internal/Warning.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/thermostat-plugin-validator/src/main/java/com/redhat/thermostat/plugin/validator/internal/Warning.java Fri Sep 06 10:51:16 2013 +0200 @@ -0,0 +1,52 @@ +/* + * Copyright 2012, 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * . + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.plugin.validator.internal; + +import java.io.File; + +class Warning extends AbstractValidationError { + + Warning(int lineNumber, int columnNumber, String message, File xmlFile) { + super(lineNumber, columnNumber, message, xmlFile); + } + + @Override + public String getName() { + return this.getClass().getSimpleName(); + } + +} \ No newline at end of file diff -r ff7b684c65ef -r 4afcf1274915 thermostat-plugin-validator/src/test/java/com/redhat/thermostat/plugin/validator/PluginValidatorTest.java --- a/thermostat-plugin-validator/src/test/java/com/redhat/thermostat/plugin/validator/PluginValidatorTest.java Wed Sep 11 12:44:35 2013 -0400 +++ b/thermostat-plugin-validator/src/test/java/com/redhat/thermostat/plugin/validator/PluginValidatorTest.java Fri Sep 06 10:51:16 2013 +0200 @@ -37,7 +37,7 @@ package com.redhat.thermostat.plugin.validator; -import static org.junit.Assert.fail; +import static org.junit.Assert.*; import java.io.BufferedWriter; import java.io.File; @@ -46,15 +46,16 @@ import org.junit.Test; + public class PluginValidatorTest { - + @Test public void validateEmptyConfiguration() throws IOException { String config = "\n"; File testFile = createFile("testSystemId", config); PluginValidator validator = new PluginValidator(); try { - validator.validate(testFile, false); + validator.validate(testFile); fail("should not come here"); } catch (PluginConfigurationValidatorException e) { //pass @@ -75,43 +76,20 @@ " \n" + " test\n" + " \n" + - " foo\n" + - " bar\n" + - " baz\n" + + " foo1\n" + + " bar2\n" + + " baz3\n" + " \n" + - " \n" + - " thermostat-foo\n" + - " \n" + " \n" + " \n" + ""; File testFile = createFile("testSystemId", config); PluginValidator validator = new PluginValidator(); - validator.validate(testFile, false); + validator.validate(testFile); + + // Second validation on the same file + validator.validate(testFile); testFile.delete(); - - config = "\n" + - "\n" + - " \n" + - " \n" + - " test\n" + - " \n" + - " foo\n" + - " bar\n" + - " baz\n" + - " \n" + - " \n" + - " thermostat-foo\n" + - " \n" + - " \n" + - " \n" + - ""; - File testFile2 = createFile("testSystemId", config); - PluginValidator validator2 = new PluginValidator(); - validator2.validate(testFile, false); - testFile2.delete(); } catch (PluginConfigurationValidatorException e) { fail("should not reach here, plugin.xml should be validated according to schema"); } @@ -123,18 +101,18 @@ "\n" + - " \n" + + " \n" + " \n" + // Error line " \n" + " test\n" + " \n" + - " foo\n" + - " bar\n" + - " baz\n" + + " foo\n" + // Error line + " bar2\n" + + " baz\n" + // Error line " \n" + - " \n" + - " thermostat-foo\n" + - " \n" + + " \n" + // Error line + " thermostat-foo\n" + // Error line + " \n" + // Error line " \n" + " \n" + ""; @@ -142,7 +120,7 @@ File testFile = createFile("testSystemId", config); PluginValidator validator = new PluginValidator(); try { - validator.validate(testFile, false); + validator.validate(testFile); fail("plugin.xml should not validate according to schema"); } catch (PluginConfigurationValidatorException e) { //pass @@ -191,13 +169,10 @@ " cli\n" + " \n" + " \n" + - " \n \t \nfoo\t \n \n\n" + - " \tbar baz\n\n" + - " buzz\n" + + " foo1\n" + + " bar2\n" + + " baz3\n" + " \n" + - " \n\t\n\t \t\t\n" + - " \t\t\t thermostat-foo\n\t\t\n\n" + - " \n" + " \n" + " \n" + ""; @@ -205,9 +180,9 @@ File testFile = createFile("testSystemId", config); PluginValidator validator = new PluginValidator(); try { - validator.validate(testFile, false); + validator.validate(testFile); } catch (PluginConfigurationValidatorException e) { - fail("should not reach here, plugin.xml should be validated according to schema"); + fail("should not reach here, plugin.xml should be validated according to schema"); } finally { testFile.delete(); } diff -r ff7b684c65ef -r 4afcf1274915 validate-command/command/src/main/java/com/redhat/thermostat/validate/command/ValidateCommand.java --- a/validate-command/command/src/main/java/com/redhat/thermostat/validate/command/ValidateCommand.java Wed Sep 11 12:44:35 2013 -0400 +++ b/validate-command/command/src/main/java/com/redhat/thermostat/validate/command/ValidateCommand.java Fri Sep 06 10:51:16 2013 +0200 @@ -38,7 +38,6 @@ import java.io.File; import java.io.FileNotFoundException; -import java.io.IOException; import com.redhat.thermostat.common.cli.Arguments; import com.redhat.thermostat.common.cli.Command; @@ -47,28 +46,32 @@ import com.redhat.thermostat.common.cli.CommandLineArgumentParseException; import com.redhat.thermostat.plugin.validator.PluginConfigurationValidatorException; import com.redhat.thermostat.plugin.validator.PluginValidator; +import com.redhat.thermostat.plugin.validator.ValidationErrorsFormatter; import com.redhat.thermostat.shared.locale.Translate; import com.redhat.thermostat.validate.command.locale.LocaleResources; public class ValidateCommand implements Command { private static final Translate translator = LocaleResources.createLocalizer(); + private PluginValidator validator; public void run(CommandContext ctx) throws CommandException { Arguments args = ctx.getArguments(); - PluginValidator validator = new PluginValidator(); + validator = new PluginValidator(); File pluginFile = null; String argString = null; try { argString = args.getNonOptionArguments().get(0); pluginFile = new File(argString); - validator.validate(pluginFile, true); + validator.validate(pluginFile); ctx.getConsole().getOutput().println(translator.localize( LocaleResources.VALIDATION_SUCCESSFUL, pluginFile.getAbsolutePath()) .getContents()); } catch (PluginConfigurationValidatorException e) { + ValidationErrorsFormatter formatter = new ValidationErrorsFormatter(); + ctx.getConsole().getError().println(formatter.format(e.getAllErrors())); ctx.getConsole().getError().println(translator.localize( LocaleResources.VALIDATION_FAILED, pluginFile.getAbsolutePath()) .getContents()); diff -r ff7b684c65ef -r 4afcf1274915 validate-command/command/src/test/java/com/redhat/thermostat/validate/command/ValidateCommandTest.java --- a/validate-command/command/src/test/java/com/redhat/thermostat/validate/command/ValidateCommandTest.java Wed Sep 11 12:44:35 2013 -0400 +++ b/validate-command/command/src/test/java/com/redhat/thermostat/validate/command/ValidateCommandTest.java Fri Sep 06 10:51:16 2013 +0200 @@ -36,7 +36,8 @@ package com.redhat.thermostat.validate.command; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -95,11 +96,10 @@ list.add(fileName); cmd.run(ctxt); + String errorOutput = new String(errorBaos.toByteArray()); + String validateOutput = buildErrorMessage(); - String actual = new String(errorBaos.toByteArray()); - String expected = "Validation failed for file " + fileName + "\n\n"; - assertEquals(expected, actual); - + assertEquals(validateOutput, errorOutput); assertEquals("", new String(outputBaos.toByteArray())); } @@ -143,5 +143,47 @@ public void testStorageRequired() { assertFalse(cmd.isStorageRequired()); } + + private String buildErrorMessage() { + String LS = System.getProperty("line.separator"); + StringBuilder builder = new StringBuilder(); + + + builder.append("Error in file ").append(fileName).append(":10.60").append(LS); + builder.append("cvc-complex-type.2.4.b: The content of element 'bundle' is not complete. One of '{version}' is expected.").append(LS).append(LS); + + builder.append(" test").append(LS); + builder.append(" ").append(LS); + builder.append(" foo1").append(LS); + builder.append(" bar").append(LS); + builder.append(" ^").append(LS); + builder.append("Error in file ").append(fileName).append(":11.29").append(LS); + builder.append("cvc-complex-type.2.3: Element 'bundle' cannot have character [children], because the type's content type is element-only.").append(LS).append(LS); + + builder.append(" ").append(LS); + builder.append(" foo1").append(LS); + builder.append(" bar").append(LS); + builder.append(" baz").append(LS); + builder.append(" ^").append(LS); + builder.append("Error in file ").append(fileName).append(":11.29").append(LS); + builder.append("cvc-complex-type.2.4.b: The content of element 'bundle' is not complete. One of '{symbolic-name}' is expected.").append(LS).append(LS); + + builder.append(" ").append(LS); + builder.append(" foo1").append(LS); + builder.append(" bar").append(LS); + builder.append(" baz").append(LS); + builder.append(" ^").append(LS); + builder.append("Error in file ").append(fileName).append(":13.21").append(LS); + builder.append("cvc-complex-type.2.4.d: Invalid content was found starting with element 'dependencies'. No child element is expected at this point.").append(LS).append(LS); + + builder.append(" bar").append(LS); + builder.append(" baz").append(LS); + builder.append(" ").append(LS); + builder.append(" ").append(LS); + builder.append(" ^").append(LS).append(LS); + builder.append("Validation failed for file ").append(fileName).append(LS).append(LS); + + return builder.toString(); + } } diff -r ff7b684c65ef -r 4afcf1274915 validate-command/command/src/test/resources/correctPlugin.xml --- a/validate-command/command/src/test/resources/correctPlugin.xml Wed Sep 11 12:44:35 2013 -0400 +++ b/validate-command/command/src/test/resources/correctPlugin.xml Fri Sep 06 10:51:16 2013 +0200 @@ -6,13 +6,10 @@ test - foo - bar - baz + foo1 + bar2 + baz3 - - thermostat-foo - diff -r ff7b684c65ef -r 4afcf1274915 validate-command/command/src/test/resources/incorrectPlugin.xml --- a/validate-command/command/src/test/resources/incorrectPlugin.xml Wed Sep 11 12:44:35 2013 -0400 +++ b/validate-command/command/src/test/resources/incorrectPlugin.xml Fri Sep 06 10:51:16 2013 +0200 @@ -3,12 +3,11 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://icedtea.classpath.org/thermostat/plugins/v1.0 thermostat-plugin.xsd"> - test - foo - bar + foo1 + bar baz diff -r ff7b684c65ef -r 4afcf1274915 validate-command/distribution/thermostat-plugin.xml --- a/validate-command/distribution/thermostat-plugin.xml Wed Sep 11 12:44:35 2013 -0400 +++ b/validate-command/distribution/thermostat-plugin.xml Fri Sep 06 10:51:16 2013 +0200 @@ -53,7 +53,7 @@ com.redhat.thermostat.validate.command${project.version} com.redhat.thermostat.common.core${project.version} - com.redhat.thermostat.shared.config${project.version} + com.redhat.thermostat.configuration${project.version} com.redhat.thermostat.plugin.validator${project.version}