Repository: redpen-cc/redpen
Pull request #788 (2017-06-22 12:03:13)

Add error level to VdalitaorConfiguration
Ref #643 

coveralls: 
[![Coverage Status](https://:/builds/12084313/badge)](https://:/builds/12084313)

Coverage increased (+0.02%) to 91.026% when pulling **0e59e0661a99ae0a4a4fe0bbf153c23a7ecf1dda on takahi-i:add-severity** into **f63cc623dcd175f98972be6b2d41c58857c85d63 on redpen-cc:master**.

coveralls: 
[![Coverage Status](https://:/builds/12115013/badge)](https://:/builds/12115013)

Coverage increased (+0.02%) to 91.026% when pulling **d67180cdaf0e78a91ff30a9c40ff7cd7e5800355 on takahi-i:add-severity** into **f63cc623dcd175f98972be6b2d41c58857c85d63 on redpen-cc:master**.

coveralls: 
[![Coverage Status](https://:/builds/12116404/badge)](https://:/builds/12116404)

Coverage increased (+0.03%) to 91.044% when pulling **8c0e9f18b0449f48ffce2cca5d3802137a82b157 on takahi-i:add-severity** into **f63cc623dcd175f98972be6b2d41c58857c85d63 on redpen-cc:master**.


Patch:
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index a9b29e0a..80e710e6 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -20,3 +20,4 @@
 * Haruki Okada
 * nmurakami
 * taisa831
+* Tomoko Uchida
diff --git a/README.md b/README.md
index ea21e3bb..f9bc1a10 100644
--- a/README.md
+++ b/README.md
@@ -74,13 +74,16 @@ Deploy
 ======
 [![Deploy](https://www.herokucdn.com/deploy/button.png)](https://heroku.com/deploy)
 
-Links
-======
+Supports
+=========
 
 * [Mailing List](https://groups.google.com/forum/#!forum/redpen-validator)
 * [Docs](http://redpen.cc/docs.html)
 * [JavaDoc](http://redpen.cc/javadoc/latest/index.html)
 * [Blog](http://blog.redpen.cc)
+* Chat
+  * [English](https://gitter.im/redpen-cc/redpen)
+  * [Japanese](https://gitter.im/redpen-cc/redpen/redpen-ja)
 
 Acknowledgements
 ================
diff --git a/redpen-cli/sample/conf/redpen-conf-en.xml b/redpen-cli/sample/conf/redpen-conf-en.xml
index 85425cef..6ce7807f 100644
--- a/redpen-cli/sample/conf/redpen-conf-en.xml
+++ b/redpen-cli/sample/conf/redpen-conf-en.xml
@@ -20,7 +20,7 @@
         <validator name="WeakExpression"/>
 
         <!--Rules on sections and paragraphs-->
-        <validator name="ParagraphNumber"/>
+        <validator name="ParagraphNumber" level="info"/>
         <validator name="SpaceBeginningOfSentence" />
         <validator name="ParenthesizedSentence"/>
         <validator name="EmptySection" />
@@ -30,8 +30,8 @@
 
         <!--Rules on symbols and terminologies-->
         <validator name="InvalidSymbol"/>
-        <validator name="Spelling"/>
-        <validator name="WordFrequency"/>
+        <validator name="Spelling" level="info"/>
+        <validator name="WordFrequency" level="info"/>
         <validator name="Hyphenation"/>
         <validator name="NumberFormat"/>
 
diff --git a/redpen-cli/sample/conf/redpen-conf-ja.xml b/redpen-cli/sample/conf/redpen-conf-ja.xml
index 06f997bf..8ea8f020 100644
--- a/redpen-cli/sample/conf/redpen-conf-ja.xml
+++ b/redpen-cli/sample/conf/redpen-conf-ja.xml
@@ -4,8 +4,8 @@
         <validator name="SentenceLength">
             <property name="max_len" value="100"/>
         </validator>
-        <validator name="CommaNumber" />
-        <validator name="HeaderLength" />
+        <validator name="CommaNumber"/>
+        <validator name="HeaderLength"/>
 
         <!--Rules on expressions-->
         <validator name="SuccessiveWord" />
@@ -23,7 +23,7 @@
         <!--Rules on symbols and terminologies-->
         <validator name="InvalidSymbol"/>
         <validator name="KatakanaEndHyphen"/>
-        <validator name="KatakanaSpellCheck"/>
+        <validator name="KatakanaSpellCheck" level="info"/>
         <validator name="SpaceBetweenAlphabeticalWord" />
         <validator name="ParenthesizedSentence">
             <property name="max_count" value="3"/>
@@ -35,7 +35,7 @@
         <validator name="SectionLength">
             <property name="max_num" value="1500"/>
         </validator>
-        <validator name="EmptySection" />
+        <validator name="EmptySection" level="Info"/>
         <validator name="GappedSection" />
         <validator name="SectionLevel" />
         <validator name="ParagraphNumber"/>
diff --git a/redpen-core/pom.xml b/redpen-core/pom.xml
index 535ca829..709b694e 100644
--- a/redpen-core/pom.xml
+++ b/redpen-core/pom.xml
@@ -116,7 +116,7 @@
                     <excludes>
                         <exclude>**/**$**</exclude>
                     </excludes>
-                    <argLine>${argLine} -Dfile.encoding=UTF-8</argLine>
+                    <argLine>${argLine} -Dfile.encoding=UTF-8 -Duser.language=en -Duser.country=US</argLine>
                 </configuration>
             </plugin>
             <plugin>
diff --git a/redpen-core/src/main/java/cc/redpen/config/ConfigurationLoader.java b/redpen-core/src/main/java/cc/redpen/config/ConfigurationLoader.java
index b6a99a9c..f6ab5f7b 100644
--- a/redpen-core/src/main/java/cc/redpen/config/ConfigurationLoader.java
+++ b/redpen-core/src/main/java/cc/redpen/config/ConfigurationLoader.java
@@ -72,6 +72,7 @@ public class ConfigurationLoader {
     private static Document toDocument(InputStream input) throws RedPenException {
         DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
         try (BufferedInputStream bis = new BufferedInputStream(input)) {
+            dbFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
             DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
             dBuilder.setErrorHandler(new SAXErrorHandler());
             return dBuilder.parse(bis);
@@ -233,6 +234,7 @@ public class ConfigurationLoader {
                 validatorConfigurations.put(currentValidatorName, currentConfiguration);
                 NodeList propertyElementList = nNode.getChildNodes();
                 extractProperties(currentConfiguration, propertyElementList);
+                extractLevel(currentConfiguration, element);
             } else {
                 LOG.warn("Invalid block: \"" + element.getNodeName() + "\"");
                 LOG.warn("Skip this block ...");
@@ -241,6 +243,12 @@ public class ConfigurationLoader {
         validatorConfigurations.values().forEach(configBuilder::addValidatorConfig);
     }
 
+    private void extractLevel(ValidatorConfiguration currentConfiguration, Element element) {
+        String level = element.getAttribute("level");
+        if (level == null || level.length() == 0) return;
+        currentConfiguration.setLevel(level);
+     }
+
     private void extractProperties(ValidatorConfiguration currentConfiguration,
                                    NodeList propertyElementList) {
         for (int j = 0; j < propertyElementList.getLength(); j++) {
diff --git a/redpen-core/src/main/java/cc/redpen/config/ValidatorConfiguration.java b/redpen-core/src/main/java/cc/redpen/config/ValidatorConfiguration.java
index 6e8df1a4..61a01efd 100644
--- a/redpen-core/src/main/java/cc/redpen/config/ValidatorConfiguration.java
+++ b/redpen-core/src/main/java/cc/redpen/config/ValidatorConfiguration.java
@@ -26,8 +26,29 @@ import java.util.Objects;
  * Configuration for Validators.
  */
 public class ValidatorConfiguration implements Serializable, Cloneable {
+    /**
+     * Define how severe the validation errors are.
+     */
+    public enum LEVEL {
+        INFO, WARN, ERROR;
+
+        Map<String, String> mapping = new HashMap<String, String>() {
+            {
+                put("INFO", "Info");
+                put("WARN", "Warn");
+                put("ERROR", "Error");
+            }
+        };
+
+        @Override
+        public String toString() {
+            return mapping.get(name());
+        }
+    }
+
     private final String configurationName;
     private Map<String, String> properties;
+    private LEVEL level = LEVEL.ERROR;
 
     /**
      * @param name name configuration settings
@@ -40,9 +61,17 @@ public class ValidatorConfiguration implements Serializable, Cloneable {
      * @param name name configuration settings
      * @param properties validator properties
      */
-    public ValidatorConfiguration(String name, Map<String, String> properties) {
+    public ValidatorConfiguration(String name, Map<String, String> properties) { this(name, properties, LEVEL.ERROR); }
+
+    /**
+     * @param name name configuration settings
+     * @param properties validator properties
+     * @param level error level
+     */
+    public ValidatorConfiguration(String name, Map<String, String> properties, LEVEL level) {
         this.configurationName = name;
         this.properties = properties;
+        this.level = level;
     }
 
     /**
@@ -73,6 +102,32 @@ public class ValidatorConfiguration implements Serializable, Cloneable {
         return configurationName;
     }
 
+    /**
+     * Get error level.
+     * @return error level
+     */
+    public LEVEL getLevel() {
+        return level;
+    }
+
+    /**
+     * Set error level.
+     * @param level error level
+     */
+    public ValidatorConfiguration setLevel(String level) {
+        try {
+            setLevel(LEVEL.valueOf(level.toUpperCase()));
+        } catch (IllegalArgumentException e) {
+            throw new RuntimeException("No such a error level as " + level, e);
+        }
+        return this;
+    }
+
+    public ValidatorConfiguration setLevel(LEVEL level) {
+        this.level = level;
+        return this;
+    }
+
     /**
      * Get validator class name
      *
@@ -104,7 +159,8 @@ public class ValidatorConfiguration implements Serializable, Cloneable {
         if (!(o instanceof ValidatorConfiguration)) return false;
         ValidatorConfiguration that = (ValidatorConfiguration)o;
         return Objects.equals(configurationName, that.configurationName) &&
-               Objects.equals(properties, that.properties);
+                Objects.equals(properties, that.properties) &&
+                Objects.equals(level, that.level);
     }
 
     @Override public int hashCode() {
@@ -122,6 +178,7 @@ public class ValidatorConfiguration implements Serializable, Cloneable {
         try {
             ValidatorConfiguration clone = (ValidatorConfiguration)super.clone();
             clone.properties = new HashMap<>(properties);
+            clone.level = level;
             return clone;
         }
         catch (CloneNotSupportedException e) {
diff --git a/redpen-core/src/main/java/cc/redpen/formatter/JSONBySentenceFormatter.java b/redpen-core/src/main/java/cc/redpen/formatter/JSONBySentenceFormatter.java
index 9469e3b5..09fd1e2e 100644
--- a/redpen-core/src/main/java/cc/redpen/formatter/JSONBySentenceFormatter.java
+++ b/redpen-core/src/main/java/cc/redpen/formatter/JSONBySentenceFormatter.java
@@ -73,6 +73,7 @@ public class JSONBySentenceFormatter extends JSONFormatter {
 
         jsonError.put("message", error.getMessage());
         jsonError.put("validator", error.getValidatorName());
+        jsonError.put("level", error.getLevel());
 
         LineOffset startOffset;
         LineOffset endOffset;
diff --git a/redpen-core/src/main/java/cc/redpen/formatter/JSONFormatter.java b/redpen-core/src/main/java/cc/redpen/formatter/JSONFormatter.java
index b921dd59..17e41d30 100644
--- a/redpen-core/src/main/java/cc/redpen/formatter/JSONFormatter.java
+++ b/redpen-core/src/main/java/cc/redpen/formatter/JSONFormatter.java
@@ -89,6 +89,7 @@ public class JSONFormatter extends Formatter {
         jsonError.put("message", error.getMessage());
         jsonError.put("validator", error.getValidatorName());
         jsonError.put("sentenceStartColumnNum", error.getStartColumnNumber());
+        jsonError.put("level", error.getLevel().toString());
         if (error.getStartPosition().isPresent()) {
             jsonError.put("startPosition", asJSON(error.getStartPosition().get()));
         }
diff --git a/redpen-core/src/main/java/cc/redpen/formatter/PlainFormatter.java b/redpen-core/src/main/java/cc/redpen/formatter/PlainFormatter.java
index 1f36f97b..d2a45fd4 100644
--- a/redpen-core/src/main/java/cc/redpen/formatter/PlainFormatter.java
+++ b/redpen-core/src/main/java/cc/redpen/formatter/PlainFormatter.java
@@ -37,9 +37,7 @@ public final class PlainFormatter extends Formatter {
 
     @Override
     public void format(PrintWriter pw, Map<Document, List<ValidationError>> docErrorsMap) throws RedPenException, IOException {
-
         BufferedWriter writer = new BufferedWriter(new PrintWriter(pw));
-
         for (Document document : docErrorsMap.keySet()) {
             List<ValidationError> errors = docErrorsMap.get(document);
             for (ValidationError error : errors) {
@@ -58,7 +56,7 @@ public final class PlainFormatter extends Formatter {
         StringBuilder str = new StringBuilder();
         document.getFileName().ifPresent(e -> str.append(e).append(":"));
         str.append(error.getLineNumber());
-        str.append(": ValidationError[").append(error.getValidatorName()).append("], ");
+        str.append(": Validation" + error.getLevel()).append("[").append(error.getValidatorName()).append("], ");
         str.append(error.getMessage());
         str.append(" at line: ").append(error.getSentence().getContent());
         str.append("\n");
diff --git a/redpen-core/src/main/java/cc/redpen/formatter/XMLFormatter.java b/redpen-core/src/main/java/cc/redpen/formatter/XMLFormatter.java
index 2630884d..711f215a 100644
--- a/redpen-core/src/main/java/cc/redpen/formatter/XMLFormatter.java
+++ b/redpen-core/src/main/java/cc/redpen/formatter/XMLFormatter.java
@@ -114,6 +114,11 @@ public class XMLFormatter extends Formatter {
         Text lineNum = doc.createTextNode(Integer.toString(error.getLineNumber()));
         lineNumberElement.appendChild(lineNum);
 
+        Element errorLevelElement = doc.createElement("level");
+        errorElement.appendChild(errorLevelElement);
+        Text level = doc.createTextNode(error.getLevel().toString());
+        errorLevelElement.appendChild(level);
+
         Element sentenceStartOffset = doc.createElement("sentenceStartColumnNum");
         errorElement.appendChild(sentenceStartOffset);
         Text offset = doc.createTextNode(Integer.toString(error.getStartColumnNumber()));
diff --git a/redpen-core/src/main/java/cc/redpen/validator/ValidationError.java b/redpen-core/src/main/java/cc/redpen/validator/ValidationError.java
index cd1df5fe..d222b112 100644
--- a/redpen-core/src/main/java/cc/redpen/validator/ValidationError.java
+++ b/redpen-core/src/main/java/cc/redpen/validator/ValidationError.java
@@ -17,6 +17,7 @@
  */
 package cc.redpen.validator;
 
+import cc.redpen.config.ValidatorConfiguration;
 import cc.redpen.model.Sentence;
 import cc.redpen.parser.LineOffset;
 
@@ -28,12 +29,13 @@ import java.util.Optional;
  */
 public class ValidationError implements Serializable {
 
-    private static final long serialVersionUID = -7759439419047004667L;
+    private static final long serialVersionUID = -1273191135155157144L;
     private final String message;
     private final String validatorName;
     private final Sentence sentence;
     private final LineOffset startPosition;
     private final LineOffset endPosition;
+    private final ValidatorConfiguration.LEVEL level;
 
     /**
      * Constructor.
@@ -43,13 +45,28 @@ public class ValidationError implements Serializable {
      * @param sentenceWithError sentence containing validation error
      */
     public ValidationError(String validatorName,
-                           String errorMessage,
-                           Sentence sentenceWithError) {
+            String errorMessage,
+            Sentence sentenceWithError) {
+        this(validatorName, errorMessage, sentenceWithError, ValidatorConfiguration.LEVEL.ERROR);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param validatorName    validator name
+     * @param errorMessage      error message
+     * @param sentenceWithError sentence containing validation error
+     */
+    public ValidationError(String validatorName,
+            String errorMessage,
+            Sentence sentenceWithError,
+            ValidatorConfiguration.LEVEL level) {
         this.message = errorMessage;
         this.validatorName = validatorName;;
         this.sentence = sentenceWithError;
         this.startPosition = null;
         this.endPosition = null;
+        this.level = level;
     }
 
     /**
@@ -63,14 +80,31 @@ public class ValidationError implements Serializable {
      */
     ValidationError(String validatorName, String errorMessage, Sentence sentenceWithError,
             int startPosition, int endPosition) {
+        this(validatorName, errorMessage, sentenceWithError, startPosition, endPosition, ValidatorConfiguration.LEVEL.ERROR);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param validatorName    validator name
+     * @param errorMessage      error message
+     * @param sentenceWithError sentence containing validation error
+     * @param startPosition     position where error starts
+     * @param endPosition       position where error ends
+     */
+    ValidationError(String validatorName, String errorMessage, Sentence sentenceWithError,
+            int startPosition, int endPosition,
+            ValidatorConfiguration.LEVEL level) {
         this.message = errorMessage;
         this.validatorName = validatorName;
         this.sentence = sentenceWithError;
         this.startPosition = sentenceWithError.getOffset(startPosition).get();
         this.endPosition = sentenceWithError.getOffset(endPosition).get();
+        this.level = level;
     }
 
     /**
+     * Constructor.
      *
      * @param validatorClass    validator class
      * @param errorMessage      error message
@@ -80,12 +114,13 @@ public class ValidationError implements Serializable {
      * @deprecated
      */
     ValidationError(Class validatorClass, String errorMessage, Sentence sentenceWithError,
-                    LineOffset startPosition, LineOffset endPosition) {
+            LineOffset startPosition, LineOffset endPosition) {
         this.message = errorMessage;
         this.validatorName = validatorClass.getSimpleName();
         this.sentence = sentenceWithError;
         this.startPosition = startPosition;
         this.endPosition = endPosition;
+        this.level = ValidatorConfiguration.LEVEL.ERROR;
     }
 
     /**
@@ -156,6 +191,14 @@ public class ValidationError implements Serializable {
         return Optional.ofNullable(endPosition);
     }
 
+    /**
+     * Get error level.
+     * @return error level
+     */
+    public ValidatorConfiguration.LEVEL getLevel() {
+        return level;
+    }
+
     @Override
     public String toString() {
         return "ValidationError{" +
@@ -164,6 +207,7 @@ public class ValidationError implements Serializable {
                 ", sentence=" + sentence +
                 ", startPosition=" + startPosition +
                 ", endPosition=" + endPosition +
+                ", level=" + level +
                 '}';
     }
 }
diff --git a/redpen-core/src/main/java/cc/redpen/validator/Validator.java b/redpen-core/src/main/java/cc/redpen/validator/Validator.java
index e8e2ba73..d7fb4672 100644
--- a/redpen-core/src/main/java/cc/redpen/validator/Validator.java
+++ b/redpen-core/src/main/java/cc/redpen/validator/Validator.java
@@ -338,6 +338,13 @@ public abstract class Validator {
         return globalConfig.findFile(relativePath);
     }
 
+    protected ValidatorConfiguration.LEVEL getLevel() {
+        if( config == null) {
+            return ValidatorConfiguration.LEVEL.ERROR;
+        }
+        return config.getLevel();
+    }
+
     /**
      * create a ValidationError for the specified position with specified message
      *
@@ -345,7 +352,7 @@ public abstract class Validator {
      * @param sentenceWithError sentence
      */
     protected void addError(String message, Sentence sentenceWithError) {
-        errors.add(new ValidationError(this.validatorName, message, sentenceWithError));
+        errors.add(new ValidationError(this.validatorName, message, sentenceWithError, getLevel()));
     }
 
     /**
@@ -358,11 +365,9 @@ public abstract class Validator {
      */
     protected void addErrorWithPosition(String message, Sentence sentenceWithError,
                                         int start, int end) {
-        errors.add(new ValidationError(this.validatorName, message, sentenceWithError, start, end));
+        errors.add(new ValidationError(this.validatorName, message, sentenceWithError, start, end, getLevel()));
     }
 
-
-
     /**
      * create a ValidationError for the specified position with localized default error message
      *
@@ -370,7 +375,7 @@ public abstract class Validator {
      * @param args              objects to format
      */
     protected void addLocalizedError(Sentence sentenceWithError, Object... args) {
-        errors.add(new ValidationError(this.validatorName, getLocalizedErrorMessage(null, args), sentenceWithError));
+        errors.add(new ValidationError(this.validatorName, getLocalizedErrorMessage(null, args), sentenceWithError, getLevel()));
     }
 
     /**
@@ -381,7 +386,7 @@ public abstract class Validator {
      * @param args              objects to format
      */
     protected void addLocalizedError(String messageKey, Sentence sentenceWithError, Object... args) {
-        errors.add(new ValidationError(this.validatorName, getLocalizedErrorMessage(messageKey, args), sentenceWithError));
+        errors.add(new ValidationError(this.validatorName, getLocalizedErrorMessage(messageKey, args), sentenceWithError, getLevel()));
     }
 
     /**
@@ -395,7 +400,8 @@ public abstract class Validator {
                 sentenceWithError,
                 token.getOffset(),
                 token.getOffset() + token.getSurface().length(),
-                token.getSurface()
+                token.getSurface(),
+                getLevel()
         );
     }
 
@@ -423,7 +429,7 @@ public abstract class Validator {
      */
     protected void addLocalizedErrorWithPosition(String messageKey, Sentence sentenceWithError,
                                                  int start, int end, Object... args) {
-        errors.add(new ValidationError(this.validatorName, getLocalizedErrorMessage(messageKey, args), sentenceWithError, start, end));
+        errors.add(new ValidationError(this.validatorName, getLocalizedErrorMessage(messageKey, args), sentenceWithError, start, end, getLevel()));
     }
 
     /**
diff --git a/redpen-core/src/main/resources/default-resources/spellchecker/spellchecker-en.dat b/redpen-core/src/main/resources/default-resources/spellchecker/spellchecker-en.dat
index 362299d1..f7b67e24 100644
--- a/redpen-core/src/main/resources/default-resources/spellchecker/spellchecker-en.dat
+++ b/redpen-core/src/main/resources/default-resources/spellchecker/spellchecker-en.dat
@@ -109626,8 +109626,8 @@ severeness's
 severer
 severest
 severing
-severity
-severity's
+level
+level's
 Severn
 Severn's
 severs
@@ -137162,4 +137162,4 @@ Zyuganov's
 Zzz
 AsciiDoc
 Asciidoctor
-Re:VIEW
\ No newline at end of file
+Re:VIEW


Test patch:
diff --git a/redpen-core/src/test/java/cc/redpen/config/ConfigurationLoaderTest.java b/redpen-core/src/test/java/cc/redpen/config/ConfigurationLoaderTest.java
index 32ecaaff..53b4161c 100644
--- a/redpen-core/src/test/java/cc/redpen/config/ConfigurationLoaderTest.java
+++ b/redpen-core/src/test/java/cc/redpen/config/ConfigurationLoaderTest.java
@@ -17,12 +17,19 @@
  */
 package cc.redpen.config;
 
+import cc.redpen.RedPen;
 import cc.redpen.RedPenException;
+import cc.redpen.model.Document;
+import cc.redpen.model.Sentence;
+import cc.redpen.validator.ValidationError;
 import org.junit.Test;
 
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
 
 import static cc.redpen.config.SymbolType.*;
 import static org.junit.Assert.assertEquals;
@@ -435,4 +442,58 @@ public class ConfigurationLoaderTest {
         assertEquals("300",
                 configuration.getValidatorConfigs().get(0).getProperty("max_length"));
     }
+
+    @Test
+    public void testValidatorConfigurationWithErrorLevel() throws RedPenException {
+        String sampleConfigString =
+                "<redpen-conf lang=\"ja\">" +
+                        "<validators>" +
+                        "<validator name=\"SentenceLength\" level=\"INFO\" />" +
+                        "</validators>" +
+                        "</redpen-conf>";
+
+        Configuration configuration = new ConfigurationLoader().loadFromString(sampleConfigString);
+
+        assertNotNull(configuration);
+        assertEquals(1, configuration.getValidatorConfigs().size());
+        assertEquals("SentenceLength",
+                configuration.getValidatorConfigs().get(0).getConfigurationName());
+        assertEquals(ValidatorConfiguration.LEVEL.INFO,
+                configuration.getValidatorConfigs().get(0).getLevel());
+    }
+
+    @Test(expected = RuntimeException.class)
+    public void testValidatorConfigurationWithInvalidErrorLevel() throws RedPenException {
+        String sampleConfigString =
+                "<redpen-conf lang=\"ja\">" +
+                        "<validators>" +
+                        "<validator name=\"SentenceLength\" level=\"FOOBAR\" />" +
+                        "</validators>" +
+                        "</redpen-conf>";
+
+        Configuration configuration = new ConfigurationLoader().loadFromString(sampleConfigString);
+    }
+
+    @Test
+    public void testSpecifiedErrorLevelComeInErrors() throws RedPenException {
+        String sampleConfigString =
+                "<redpen-conf lang=\"ja\">" +
+                        "<validators>" +
+                        "<validator name=\"SentenceLength\" level=\"INFO\" />" +
+                        "</validators>" +
+                        "</redpen-conf>";
+
+        Configuration config = new ConfigurationLoader().loadFromString(sampleConfigString);
+        List<Document> documents = new ArrayList<>();documents.add(
+                Document.builder()
+                        .addSection(1)
+                        .addParagraph()
+                        .addSentence(new Sentence("This is a long long long long long long long long long long long long long long long long long long long long long long long long sentence", 1))
+                        .build());
+
+        RedPen redPen = new RedPen(config);
+        Map<Document, List<ValidationError>> errors = redPen.validate(documents);
+        assertEquals(1, errors.get(documents.get(0)).size());
+        assertEquals(ValidatorConfiguration.LEVEL.INFO, errors.get(documents.get(0)).get(0).getLevel());
+    }
 }
diff --git a/redpen-core/src/test/java/cc/redpen/config/ValidatorConfigurationTest.java b/redpen-core/src/test/java/cc/redpen/config/ValidatorConfigurationTest.java
index 22da95e5..f57a24d4 100644
--- a/redpen-core/src/test/java/cc/redpen/config/ValidatorConfigurationTest.java
+++ b/redpen-core/src/test/java/cc/redpen/config/ValidatorConfigurationTest.java
@@ -16,12 +16,15 @@ public class ValidatorConfigurationTest {
 
     assertNotSame(conf.getProperties(), clone.getProperties());
     assertEquals(conf.getProperties(), clone.getProperties());
+
+    assertNotSame(conf.getProperties(), clone.getProperties());
+    assertEquals(conf.getLevel(), clone.getLevel());
   }
 
   @Test
   public void equals() throws Exception {
-    ValidatorConfiguration conf = new ValidatorConfiguration("test").addProperty("foo", "bar");
-    ValidatorConfiguration conf2 = new ValidatorConfiguration("test").addProperty("foo", "bar");
+    ValidatorConfiguration conf = new ValidatorConfiguration("test").addProperty("foo", "bar").setLevel(ValidatorConfiguration.LEVEL.ERROR);
+    ValidatorConfiguration conf2 = new ValidatorConfiguration("test").addProperty("foo", "bar").setLevel(ValidatorConfiguration.LEVEL.ERROR);
     assertEquals(conf, conf2);
   }
 
@@ -38,4 +41,11 @@ public class ValidatorConfigurationTest {
     ValidatorConfiguration conf2 = new ValidatorConfiguration("test2");
     assertFalse(conf.equals(conf2));
   }
-}
\ No newline at end of file
+
+  @Test
+  public void equals_levels() throws Exception {
+    ValidatorConfiguration conf = new ValidatorConfiguration("test").setLevel(ValidatorConfiguration.LEVEL.INFO);
+    ValidatorConfiguration conf2 = new ValidatorConfiguration("test2").setLevel(ValidatorConfiguration.LEVEL.INFO);;
+    assertFalse(conf.equals(conf2));
+  }
+}
diff --git a/redpen-core/src/test/java/cc/redpen/formatter/JSONBySentenceFormatterTest.java b/redpen-core/src/test/java/cc/redpen/formatter/JSONBySentenceFormatterTest.java
index 807c7be2..7a415409 100644
--- a/redpen-core/src/test/java/cc/redpen/formatter/JSONBySentenceFormatterTest.java
+++ b/redpen-core/src/test/java/cc/redpen/formatter/JSONBySentenceFormatterTest.java
@@ -49,6 +49,7 @@ public class JSONBySentenceFormatterTest extends Validator {
         JSONObject error = sentenceErrors.getJSONArray("errors").getJSONObject(0);
         assertEquals("json by sentence test error", error.getString("message"));
         assertEquals("JSONBySentenceFormatterTest", error.getString("validator"));
+        assertEquals("Error", error.getString("level"));
     }
 
     @Test
@@ -82,6 +83,36 @@ public class JSONBySentenceFormatterTest extends Validator {
         assertEquals(1, sentenceErrors.getJSONArray("errors").length());
         JSONObject error = sentenceErrors.getJSONArray("errors").getJSONObject(0);
         assertEquals("InvalidSymbol", error.getString("validator"));
+        assertEquals("Error", error.getString("level"));
+    }
+
+    @Test
+    public void testFormatDocumentsSettingErrorLevel() throws RedPenException, JSONException {
+        String sampleText = "This is a good day。"; // invalid end of sentence symbol
+        Configuration conf = Configuration.builder().build();
+        Configuration configuration = Configuration.builder()
+                .addValidatorConfig(new ValidatorConfiguration("InvalidSymbol").setLevel(ValidatorConfiguration.LEVEL.INFO))
+                .build();
+
+        List<Document> documents = new ArrayList<>();
+        DocumentParser parser = DocumentParser.MARKDOWN;
+        documents.add(parser.parse(sampleText,
+                new SentenceExtractor(conf.getSymbolTable()), conf.getTokenizer()));
+        RedPen redPen = new RedPen(configuration);
+        List<ValidationError> errors = redPen.validate(documents).get(documents.get(0));
 
+        JSONFormatter formatter = new JSONBySentenceFormatter();
+        String resultString = formatter.format(new cc.redpen.model.Document.DocumentBuilder(
+                new WhiteSpaceTokenizer()).build(), errors);
+
+        JSONObject jsonObject = new JSONObject(resultString);
+        JSONArray jsonErrors = jsonObject.getJSONArray("errors");
+        assertEquals(1, jsonErrors.length());
+        JSONObject sentenceErrors = jsonErrors.getJSONObject(0);
+        assertNotNull(sentenceErrors.getJSONArray("errors"));
+        assertEquals(1, sentenceErrors.getJSONArray("errors").length());
+        JSONObject error = sentenceErrors.getJSONArray("errors").getJSONObject(0);
+        assertEquals("InvalidSymbol", error.getString("validator"));
+        assertEquals("Info", error.getString("level"));
     }
 }
diff --git a/redpen-core/src/test/java/cc/redpen/formatter/JSONFormatterTest.java b/redpen-core/src/test/java/cc/redpen/formatter/JSONFormatterTest.java
index f8d2487b..1f6fc5a7 100644
--- a/redpen-core/src/test/java/cc/redpen/formatter/JSONFormatterTest.java
+++ b/redpen-core/src/test/java/cc/redpen/formatter/JSONFormatterTest.java
@@ -59,7 +59,8 @@ public class JSONFormatterTest extends Validator {
         assertEquals("testing JSONFormatter", jsonErrors.getJSONObject(0).getString("sentence"));
         assertEquals("json test error", jsonErrors.getJSONObject(0).getString("message"));
         assertEquals(1, jsonErrors.getJSONObject(0).getInt("lineNum"));
-        assertEquals("{\"document\":\"docName\",\"errors\":[{\"sentence\":\"testing JSONFormatter\",\"validator\":\"JSONFormatterTest\",\"lineNum\":1,\"sentenceStartColumnNum\":0,\"message\":\"json test error\"}]}", result);
+        assertEquals("Error", jsonErrors.getJSONObject(0).getString("level"));
+        assertEquals("{\"document\":\"docName\",\"errors\":[{\"sentence\":\"testing JSONFormatter\",\"level\":\"Error\",\"validator\":\"JSONFormatterTest\",\"lineNum\":1,\"sentenceStartColumnNum\":0,\"message\":\"json test error\"}]}", result);
     }
 
     @Test
@@ -73,7 +74,7 @@ public class JSONFormatterTest extends Validator {
         documentListMap.put(document, errors);
 
         String result = formatter.format(documentListMap);
-        assertEquals("[{\"document\":\"docName\",\"errors\":[{\"sentence\":\"testing JSONFormatter\",\"validator\":\"JSONFormatterTest\",\"lineNum\":1,\"sentenceStartColumnNum\":0,\"message\":\"json test error\"}]}]", result);
+        assertEquals("[{\"document\":\"docName\",\"errors\":[{\"sentence\":\"testing JSONFormatter\",\"level\":\"Error\",\"validator\":\"JSONFormatterTest\",\"lineNum\":1,\"sentenceStartColumnNum\":0,\"message\":\"json test error\"}]}]", result);
         JSONArray jsonArray = new JSONArray(result);
         assertTrue(jsonArray.length() == 1);
         JSONObject jsonObject = jsonArray.getJSONObject(0);
@@ -117,4 +118,28 @@ public class JSONFormatterTest extends Validator {
         assertEquals("19", jsonErrors.getJSONObject(0).getJSONObject("endPosition").getString("offset"));
     }
 
+    @Test
+    public void testFormatDocumentsSettingErrorLevel() throws RedPenException, JSONException {
+        String sampleText = "This is a good day。"; // invalid end of sentence symbol
+        Configuration conf = Configuration.builder().build();
+        Configuration configuration = Configuration.builder()
+                .addValidatorConfig(new ValidatorConfiguration("InvalidSymbol").setLevel(ValidatorConfiguration.LEVEL.INFO))
+                .build();
+
+        List<Document> documents = new ArrayList<>();
+        DocumentParser parser = DocumentParser.MARKDOWN;
+        documents.add(parser.parse(sampleText,
+                new SentenceExtractor(conf.getSymbolTable()), conf.getTokenizer()));
+        RedPen redPen = new RedPen(configuration);
+        List<ValidationError> errors = redPen.validate(documents).get(documents.get(0));
+
+        JSONFormatter formatter = new JSONFormatter();
+        String resultString = formatter.format(new cc.redpen.model.Document.DocumentBuilder(
+                new WhiteSpaceTokenizer()).build(), errors);
+        JSONObject jsonObject = new JSONObject(resultString);
+        JSONArray jsonErrors = jsonObject.getJSONArray("errors");
+        assertEquals(1, jsonErrors.length());
+        assertEquals(sampleText, jsonErrors.getJSONObject(0).getString("sentence"));
+        assertEquals("Info", jsonErrors.getJSONObject(0).getString("level"));
+    }
 }
diff --git a/redpen-core/src/test/java/cc/redpen/formatter/PlainFormatterTest.java b/redpen-core/src/test/java/cc/redpen/formatter/PlainFormatterTest.java
index df36a801..120212e9 100644
--- a/redpen-core/src/test/java/cc/redpen/formatter/PlainFormatterTest.java
+++ b/redpen-core/src/test/java/cc/redpen/formatter/PlainFormatterTest.java
@@ -17,11 +17,16 @@
  */
 package cc.redpen.formatter;
 
+import cc.redpen.RedPenException;
+import cc.redpen.config.Configuration;
+import cc.redpen.config.ValidatorConfiguration;
 import cc.redpen.model.Document;
 import cc.redpen.model.Sentence;
 import cc.redpen.tokenizer.WhiteSpaceTokenizer;
 import cc.redpen.validator.ValidationError;
 import cc.redpen.validator.Validator;
+import cc.redpen.validator.ValidatorFactory;
+import org.junit.Before;
 import org.junit.Test;
 
 import java.util.ArrayList;
@@ -29,14 +34,19 @@ import java.util.Arrays;
 import java.util.List;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 
 public class PlainFormatterTest extends Validator {
+    @Before
+    public void setUp() throws Exception {
+        formatter = new PlainFormatter();
+    }
+
     @Test
     public void testConvertValidationError() {
         List<ValidationError> errors = new ArrayList<>();
         setErrorList(errors);
         addLocalizedError(new Sentence("This is a sentence", 0));
-        Formatter formatter = new PlainFormatter();
         Document document = new cc.redpen.model.Document.DocumentBuilder(new WhiteSpaceTokenizer())
                 .setFileName("foobar.md").build();
         List<ValidationError> validationErrors = Arrays.asList(errors.get(0));
@@ -49,10 +59,27 @@ public class PlainFormatterTest extends Validator {
         List<ValidationError> errors = new ArrayList<>();
         setErrorList(errors);
         addLocalizedError(new Sentence("This is a sentence", 0));
-        Formatter formatter = new PlainFormatter();
         Document document = new cc.redpen.model.Document.DocumentBuilder(new WhiteSpaceTokenizer()).build();
         List<ValidationError> validationErrors = Arrays.asList(errors.get(0));
         String resultString = formatter.format(document, validationErrors);
         assertEquals("0: ValidationError[PlainFormatterTest], plain test error at line: This is a sentence\n", resultString);
     }
+
+    @Test
+    public void testConvertValidationErrorChangingErrorLevel() throws RedPenException {
+        Configuration config = Configuration.builder()
+                .addValidatorConfig(new ValidatorConfiguration("SentenceLength").setLevel(ValidatorConfiguration.LEVEL.INFO)).build();
+        Validator validator = ValidatorFactory.getInstance(config.getValidatorConfigs().get(0), config);
+        Sentence sentence = new Sentence("This is a long long long long long long long long long long long long" +
+                " long long long long long long long long long long long long long long long long long long long long sentence", 0);
+        List<ValidationError> errors = new ArrayList<>();
+        validator.setErrorList(errors);
+        validator.validate(sentence);
+        Document document = new cc.redpen.model.Document.DocumentBuilder(new WhiteSpaceTokenizer()).build();
+        List<ValidationError> validationErrors = Arrays.asList(errors.get(0));
+        String resultString = formatter.format(document, validationErrors);
+        assertTrue(resultString.indexOf("ValidationInfo[SentenceLength]") > 0 );
+    }
+
+    private Formatter formatter;
 }
diff --git a/redpen-core/src/test/java/cc/redpen/formatter/XMLFormatterTest.java b/redpen-core/src/test/java/cc/redpen/formatter/XMLFormatterTest.java
index feeb7add..282f5964 100644
--- a/redpen-core/src/test/java/cc/redpen/formatter/XMLFormatterTest.java
+++ b/redpen-core/src/test/java/cc/redpen/formatter/XMLFormatterTest.java
@@ -68,6 +68,9 @@ public class XMLFormatterTest extends Validator {
         assertEquals(1, document.getElementsByTagName("lineNum").getLength());
         assertEquals("0",
                 document.getElementsByTagName("lineNum").item(0).getTextContent());
+        assertEquals(1, document.getElementsByTagName("level").getLength());
+        assertEquals("Error",
+                document.getElementsByTagName("level").item(0).getTextContent());
         assertEquals(1, document.getElementsByTagName("sentence").getLength());
         assertEquals("This is a sentence",
                 document.getElementsByTagName("sentence").item(0).getTextContent());
@@ -92,9 +95,12 @@ public class XMLFormatterTest extends Validator {
                 document.getElementsByTagName("message").item(0).getTextContent());
         assertEquals(0, document.getElementsByTagName("file").getLength());
         assertEquals(1, document.getElementsByTagName("lineNum").getLength());
+        assertEquals(1, document.getElementsByTagName("level").getLength());
+        assertEquals(1, document.getElementsByTagName("validator").getLength());
+        assertEquals("Error",
+                document.getElementsByTagName("level").item(0).getTextContent());
         assertEquals("0",
                 document.getElementsByTagName("lineNum").item(0).getTextContent());
-        assertEquals(1, document.getElementsByTagName("validator").getLength());
         assertEquals(this.getClass().getSimpleName(),
                 document.getElementsByTagName("validator").item(0).getTextContent());
     }
@@ -124,9 +130,9 @@ public class XMLFormatterTest extends Validator {
         assertEquals(1, document.getElementsByTagName("message").getLength());
         assertEquals(0, document.getElementsByTagName("file").getLength());
         assertEquals(1, document.getElementsByTagName("lineNum").getLength());
+        assertEquals(1, document.getElementsByTagName("validator").getLength());
         assertEquals("1",
                 document.getElementsByTagName("lineNum").item(0).getTextContent());
-        assertEquals(1, document.getElementsByTagName("validator").getLength());
         assertEquals("InvalidSymbol",
                 document.getElementsByTagName("validator").item(0).getTextContent());
         assertEquals("1,18",
@@ -135,6 +141,30 @@ public class XMLFormatterTest extends Validator {
                 document.getElementsByTagName("errorEndPosition").item(0).getTextContent());
     }
 
+    @Test
+    public void testConvertedValidationErrorChangingLevel() throws RedPenException {
+        String sampleText = "This is a good day。\n"; // invalid end of sentence symbol
+        Configuration conf = Configuration.builder().build();
+        Configuration configuration = Configuration.builder()
+                .addValidatorConfig(new ValidatorConfiguration("InvalidSymbol").setLevel(ValidatorConfiguration.LEVEL.INFO))
+                .build();
+
+        List<cc.redpen.model.Document> documents = new ArrayList<>();
+        DocumentParser parser = DocumentParser.MARKDOWN;
+        documents.add(parser.parse(sampleText,
+                new SentenceExtractor(conf.getSymbolTable()), conf.getTokenizer()));
+        RedPen redPen = new RedPen(configuration);
+        List<ValidationError> errors = redPen.validate(documents).get(documents.get(0));
+
+        XMLFormatter formatter = new XMLFormatter();
+        String resultString = formatter.format(new cc.redpen.model.Document.DocumentBuilder(
+                new WhiteSpaceTokenizer()).build(), errors);
+
+        Document document = extractDocument(resultString);
+        assertEquals("Info",
+                document.getElementsByTagName("level").item(0).getTextContent());
+    }
+
     private Document extractDocument(String resultString) {
         DocumentBuilder docBuilder = null;
         try {