Repository: redpen-cc/redpen
Pull request #560 (2016-03-04 11:12:15)

Validators define properties
This simplifies writing new Validators and defines all supported properties.

* No need to override equals(), hashCode() and toString() - already implemented in Validator.java
* Default properties are now provided in constructor, avoiding more of boiler-plate code
* All default properties will become visible to Intellij and WordPress plugins via ConfigurationBuilder
* Simplify all bundled Validators, so their code is much more readable now


takahi-i: Can you please merge origin master?

Patch:
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 798dfc6a..09f0a711 100644
--- a/redpen-core/src/main/java/cc/redpen/validator/Validator.java
+++ b/redpen-core/src/main/java/cc/redpen/validator/Validator.java
@@ -17,7 +17,6 @@
  */
 package cc.redpen.validator;
 
-
 import cc.redpen.RedPenException;
 import cc.redpen.config.Configuration;
 import cc.redpen.config.SymbolTable;
@@ -36,20 +35,40 @@ import java.io.File;
 import java.text.MessageFormat;
 import java.util.*;
 
+import static java.lang.Boolean.parseBoolean;
+import static java.lang.Double.parseDouble;
+import static java.lang.Integer.parseInt;
+import static java.util.Arrays.asList;
+import static java.util.ResourceBundle.Control.FORMAT_DEFAULT;
+import static java.util.stream.Collectors.toSet;
+import static org.apache.commons.lang3.StringUtils.isEmpty;
+
 /**
  * Validate input document.
  */
 public abstract class Validator {
-
     private static final Logger LOG = LoggerFactory.getLogger(Validator.class);
-    private final static ResourceBundle.Control fallbackControl = ResourceBundle.Control.getNoFallbackControl(ResourceBundle.Control.FORMAT_DEFAULT);
+    private final static ResourceBundle.Control fallbackControl = ResourceBundle.Control.getNoFallbackControl(FORMAT_DEFAULT);
 
+    private Map<String, Object> attributes;
     private ResourceBundle errorMessages = null;
     private ValidatorConfiguration config;
     private Configuration globalConfig;
 
     public Validator() {
+        this(new Object[0]);
+    }
+
+    /**
+     * @param keyValues String key and Object value pairs for supported config attributes.
+     */
+    public Validator(Object...keyValues) {
         setLocale(Locale.getDefault());
+        attributes = new HashMap<>();
+        if (keyValues.length % 2 != 0) throw new IllegalArgumentException("Not enough values specified");
+        for (int i = 0; i < keyValues.length; i+=2) {
+            attributes.put(keyValues[i].toString(), keyValues[i+1]);
+        }
     }
 
     private List<ValidationError> errors;
@@ -116,9 +135,27 @@ public abstract class Validator {
     public final void preInit(ValidatorConfiguration config, Configuration globalConfig) throws RedPenException {
         this.config = config;
         this.globalConfig = globalConfig;
+        initAttributes(config);
         init();
     }
 
+    private void initAttributes(ValidatorConfiguration config) {
+        attributes.forEach((name, defaultValue) -> {
+            String value = config.getAttribute(name);
+            if (value == null) return;
+            if (defaultValue instanceof Integer)
+                attributes.put(name, Integer.valueOf(value));
+            else if (defaultValue instanceof Float)
+                attributes.put(name, Float.valueOf(value));
+            else if (defaultValue instanceof Boolean)
+                attributes.put(name, Boolean.valueOf(value));
+            else if (defaultValue instanceof Set)
+                attributes.put(name, isEmpty(value) ? defaultValue : asList((value).split(",")).stream().map(String::toLowerCase).collect(toSet()));
+            else
+                attributes.put(name, value);
+        });
+    }
+
     void setLocale(Locale locale) {
         // getPackage() would return null for default package
         String packageName = this.getClass().getPackage() != null ? this.getClass().getPackage().getName() : "";
@@ -149,53 +186,52 @@ public abstract class Validator {
     protected void init() throws RedPenException {
     }
 
-    protected Optional<String> getConfigAttribute(String attributeName) {
-        return Optional.ofNullable(config.getAttribute(attributeName));
+    protected int getIntAttribute(String name) {
+        return (int)attributes.get(name);
     }
 
-    protected String getConfigAttribute(String attributeName, String defaultValue) {
-        String value = config.getAttribute(attributeName);
-        if (value != null) {
-            LOG.info("{} is set to {}", attributeName, value);
-            return value;
-        } else {
-            LOG.info("{} is not set. Use default value of {}", attributeName, defaultValue);
-            return defaultValue;
-        }
+    protected float getFloatAttribute(String name) {
+        return (float)attributes.get(name);
     }
 
+    protected String getStringAttribute(String name) {
+        return (String)attributes.get(name);
+    }
 
-    protected int getConfigAttributeAsInt(String attributeName, int defaultValue) {
-        String value = config.getAttribute(attributeName);
-        if (value != null) {
-            LOG.info("{} is set to {}", attributeName, value);
-            return Integer.valueOf(value);
-        } else {
-            LOG.info("{} is not set. Use default value of {}", attributeName, defaultValue);
-            return defaultValue;
-        }
+    protected boolean getBooleanAttribute(String name) {
+        return (boolean)attributes.get(name);
     }
 
-    protected boolean getConfigAttributeAsBoolean(String attributeName, boolean defaultValue) {
-        String value = config.getAttribute(attributeName);
-        if (value != null) {
-            LOG.info("{} is set to {}", attributeName, value);
-            return Boolean.valueOf(value);
-        } else {
-            LOG.info("{} is not set. Use default value of {}", attributeName, defaultValue);
-            return defaultValue;
-        }
+    @SuppressWarnings("unchecked")
+    protected Set<String> getSetAttribute(String name) {
+        return (Set) attributes.get(name);
     }
 
-    protected double getConfigAttributeAsDouble(String attributeName, double defaultValue) {
-        String value = config.getAttribute(attributeName);
-        if (value != null) {
-            LOG.info("{} is set to {}", attributeName, value);
-            return Double.valueOf(value);
-        } else {
-            LOG.info("{} is not set. Use default value of {}", attributeName, defaultValue);
-            return defaultValue;
-        }
+    /** @deprecated Please use constructor with default attributes instead, and then getXXXAttribute() methods */
+    @Deprecated
+    protected Optional<String> getConfigAttribute(String name) {
+        return Optional.ofNullable(config.getAttribute(name));
+    }
+
+    /** @deprecated Please use constructor with default attributes instead, and then getXXXAttribute() methods */
+    @Deprecated
+    protected String getConfigAttribute(String name, String defaultValue) {
+        return getConfigAttribute(name).orElse(defaultValue);
+    }
+
+    @Deprecated
+    protected int getConfigAttributeAsInt(String name, int defaultValue) {
+        return parseInt(getConfigAttribute(name, Integer.toString(defaultValue)));
+    }
+
+    @Deprecated
+    protected boolean getConfigAttributeAsBoolean(String name, boolean defaultValue) {
+        return parseBoolean(getConfigAttribute(name, Boolean.toString(defaultValue)));
+    }
+
+    @Deprecated
+    protected double getConfigAttributeAsDouble(String name, double defaultValue) {
+        return parseDouble(getConfigAttribute(name, Double.toString(defaultValue)));
     }
 
     protected SymbolTable getSymbolTable() {
@@ -374,6 +410,22 @@ public abstract class Validator {
                                                   Optional<LineOffset> start, Optional<LineOffset> end, Object... args) {
         errors.add(new ValidationError(this.getClass(), getLocalizedErrorMessage(messageKey, args), sentenceWithError, start.get(), end.get()));
     }
+
+    @Override public String toString() {
+        return getClass().getSimpleName() + attributes;
+    }
+
+    @Override public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof Validator)) return false;
+        Validator validator = (Validator)o;
+        return Objects.equals(getClass(), validator.getClass()) && Objects.equals(config, validator.config);
+    }
+
+    @Override public int hashCode() {
+        return Objects.hash(getClass(), config);
+    }
+
     /**
      * Resource Extractor loads key-value dictionary
      */
diff --git a/redpen-core/src/main/java/cc/redpen/validator/section/DuplicatedSectionValidator.java b/redpen-core/src/main/java/cc/redpen/validator/section/DuplicatedSectionValidator.java
index 8cbe79eb..6c5da1c3 100644
--- a/redpen-core/src/main/java/cc/redpen/validator/section/DuplicatedSectionValidator.java
+++ b/redpen-core/src/main/java/cc/redpen/validator/section/DuplicatedSectionValidator.java
@@ -17,7 +17,6 @@
  */
 package cc.redpen.validator.section;
 
-import cc.redpen.RedPenException;
 import cc.redpen.model.Paragraph;
 import cc.redpen.model.Section;
 import cc.redpen.model.Sentence;
@@ -30,12 +29,6 @@ import java.util.*;
  * DuplicatedSectionValidator check if there are highly similar section pairs.
  */
 final public class DuplicatedSectionValidator extends Validator {
-    /**
-     * Default threshold (Cosine similarity).
-     */
-    static final public double DEFAULT_SIMILARITY_THRESHOLD = 0.9d;
-
-    private double threhold;
     private List<SectionVector> sectionVectors = new ArrayList<>();
 
     class SectionVector {
@@ -48,6 +41,10 @@ final public class DuplicatedSectionValidator extends Validator {
         }
     }
 
+    public DuplicatedSectionValidator() {
+        super("threshold", 0.9f); // Default threshold (Cosine similarity).
+    }
+
     @Override
     public void preValidate(Section section) {
         Map<String, Integer> sectionVector = extractWordFrequency(section);
@@ -75,7 +72,7 @@ final public class DuplicatedSectionValidator extends Validator {
             Map<String, Integer> candidateVector = sectionVector.sectionVector;
             // NOTE: not header.equals() since the we need check if the references are identical
             if (sectionVector.header != section.getHeaderContent(0) &&
-                    calcCosine(targetVector, candidateVector) > threhold) {
+                    calcCosine(targetVector, candidateVector) > getFloatAttribute("threshold")) {
                 Optional<Sentence> header = Optional.ofNullable(section.getHeaderContent(0));
                 addLocalizedError(header.orElse(section.getParagraph(0).getSentence(0)),
                         sectionVector.header.getLineNumber());
@@ -114,34 +111,4 @@ final public class DuplicatedSectionValidator extends Validator {
             sectionVector.put(surface, ++currentNum);
         }
     }
-
-    @Override
-    protected void init() throws RedPenException {
-        this.threhold = getConfigAttributeAsDouble("threshold", DEFAULT_SIMILARITY_THRESHOLD);
-    }
-
-    @Override
-    public String toString() {
-        return "DuplicatedSectionValidator{" +
-                "sectionVectors=" + sectionVectors +
-                '}';
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-
-        DuplicatedSectionValidator that = (DuplicatedSectionValidator) o;
-
-        if (sectionVectors != null ? !sectionVectors.equals(that.sectionVectors) : that.sectionVectors != null)
-            return false;
-
-        return true;
-    }
-
-    @Override
-    public int hashCode() {
-        return sectionVectors != null ? sectionVectors.hashCode() : 0;
-    }
 }
diff --git a/redpen-core/src/main/java/cc/redpen/validator/section/FrequentSentenceStartValidator.java b/redpen-core/src/main/java/cc/redpen/validator/section/FrequentSentenceStartValidator.java
index fe39876b..4660b2a7 100644
--- a/redpen-core/src/main/java/cc/redpen/validator/section/FrequentSentenceStartValidator.java
+++ b/redpen-core/src/main/java/cc/redpen/validator/section/FrequentSentenceStartValidator.java
@@ -17,7 +17,6 @@
  */
 package cc.redpen.validator.section;
 
-import cc.redpen.RedPenException;
 import cc.redpen.model.Document;
 import cc.redpen.model.Paragraph;
 import cc.redpen.model.Sentence;
@@ -30,29 +29,21 @@ import java.util.Map;
  * Check that too many sentences don't start with the same words
  */
 public class FrequentSentenceStartValidator extends Validator {
-
     private Map<String, Integer> sentenceStartHistogram = new HashMap<>(); // histogram of sentence starts
-    private int leadingWordLimit = 3; // number of words starting each sentence to consider
-    private int percentageThreshold = 25; // maximum percentage of sentences that can start with the same words
-    private int minimumSentenceCount = 5; // must have at least this number of sentences
 
-    @Override
-    protected void init() throws RedPenException {
-        super.init();
-        leadingWordLimit = getConfigAttributeAsInt("leading_word_limit", leadingWordLimit);
-        percentageThreshold = getConfigAttributeAsInt("percentage_threshold", percentageThreshold);
-        minimumSentenceCount = getConfigAttributeAsInt("min_sentence_count", minimumSentenceCount);
+    public FrequentSentenceStartValidator() {
+        super("leading_word_limit", 3, // number of words starting each sentence to consider
+              "percentage_threshold", 25, // maximum percentage of sentences that can start with the same words
+              "min_sentence_count", 5); // must have at least this number of sentences
     }
 
     /**
      * Add sequences of tokens, up to leadingWordLimit, in the histogram
-     *
-     * @param sentence
      */
     private void processSentence(Sentence sentence) {
-        if (sentence.getTokens().size() > leadingWordLimit) {
+        if (sentence.getTokens().size() > getIntAttribute("leading_word_limit")) {
             String leadingPhrase = "";
-            for (int i = 0; i < leadingWordLimit; i++) {
+            for (int i = 0; i < getIntAttribute("leading_word_limit"); i++) {
                 leadingPhrase += (leadingPhrase.isEmpty() ? "" : " ") + sentence.getTokens().get(i).getSurface();
                 Integer count = sentenceStartHistogram.get(leadingPhrase);
                 if (sentenceStartHistogram.get(leadingPhrase) == null) {
@@ -65,7 +56,6 @@ public class FrequentSentenceStartValidator extends Validator {
 
     @Override
     public void validate(Document document) {
-
         // remember the last sentence since we can't add an error without a sentence
         Sentence lastSentence = null;
         int sentenceCount = 0;
@@ -80,11 +70,11 @@ public class FrequentSentenceStartValidator extends Validator {
         }
 
         // make sure we have enough sentences to make this validation worthwhile
-        if (sentenceCount >= minimumSentenceCount) {
+        if (sentenceCount >= getIntAttribute("min_sentence_count")) {
             for (String start : sentenceStartHistogram.keySet()) {
                 int count = sentenceStartHistogram.get(start);
                 int percentage = (int) ((100.0 * (float) count / (float) sentenceStartHistogram.size()));
-                if (percentage > percentageThreshold) {
+                if (percentage > getIntAttribute("percentage_threshold")) {
                     addLocalizedError("SentenceStartTooFrequent", lastSentence, percentage, start);
                 }
             }
diff --git a/redpen-core/src/main/java/cc/redpen/validator/section/ParagraphNumberValidator.java b/redpen-core/src/main/java/cc/redpen/validator/section/ParagraphNumberValidator.java
index c9183478..8a54dbcb 100644
--- a/redpen-core/src/main/java/cc/redpen/validator/section/ParagraphNumberValidator.java
+++ b/redpen-core/src/main/java/cc/redpen/validator/section/ParagraphNumberValidator.java
@@ -17,7 +17,6 @@
  */
 package cc.redpen.validator.section;
 
-import cc.redpen.RedPenException;
 import cc.redpen.model.Section;
 import cc.redpen.validator.Validator;
 
@@ -26,49 +25,15 @@ import cc.redpen.validator.Validator;
  * This validator reports it.
  */
 final public class ParagraphNumberValidator extends Validator {
-    /**
-     * Default maximum number of paragraphs in a section.
-     */
-    @SuppressWarnings("WeakerAccess")
-    public static final int DEFAULT_MAX_PARAGRAPHS_IN_A_SECTION = 5;
-    private int maxParagraphs;
+    public ParagraphNumberValidator() {
+        super("max_num", 5); // Default maximum number of paragraphs in a section.
+    }
 
     @Override
     public void validate(Section section) {
         int paragraphNumber = section.getNumberOfParagraphs();
-        if (maxParagraphs < paragraphNumber) {
+        if (getIntAttribute("max_num") < paragraphNumber) {
             addLocalizedError(section.getJoinedHeaderContents(), paragraphNumber);
         }
     }
-
-    @Override
-    protected void init() throws RedPenException {
-        this.maxParagraphs = getConfigAttributeAsInt("max_num", DEFAULT_MAX_PARAGRAPHS_IN_A_SECTION);
-    }
-
-    @Override
-    public String toString() {
-        return "ParagraphNumberValidator{" +
-                "maxParagraphs=" + maxParagraphs +
-                '}';
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-
-        ParagraphNumberValidator that = (ParagraphNumberValidator) o;
-
-        return maxParagraphs == that.maxParagraphs;
-    }
-
-    @Override
-    public int hashCode() {
-        return maxParagraphs;
-    }
-
-    protected void setMaxParagraphNumber(int max) {
-        this.maxParagraphs = max;
-    }
 }
diff --git a/redpen-core/src/main/java/cc/redpen/validator/section/ParagraphStartWithValidator.java b/redpen-core/src/main/java/cc/redpen/validator/section/ParagraphStartWithValidator.java
index 6aca69b5..063b0bd3 100644
--- a/redpen-core/src/main/java/cc/redpen/validator/section/ParagraphStartWithValidator.java
+++ b/redpen-core/src/main/java/cc/redpen/validator/section/ParagraphStartWithValidator.java
@@ -17,7 +17,6 @@
  */
 package cc.redpen.validator.section;
 
-import cc.redpen.RedPenException;
 import cc.redpen.model.Paragraph;
 import cc.redpen.model.Section;
 import cc.redpen.model.Sentence;
@@ -27,12 +26,9 @@ import cc.redpen.validator.Validator;
  * Validate whether paragraph start as specified.
  */
 public final class ParagraphStartWithValidator extends Validator {
-    /**
-     * Default matter paragraph start with.
-     */
-    @SuppressWarnings("WeakerAccess")
-    public static final String DEFAULT_PARAGRAPH_START_WITH = "";
-    private String beginningOfParagraph = DEFAULT_PARAGRAPH_START_WITH;
+    public ParagraphStartWithValidator() {
+        super("start_from", ""); // Default matter paragraph start with.
+    }
 
     @Override
     public void validate(Section section) {
@@ -41,31 +37,10 @@ public final class ParagraphStartWithValidator extends Validator {
                 continue;
             }
             Sentence firstSentence = currentParagraph.getSentence(0);
-            if (firstSentence.getContent().indexOf(this.beginningOfParagraph) != 0) {
+            if (firstSentence.getContent().indexOf(getStringAttribute("start_from")) != 0) {
                 addLocalizedError(section.getJoinedHeaderContents(),
                         firstSentence.getContent().charAt(0));
             }
         }
     }
-
-    @Override
-    protected void init() throws RedPenException {
-        this.beginningOfParagraph = getConfigAttribute("start_from", DEFAULT_PARAGRAPH_START_WITH);
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-
-        ParagraphStartWithValidator that = (ParagraphStartWithValidator) o;
-
-        return !(beginningOfParagraph != null ? !beginningOfParagraph.equals(that.beginningOfParagraph) : that.beginningOfParagraph != null);
-
-    }
-
-    @Override
-    public int hashCode() {
-        return beginningOfParagraph != null ? beginningOfParagraph.hashCode() : 0;
-    }
 }
diff --git a/redpen-core/src/main/java/cc/redpen/validator/section/SectionLengthValidator.java b/redpen-core/src/main/java/cc/redpen/validator/section/SectionLengthValidator.java
index a75085c1..d04eacc7 100644
--- a/redpen-core/src/main/java/cc/redpen/validator/section/SectionLengthValidator.java
+++ b/redpen-core/src/main/java/cc/redpen/validator/section/SectionLengthValidator.java
@@ -17,22 +17,18 @@
  */
 package cc.redpen.validator.section;
 
-import cc.redpen.RedPenException;
 import cc.redpen.model.Paragraph;
 import cc.redpen.model.Section;
 import cc.redpen.model.Sentence;
 import cc.redpen.validator.Validator;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 /**
  * Validate the length of one section.
  */
 final public class SectionLengthValidator extends Validator {
-    private static final int DEFAULT_MAXIMUM_CHAR_NUMBER_IN_A_SECTION = 1000;
-    private static final Logger LOG =
-            LoggerFactory.getLogger(SectionLengthValidator.class);
-    private int maxSectionCharNumber;
+    public SectionLengthValidator() {
+        super("max_num", 1000);
+    }
 
     @Override
     public void validate(Section section) {
@@ -44,41 +40,8 @@ final public class SectionLengthValidator extends Validator {
             }
         }
 
-        if (sectionCharNumber > maxSectionCharNumber) {
+        if (sectionCharNumber > getIntAttribute("max_num")) {
             addLocalizedError(section.getJoinedHeaderContents(), sectionCharNumber);
         }
     }
-
-    @Override
-    protected void init() throws RedPenException {
-        this.maxSectionCharNumber = getConfigAttributeAsInt("max_num", DEFAULT_MAXIMUM_CHAR_NUMBER_IN_A_SECTION);
-    }
-
-    protected void setMaxSectionLength(int max) {
-        this.maxSectionCharNumber = max;
-    }
-
-    @Override
-    public String toString() {
-        return "SectionLengthValidator{" +
-                "maxSectionCharNumber=" + maxSectionCharNumber +
-                '}';
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-
-        SectionLengthValidator that = (SectionLengthValidator) o;
-
-        if (maxSectionCharNumber != that.maxSectionCharNumber) return false;
-
-        return true;
-    }
-
-    @Override
-    public int hashCode() {
-        return maxSectionCharNumber;
-    }
 }
diff --git a/redpen-core/src/main/java/cc/redpen/validator/section/UnexpandedAcronymValidator.java b/redpen-core/src/main/java/cc/redpen/validator/section/UnexpandedAcronymValidator.java
index 0a1de7e9..264d6603 100644
--- a/redpen-core/src/main/java/cc/redpen/validator/section/UnexpandedAcronymValidator.java
+++ b/redpen-core/src/main/java/cc/redpen/validator/section/UnexpandedAcronymValidator.java
@@ -25,10 +25,9 @@ import cc.redpen.tokenizer.TokenElement;
 import cc.redpen.util.SpellingUtils;
 import cc.redpen.validator.Validator;
 
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
+import java.util.*;
+
+import static java.util.Collections.singletonList;
 
 /**
  * Ensure that there are candidates for expanded versions of acronyms. That is, if there exists an
@@ -36,10 +35,6 @@ import java.util.Set;
  */
 public class UnexpandedAcronymValidator extends Validator {
 
-    private static final int MIN_ACRONYM_LENGTH_DEFAULT = 3; // TLA
-
-    // ignore uppercase words smaller than this length
-    private int minAcronymLength = MIN_ACRONYM_LENGTH_DEFAULT;
     // a set of small words used to join acronyms, such as 'of', 'the' and 'for'
     private Set<String> acronymJoiningWords = new HashSet<>();
     // the set of acronyms we've deduced from sequences of capitalized words
@@ -50,11 +45,18 @@ public class UnexpandedAcronymValidator extends Validator {
     // a dictionary to filter out acronyms that are just uppercase words
     private Set<String> dictionary;
 
+    public UnexpandedAcronymValidator() {
+        super("min_acronym_length", 3); // ignore uppercase words smaller than this length
+    }
+
+    @Override public List<String> getSupportedLanguages() {
+        return singletonList(Locale.ENGLISH.getLanguage());
+    }
+
     @Override
     protected void init() throws RedPenException {
         super.init();
         dictionary = SpellingUtils.getDictionary(getSymbolTable().getLang());
-        minAcronymLength = getConfigAttributeAsInt("min_acronym_length", MIN_ACRONYM_LENGTH_DEFAULT);
         acronymJoiningWords.add("of");
         acronymJoiningWords.add("the");
         acronymJoiningWords.add("for");
@@ -68,6 +70,7 @@ public class UnexpandedAcronymValidator extends Validator {
         for (TokenElement token : sentence.getTokens()) {
             String word = token.getSurface();
             if (!word.trim().isEmpty()) {
+                int minAcronymLength = getIntAttribute("min_acronym_length");
                 if (isAllCapitals(word)) {
                     if ((word.length() >= minAcronymLength) && !dictionary.contains(word.toLowerCase())) {
                         contractedAcronyms.add(word);
@@ -93,9 +96,6 @@ public class UnexpandedAcronymValidator extends Validator {
 
     /**
      * Return true of the supplied word is all in capitals
-     *
-     * @param word
-     * @return
      */
     private boolean isAllCapitals(String word) {
         if (word.length() > 1) {
@@ -111,9 +111,6 @@ public class UnexpandedAcronymValidator extends Validator {
 
     /**
      * Return true if the word only starts with a capital letter
-     *
-     * @param word
-     * @return
      */
     private boolean isCapitalized(String word) {
         if (!word.isEmpty()) {
diff --git a/redpen-core/src/main/java/cc/redpen/validator/section/WordFrequencyValidator.java b/redpen-core/src/main/java/cc/redpen/validator/section/WordFrequencyValidator.java
index c019c2d6..828f4864 100644
--- a/redpen-core/src/main/java/cc/redpen/validator/section/WordFrequencyValidator.java
+++ b/redpen-core/src/main/java/cc/redpen/validator/section/WordFrequencyValidator.java
@@ -38,29 +38,23 @@ public class WordFrequencyValidator extends Validator {
     private static final String DEFAULT_RESOURCE_PATH = "default-resources/word-frequency";
 
     // reference set of word frequencies
-    private Map<String, Double> referenceWordFrequencies = null;
+    private Map<String, Double> referenceWordFrequencies;
     // how the reference words deviate from their average use
-    private Map<String, Double> referenceWordDeviations = null;
+    private Map<String, Double> referenceWordDeviations;
     // the occurance of words in the document
     private Map<String, Integer> documentWordOccurances = new HashMap<>();
     // the number of words in the document
     private int wordCount = 0;
-    // the minimum number of words in the document before this validator activates
-    private int minWordCount = 200;
-    // the standard deviation of the reference words
-    private double referenceStdDeviation = 0;
-    // the maximum deviation from the reference frequency permitted before a validation error is created
-    private double deviationFactor = 3f;
     // one ugly side-effect of using forEach...
     private Sentence lastSentence;
 
+    public WordFrequencyValidator() {
+        super("deviation_factor", 3f, // the maximum deviation from the reference frequency permitted before a validation error is created
+              "min_word_count", 200); // the minimum number of words in the document before this validator activates
+    }
+
     @Override
     protected void init() throws RedPenException {
-        super.init();
-
-        deviationFactor = getConfigAttributeAsDouble("deviation_factor", deviationFactor);
-        minWordCount = getConfigAttributeAsInt("min_word_count", minWordCount);
-
         String defaultDictionaryFile = DEFAULT_RESOURCE_PATH + "/word-frequency-" + getSymbolTable().getLang() + ".dat";
         referenceWordDeviations = new HashMap<>();
         referenceWordFrequencies =
@@ -68,13 +62,11 @@ public class WordFrequencyValidator extends Validator {
                     String[] fields = line.split(" ");
                     set.put(fields[1], Double.valueOf(fields[0]));
                 }).loadCachedFromResource(defaultDictionaryFile, "word frequencies");
-        referenceStdDeviation = getDeviations(referenceWordFrequencies, referenceWordDeviations);
+        initDeviations(referenceWordFrequencies, referenceWordDeviations);
     }
 
     /**
      * Add the words in the sentence to the word frequency histogram
-     *
-     * @param sentence
      */
     private void processSentence(Sentence sentence) {
         for (TokenElement token : sentence.getTokens()) {
@@ -94,13 +86,13 @@ public class WordFrequencyValidator extends Validator {
     }
 
     /**
-     * Return the standard deviation and full the deviations map with root of each word's variance
+     * Initializes the deviations map with root of each word's variance
      *
      * @param histogram  word frequency histogram
      * @param deviations a map that gets filled with each words variance
      * @return the standard deviation of the histogram
      */
-    private double getDeviations(Map<String, Double> histogram, Map<String, Double> deviations) {
+    private double initDeviations(Map<String, Double> histogram, Map<String, Double> deviations) {
         double sum = 0;
         for (String word : histogram.keySet()) {
             sum += histogram.get(word);
@@ -136,7 +128,7 @@ public class WordFrequencyValidator extends Validator {
         }
 
         // don't validate if the document is too short
-        if (wordCount >= minWordCount) {
+        if (wordCount >= getIntAttribute("min_word_count")) {
             Map<String, Double> documentWordFrequencies = new HashMap<>();
 
             documentWordOccurances.forEach((word, count) -> {
@@ -146,7 +138,7 @@ public class WordFrequencyValidator extends Validator {
             DecimalFormat df = new DecimalFormat("0.00");
 
             Map<String, Double> documentDeviations = new HashMap<>();
-            double stddev = getDeviations(documentWordFrequencies, documentDeviations);
+            double stddev = initDeviations(documentWordFrequencies, documentDeviations);
             documentDeviations.forEach((word, deviation) -> {
                 Double referenceDeviation = referenceWordDeviations.get(word);
                 if (referenceDeviation != null) {
@@ -156,7 +148,7 @@ public class WordFrequencyValidator extends Validator {
 
                     // if the word deviates significantly from the norm and also from the reference percentage,
                     // then raise an error
-                    if ((devRatio > 1) && (docPercentage > referencePercentage * deviationFactor)) {
+                    if ((devRatio > 1) && (docPercentage > referencePercentage * getFloatAttribute("deviation_factor"))) {
                         addLocalizedError(
                                 "WordUsedTooFrequently",
                                 lastSentence,
diff --git a/redpen-core/src/main/java/cc/redpen/validator/sentence/CommaNumberValidator.java b/redpen-core/src/main/java/cc/redpen/validator/sentence/CommaNumberValidator.java
index 01714732..9b470322 100644
--- a/redpen-core/src/main/java/cc/redpen/validator/sentence/CommaNumberValidator.java
+++ b/redpen-core/src/main/java/cc/redpen/validator/sentence/CommaNumberValidator.java
@@ -17,28 +17,27 @@
  */
 package cc.redpen.validator.sentence;
 
-
 import cc.redpen.RedPenException;
 import cc.redpen.model.Sentence;
 import cc.redpen.validator.Validator;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
 
 import static cc.redpen.config.SymbolType.COMMA;
 
 /**
  * Validate the number of commas in one sentence.
  */
-final public class CommaNumberValidator extends Validator {
-    private static final Logger LOG = LoggerFactory.getLogger(CommaNumberValidator.class);
-    /**
-     * Default maximum number of comma.
-     */
-    private static final int DEFAULT_MAX_COMMA_NUMBER = 3;
-    private int maxCommaNum = DEFAULT_MAX_COMMA_NUMBER;
+public final class CommaNumberValidator extends Validator {
+
+    public CommaNumberValidator() {
+        super("max_num", 3);
+    }
+
     private char comma;
 
+    private int getMaxNum() {
+        return getIntAttribute("max_num");
+    }
+
     @Override
     public void validate(Sentence sentence) {
         String content = sentence.getContent();
@@ -49,42 +48,13 @@ final public class CommaNumberValidator extends Validator {
             commaCount++;
             content = content.substring(position + 1, content.length());
         }
-        if (maxCommaNum < commaCount) {
-            addLocalizedError(sentence, commaCount, maxCommaNum);
+        if (getMaxNum() < commaCount) {
+            addLocalizedError(sentence, commaCount, getMaxNum());
         }
     }
 
     @Override
     protected void init() throws RedPenException {
-        this.maxCommaNum = getConfigAttributeAsInt("max_num", DEFAULT_MAX_COMMA_NUMBER);
         this.comma = getSymbolTable().getValueOrFallbackToDefault(COMMA);
     }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-
-        CommaNumberValidator that = (CommaNumberValidator) o;
-
-        if (comma != that.comma) return false;
-        if (maxCommaNum != that.maxCommaNum) return false;
-
-        return true;
-    }
-
-    @Override
-    public int hashCode() {
-        int result = maxCommaNum;
-        result = 31 * result + (int) comma;
-        return result;
-    }
-
-    @Override
-    public String toString() {
-        return "CommaNumberValidator{" +
-                "maxCommaNum=" + maxCommaNum +
-                ", comma=" + comma +
-                '}';
-    }
 }
diff --git a/redpen-core/src/main/java/cc/redpen/validator/sentence/ContractionValidator.java b/redpen-core/src/main/java/cc/redpen/validator/sentence/ContractionValidator.java
index ba44383f..ab90ead8 100644
--- a/redpen-core/src/main/java/cc/redpen/validator/sentence/ContractionValidator.java
+++ b/redpen-core/src/main/java/cc/redpen/validator/sentence/ContractionValidator.java
@@ -21,7 +21,12 @@ import cc.redpen.model.Sentence;
 import cc.redpen.tokenizer.TokenElement;
 import cc.redpen.validator.Validator;
 
-import java.util.*;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+
+import static java.util.Collections.singletonList;
 
 /**
  * Validate English contraction in the input document.
@@ -118,12 +123,12 @@ final public class ContractionValidator extends Validator {
         nonContractions.add("they");
     }
 
-    private int foundContractionNum = 0;
-    private int foundNonContractionNum = 0;
+    private int foundContractionNum;
+    private int foundNonContractionNum;
 
     @Override
     public List<String> getSupportedLanguages() {
-        return Arrays.asList(Locale.ENGLISH.getLanguage());
+        return singletonList(Locale.ENGLISH.getLanguage());
     }
 
     @Override
@@ -148,30 +153,4 @@ final public class ContractionValidator extends Validator {
             }
         }
     }
-
-    @Override
-    public String toString() {
-        return "ContractionValidator{" +
-                "foundContractionNum=" + foundContractionNum +
-                ", foundNonContractionNum=" + foundNonContractionNum +
-                '}';
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-
-        ContractionValidator that = (ContractionValidator) o;
-
-        return foundContractionNum == that.foundContractionNum && foundNonContractionNum == that.foundNonContractionNum;
-
-    }
-
-    @Override
-    public int hashCode() {
-        int result = foundContractionNum;
-        result = 31 * result + foundNonContractionNum;
-        return result;
-    }
 }
diff --git a/redpen-core/src/main/java/cc/redpen/validator/sentence/DoubleNegativeValidator.java b/redpen-core/src/main/java/cc/redpen/validator/sentence/DoubleNegativeValidator.java
index f0cb21ac..559368a5 100644
--- a/redpen-core/src/main/java/cc/redpen/validator/sentence/DoubleNegativeValidator.java
+++ b/redpen-core/src/main/java/cc/redpen/validator/sentence/DoubleNegativeValidator.java
@@ -22,8 +22,6 @@ import cc.redpen.model.Sentence;
 import cc.redpen.tokenizer.TokenElement;
 import cc.redpen.validator.ExpressionRule;
 import cc.redpen.validator.Validator;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 import java.util.List;
 import java.util.Locale;
@@ -39,8 +37,6 @@ public class DoubleNegativeValidator extends Validator {
             "default-resources/double-negative/double-negative-expression-";
     private static final String DEFAULT_RESOURCE_WORD_PATH =
             "default-resources/double-negative/double-negative-word-";
-    private static final Logger LOG =
-            LoggerFactory.getLogger(DoubleNegativeValidator.class);
     private Set<ExpressionRule> invalidExpressions;
     private Set<String> negativeWords;
 
diff --git a/redpen-core/src/main/java/cc/redpen/validator/sentence/DoubledJoshiValidator.java b/redpen-core/src/main/java/cc/redpen/validator/sentence/DoubledJoshiValidator.java
index bc28261a..a96c6b76 100644
--- a/redpen-core/src/main/java/cc/redpen/validator/sentence/DoubledJoshiValidator.java
+++ b/redpen-core/src/main/java/cc/redpen/validator/sentence/DoubledJoshiValidator.java
@@ -17,20 +17,24 @@
  */
 package cc.redpen.validator.sentence;
 
-import cc.redpen.RedPenException;
 import cc.redpen.model.Sentence;
 import cc.redpen.tokenizer.TokenElement;
 import cc.redpen.validator.Validator;
 
 import java.util.*;
 
+import static java.util.Collections.emptySet;
+import static java.util.Collections.singletonList;
+
 /**
  * DoubledJoshiValidator checks if the input texts has duplicated Kakujoshi words in one setnences.
  * <br>
  * Note: this validator works only for Japanese texts.
  */
 public class DoubledJoshiValidator extends Validator {
-    private Set<String> skipList = new HashSet<>();
+    public DoubledJoshiValidator() {
+        super("list", emptySet());
+    }
 
     @Override
     public void validate(Sentence sentence) {
@@ -44,21 +48,14 @@ public class DoubledJoshiValidator extends Validator {
                         counts.get(tokenElement.getSurface())+1);
             }
         }
+        Set<String> skipList = getSetAttribute("list");
         counts.entrySet().stream()
                 .filter(e -> e.getValue() >= 2 && !skipList.contains(e.getKey()))
                 .forEach(e -> addLocalizedError(sentence, e.getKey()));
     }
 
-    @Override
-    protected void init() throws RedPenException {
-        //TODO: filter with the kind of Joshi such as Kakujoshi, KakariJoshi etc...
-        getConfigAttribute("list").ifPresent((f -> {
-            skipList.addAll(Arrays.asList(f.split(",")));
-        }));
-    }
-
     @Override
     public List<String> getSupportedLanguages() {
-        return Arrays.asList(Locale.JAPANESE.getLanguage());
+        return singletonList(Locale.JAPANESE.getLanguage());
     }
 }
diff --git a/redpen-core/src/main/java/cc/redpen/validator/sentence/DoubledWordValidator.java b/redpen-core/src/main/java/cc/redpen/validator/sentence/DoubledWordValidator.java
index 5b46e545..6e3634e1 100644
--- a/redpen-core/src/main/java/cc/redpen/validator/sentence/DoubledWordValidator.java
+++ b/redpen-core/src/main/java/cc/redpen/validator/sentence/DoubledWordValidator.java
@@ -21,23 +21,19 @@ import cc.redpen.RedPenException;
 import cc.redpen.model.Sentence;
 import cc.redpen.tokenizer.TokenElement;
 import cc.redpen.validator.Validator;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 import java.util.HashSet;
 import java.util.Optional;
 import java.util.Set;
 
-import static java.util.Arrays.asList;
-import static java.util.stream.Collectors.toList;
-
 final public class DoubledWordValidator extends Validator {
-    private static final Logger LOG =
-            LoggerFactory.getLogger(DoubledWordValidator.class);
     private static final String DEFAULT_RESOURCE_PATH = "default-resources/doubled-word";
 
     private Set<String> skipList;
-    private Set<String> customSkipList;
+
+    public DoubledWordValidator() {
+        super("list", new HashSet<>());
+    }
 
     @Override
     public void validate(Sentence sentence) {
@@ -45,7 +41,7 @@ final public class DoubledWordValidator extends Validator {
         for (TokenElement token : sentence.getTokens()) {
             String currentSurface = token.getSurface().toLowerCase();
             if (surfaces.contains(currentSurface) && !skipList.contains(currentSurface)
-                    && !customSkipList.contains(currentSurface)) {
+              && !getSetAttribute("list").contains(currentSurface)) {
                 addLocalizedErrorFromToken(sentence, token);
             }
             surfaces.add(currentSurface);
@@ -54,49 +50,12 @@ final public class DoubledWordValidator extends Validator {
 
     @Override
     protected void init() throws RedPenException {
-        String defaultDictionaryFile = DEFAULT_RESOURCE_PATH
-                + "/doubled-word-skiplist-" + getSymbolTable().getLang() + ".dat";
+        String defaultDictionaryFile = DEFAULT_RESOURCE_PATH + "/doubled-word-skiplist-" + getSymbolTable().getLang() + ".dat";
         skipList = WORD_LIST.loadCachedFromResource(defaultDictionaryFile, "doubled word skip list");
 
-        customSkipList = new HashSet<>();
-        Optional<String> skipListStr = getConfigAttribute("list");
-        skipListStr.ifPresent(f -> {
-            String normalized = f.toLowerCase();
-            LOG.info("Found user defined skip list.");
-            customSkipList.addAll(asList(normalized.split(",")).stream().map(String::toLowerCase).collect(toList()));
-            LOG.info("Succeeded to add elements of user defined skip list.");
-        });
-
         Optional<String> confFile = getConfigAttribute("dict");
         if (confFile.isPresent()) {
-            customSkipList.addAll(WORD_LIST.loadCachedFromFile(findFile(confFile.get()), "DoubledWordValidator user dictionary"));
+            getSetAttribute("list").addAll(WORD_LIST.loadCachedFromFile(findFile(confFile.get()), "DoubledWordValidator user dictionary"));
         }
     }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-
-        DoubledWordValidator that = (DoubledWordValidator) o;
-
-        if (skipList != null ? !skipList.equals(that.skipList) : that.skipList != null) return false;
-        return !(customSkipList != null ? !customSkipList.equals(that.customSkipList) : that.customSkipList != null);
-
-    }
-
-    @Override
-    public int hashCode() {
-        int result = skipList != null ? skipList.hashCode() : 0;
-        result = 31 * result + (customSkipList != null ? customSkipList.hashCode() : 0);
-        return result;
-    }
-
-    @Override
-    public String toString() {
-        return "DoubledWordValidator{" +
-                "skipList=" + skipList +
-                ", customSkipList=" + customSkipList +
-                '}';
-    }
 }
diff --git a/redpen-core/src/main/java/cc/redpen/validator/sentence/EndOfSentenceValidator.java b/redpen-core/src/main/java/cc/redpen/validator/sentence/EndOfSentenceValidator.java
index f8712861..4eee64da 100644
--- a/redpen-core/src/main/java/cc/redpen/validator/sentence/EndOfSentenceValidator.java
+++ b/redpen-core/src/main/java/cc/redpen/validator/sentence/EndOfSentenceValidator.java
@@ -21,27 +21,26 @@ import cc.redpen.RedPenException;
 import cc.redpen.model.Sentence;
 import cc.redpen.validator.Validator;
 
-import java.util.Arrays;
 import java.util.List;
 import java.util.Locale;
 
 import static cc.redpen.config.SymbolType.*;
+import static java.util.Collections.singletonList;
 
 /**
  * This validator check if the style end of sentence is American style.
  * @see <a href="http://grammar.ccc.commnet.edu/grammar/marks/quotation.htm">Description of quotation marks</a>
  */
-final public class EndOfSentenceValidator extends Validator {
-
-    private char rightSingleQuotation = '\'';
-    private char rightDoubleQuotation = '"';
-    private char period = '.';
-    private char questionMark = '?';
-    private char exclamationMark = '!';
+public final class EndOfSentenceValidator extends Validator {
+    private char rightSingleQuotation;
+    private char rightDoubleQuotation;
+    private char period;
+    private char questionMark;
+    private char exclamationMark;
 
     @Override
     public List<String> getSupportedLanguages() {
-        return Arrays.asList(Locale.ENGLISH.getLanguage());
+        return singletonList(Locale.ENGLISH.getLanguage());
     }
 
     @Override
@@ -57,11 +56,9 @@ final public class EndOfSentenceValidator extends Validator {
                 || lastCharacter == exclamationMark) {
             if (secondCharacter == rightSingleQuotation
                     || secondCharacter == rightDoubleQuotation) {
-                StringBuilder builder = new StringBuilder();
-                builder.append(secondCharacter).append(lastCharacter);
                 addLocalizedErrorWithPosition(sentence,
                         content.length() - 2,
-                        content.length() - 1, builder.toString());
+                        content.length() - 1, String.valueOf(secondCharacter) + lastCharacter);
             }
         }
     }
@@ -74,41 +71,4 @@ final public class EndOfSentenceValidator extends Validator {
         questionMark = getSymbolTable().getSymbol(QUESTION_MARK).getValue();
         exclamationMark = getSymbolTable().getSymbol(EXCLAMATION_MARK).getValue();
     }
-
-    @Override
-    public String toString() {
-        return "EndOfSentenceValidator{" +
-                "rightSingleQuotation=" + rightSingleQuotation +
-                ", rightDoubleQuotation=" + rightDoubleQuotation +
-                ", period=" + period +
-                ", questionMark=" + questionMark +
-                ", exclamationMark=" + exclamationMark +
-                '}';
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-
-        EndOfSentenceValidator that = (EndOfSentenceValidator) o;
-
-        if (exclamationMark != that.exclamationMark) return false;
-        if (period != that.period) return false;
-        if (questionMark != that.questionMark) return false;
-        if (rightDoubleQuotation != that.rightDoubleQuotation) return false;
-        if (rightSingleQuotation != that.rightSingleQuotation) return false;
-
-        return true;
-    }
-
-    @Override
-    public int hashCode() {
-        int result = (int) rightSingleQuotation;
-        result = 31 * result + (int) rightDoubleQuotation;
-        result = 31 * result + (int) period;
-        result = 31 * result + (int) questionMark;
-        result = 31 * result + (int) exclamationMark;
-        return result;
-    }
 }
diff --git a/redpen-core/src/main/java/cc/redpen/validator/sentence/HankakuKanaValidator.java b/redpen-core/src/main/java/cc/redpen/validator/sentence/HankakuKanaValidator.java
index d1168585..a7bc44d5 100644
--- a/redpen-core/src/main/java/cc/redpen/validator/sentence/HankakuKanaValidator.java
+++ b/redpen-core/src/main/java/cc/redpen/validator/sentence/HankakuKanaValidator.java
@@ -3,12 +3,13 @@ package cc.redpen.validator.sentence;
 import cc.redpen.model.Sentence;
 import cc.redpen.validator.Validator;
 
-import java.util.Arrays;
 import java.util.List;
 import java.util.Locale;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import static java.util.Collections.singletonList;
+
 public class HankakuKanaValidator extends Validator {
     static Pattern pattern = Pattern.compile("[\\uFF65-\\uFF9F\\s-]");
 
@@ -16,7 +17,7 @@ public class HankakuKanaValidator extends Validator {
 
     @Override
     public List<String> getSupportedLanguages() {
-        return Arrays.asList(Locale.JAPANESE.getLanguage());
+        return singletonList(Locale.JAPANESE.getLanguage());
     }
 
     @Override
diff --git a/redpen-core/src/main/java/cc/redpen/validator/sentence/InvalidExpressionValidator.java b/redpen-core/src/main/java/cc/redpen/validator/sentence/InvalidExpressionValidator.java
index 2ace583f..088966cb 100644
--- a/redpen-core/src/main/java/cc/redpen/validator/sentence/InvalidExpressionValidator.java
+++ b/redpen-core/src/main/java/cc/redpen/validator/sentence/InvalidExpressionValidator.java
@@ -20,85 +20,43 @@ package cc.redpen.validator.sentence;
 import cc.redpen.RedPenException;
 import cc.redpen.model.Sentence;
 import cc.redpen.validator.Validator;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
-import java.util.Arrays;
 import java.util.HashSet;
 import java.util.Optional;
 import java.util.Set;
-import java.util.function.Consumer;
+
+import static java.util.stream.Stream.concat;
 
 /**
  * Validate input sentences contain invalid expression.
  */
-final public class InvalidExpressionValidator extends Validator {
+public final class InvalidExpressionValidator extends Validator {
     private static final String DEFAULT_RESOURCE_PATH = "default-resources/invalid-expression";
-    private static final Logger LOG =
-            LoggerFactory.getLogger(InvalidExpressionValidator.class);
     private Set<String> invalidExpressions;
-    private Set<String> customInvalidExpressions;
+
+    public InvalidExpressionValidator() {
+        super("list", new HashSet<>());
+    }
 
     @Override
     public void validate(Sentence sentence) {
-        Consumer<String> tConsumer = value -> {
+        concat(invalidExpressions.stream(), getSetAttribute("list").stream()).forEach(value -> {
             int startPosition = sentence.getContent().indexOf(value);
             if (startPosition != -1) {
                 addLocalizedErrorWithPosition(sentence, startPosition, startPosition + value.length(), value);
             }
-        };
-
-        invalidExpressions.stream().forEach(tConsumer);
-        customInvalidExpressions.stream().forEach(tConsumer);
+        });
     }
 
     @Override
     protected void init() throws RedPenException {
-
         String lang = getSymbolTable().getLang();
-        String defaultDictionaryFile = DEFAULT_RESOURCE_PATH
-                + "/invalid-expression-" + lang + ".dat";
+        String defaultDictionaryFile = DEFAULT_RESOURCE_PATH + "/invalid-expression-" + lang + ".dat";
         invalidExpressions = WORD_LIST.loadCachedFromResource(defaultDictionaryFile, "invalid expression");
 
-        customInvalidExpressions = new HashSet<>();
-        Optional<String> listStr = getConfigAttribute("list");
-        listStr.ifPresent(f -> {
-            LOG.info("User defined invalid expression list found.");
-            customInvalidExpressions.addAll(Arrays.asList(f.split(",")));
-            LOG.info("Succeeded to add elements of user defined list.");
-        });
-
         Optional<String> confFile = getConfigAttribute("dict");
         if (confFile.isPresent()) {
-            customInvalidExpressions.addAll(WORD_LIST.loadCachedFromFile(findFile(confFile.get()), "InvalidExpressionValidator user dictionary"));
+            getSetAttribute("list").addAll(WORD_LIST.loadCachedFromFile(findFile(confFile.get()), "InvalidExpressionValidator user dictionary"));
         }
     }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-
-        InvalidExpressionValidator that = (InvalidExpressionValidator) o;
-
-        if (invalidExpressions != null ? !invalidExpressions.equals(that.invalidExpressions) : that.invalidExpressions != null)
-            return false;
-        return !(customInvalidExpressions != null ? !customInvalidExpressions.equals(that.customInvalidExpressions) : that.customInvalidExpressions != null);
-
-    }
-
-    @Override
-    public int hashCode() {
-        int result = invalidExpressions != null ? invalidExpressions.hashCode() : 0;
-        result = 31 * result + (customInvalidExpressions != null ? customInvalidExpressions.hashCode() : 0);
-        return result;
-    }
-
-    @Override
-    public String toString() {
-        return "InvalidExpressionValidator{" +
-                "invalidExpressions=" + invalidExpressions +
-                ", customInvalidExpressions=" + customInvalidExpressions +
-                '}';
-    }
 }
diff --git a/redpen-core/src/main/java/cc/redpen/validator/sentence/InvalidSymbolValidator.java b/redpen-core/src/main/java/cc/redpen/validator/sentence/InvalidSymbolValidator.java
index f4613175..f35518e8 100644
--- a/redpen-core/src/main/java/cc/redpen/validator/sentence/InvalidSymbolValidator.java
+++ b/redpen-core/src/main/java/cc/redpen/validator/sentence/InvalidSymbolValidator.java
@@ -27,7 +27,7 @@ import java.util.Set;
 /**
  * Validate if there is invalid characters in sentences.
  */
-final public class InvalidSymbolValidator extends Validator {
+public final class InvalidSymbolValidator extends Validator {
 
     @Override
     public void validate(Sentence sentence) {
diff --git a/redpen-core/src/main/java/cc/redpen/validator/sentence/InvalidWordValidator.java b/redpen-core/src/main/java/cc/redpen/validator/sentence/InvalidWordValidator.java
index 4673d581..26e2e5c1 100644
--- a/redpen-core/src/main/java/cc/redpen/validator/sentence/InvalidWordValidator.java
+++ b/redpen-core/src/main/java/cc/redpen/validator/sentence/InvalidWordValidator.java
@@ -21,33 +21,32 @@ import cc.redpen.RedPenException;
 import cc.redpen.model.Sentence;
 import cc.redpen.tokenizer.TokenElement;
 import cc.redpen.validator.Validator;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 import java.util.*;
 
+import static java.util.Collections.singletonList;
+
 /**
  * Detect invalid word occurrences.
  */
 final public class InvalidWordValidator extends Validator {
-    private static final String DEFAULT_RESOURCE_PATH =
-            "default-resources/invalid-word";
-    private static final Logger LOG =
-            LoggerFactory.getLogger(InvalidWordValidator.class);
+    private static final String DEFAULT_RESOURCE_PATH = "default-resources/invalid-word";
     private Set<String> invalidWords;
-    private Set<String> customInvalidWords = new HashSet<>();
+
+    public InvalidWordValidator() {
+        super("list", new HashSet<>());
+    }
 
     @Override
     public List<String> getSupportedLanguages() {
-        return Arrays.asList(Locale.ENGLISH.getLanguage());
+        return singletonList(Locale.ENGLISH.getLanguage());
     }
 
     @Override
     public void validate(Sentence sentence) {
-        //NOTE: only Ascii white space since this validator works for european languages.
         for (TokenElement token : sentence.getTokens()) {
-            if (invalidWords.contains(token.getSurface().toLowerCase())
-                    || customInvalidWords.contains(token.getSurface().toLowerCase())) {
+            String surface = token.getSurface().toLowerCase();
+            if (invalidWords.contains(surface) || getSetAttribute("list").contains(surface)) {
                 addLocalizedErrorFromToken(sentence, token);
             }
         }
@@ -56,46 +55,12 @@ final public class InvalidWordValidator extends Validator {
     @Override
     protected void init() throws RedPenException {
         String lang = getSymbolTable().getLang();
-        String defaultDictionaryResource = DEFAULT_RESOURCE_PATH
-                + "/invalid-word-" + lang + ".dat";
+        String defaultDictionaryResource = DEFAULT_RESOURCE_PATH + "/invalid-word-" + lang + ".dat";
         invalidWords = WORD_LIST.loadCachedFromResource(defaultDictionaryResource, "invalid word");
 
-        getConfigAttribute("list").ifPresent((f -> {
-            LOG.info("User defined invalid expression list found.");
-            customInvalidWords.addAll(Arrays.asList(f.split(",")));
-            LOG.info("Succeeded to add elements of user defined list.");
-        }));
-
         Optional<String> confFile = getConfigAttribute("dict");
         if (confFile.isPresent()) {
-            customInvalidWords.addAll(WORD_LIST.loadCachedFromFile(findFile(confFile.get()), "InvalidWordValidator user dictionary"));
+            getSetAttribute("list").addAll(WORD_LIST.loadCachedFromFile(findFile(confFile.get()), "InvalidWordValidator user dictionary"));
         }
     }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-
-        InvalidWordValidator that = (InvalidWordValidator) o;
-
-        if (invalidWords != null ? !invalidWords.equals(that.invalidWords) : that.invalidWords != null) return false;
-        return !(customInvalidWords != null ? !customInvalidWords.equals(that.customInvalidWords) : that.customInvalidWords != null);
-
-    }
-
-    @Override
-    public int hashCode() {
-        int result = invalidWords != null ? invalidWords.hashCode() : 0;
-        result = 31 * result + (customInvalidWords != null ? customInvalidWords.hashCode() : 0);
-        return result;
-    }
-
-    @Override
-    public String toString() {
-        return "InvalidWordValidator{" +
-                "invalidWords=" + invalidWords +
-                ", customInvalidWords=" + customInvalidWords +
-                '}';
-    }
 }
diff --git a/redpen-core/src/main/java/cc/redpen/validator/sentence/JapaneseStyleValidator.java b/redpen-core/src/main/java/cc/redpen/validator/sentence/JapaneseStyleValidator.java
index 5b34a84c..51a41442 100644
--- a/redpen-core/src/main/java/cc/redpen/validator/sentence/JapaneseStyleValidator.java
+++ b/redpen-core/src/main/java/cc/redpen/validator/sentence/JapaneseStyleValidator.java
@@ -20,15 +20,15 @@ package cc.redpen.validator.sentence;
 import cc.redpen.model.Sentence;
 import cc.redpen.validator.Validator;
 
-import java.util.Arrays;
 import java.util.List;
 import java.util.Locale;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import static java.util.Collections.singletonList;
+
 /**
  * Validate Japanese document if it contains both Desumasu and Dearu styles.
- * 
  */
 public class JapaneseStyleValidator extends Validator {
     private static final Pattern DEARU_PATTERN = Pattern.compile("のだが|したが|したので|ないかと|してきた|であるから");
@@ -55,8 +55,8 @@ public class JapaneseStyleValidator extends Validator {
         String content = sentence.getContent();
         Matcher mat = pattern.matcher(content);
         int count = 0;
-        while(mat.find()){
-            count +=1;
+        while(mat.find()) {
+            count += 1;
         }
         return count;
     }
@@ -98,34 +98,6 @@ public class JapaneseStyleValidator extends Validator {
 
     @Override
     public List<String> getSupportedLanguages() {
-        return Arrays.asList(Locale.JAPANESE.getLanguage());
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-
-        JapaneseStyleValidator that = (JapaneseStyleValidator) o;
-
-        if (dearuCount != that.dearuCount) return false;
-        if (desumasuCount != that.desumasuCount) return false;
-
-        return true;
-    }
-
-    @Override
-    public int hashCode() {
-        int result = dearuCount;
-        result = 31 * result + desumasuCount;
-        return result;
-    }
-
-    @Override
-    public String toString() {
-        return "JapaneseStyleValidator{" +
-                "dearuCount=" + dearuCount +
-                ", desumasuCount=" + desumasuCount +
-                '}';
+        return singletonList(Locale.JAPANESE.getLanguage());
     }
 }
diff --git a/redpen-core/src/main/java/cc/redpen/validator/sentence/KatakanaEndHyphenValidator.java b/redpen-core/src/main/java/cc/redpen/validator/sentence/KatakanaEndHyphenValidator.java
index 448dd495..bb399666 100644
--- a/redpen-core/src/main/java/cc/redpen/validator/sentence/KatakanaEndHyphenValidator.java
+++ b/redpen-core/src/main/java/cc/redpen/validator/sentence/KatakanaEndHyphenValidator.java
@@ -21,11 +21,11 @@ import cc.redpen.RedPenException;
 import cc.redpen.model.Sentence;
 import cc.redpen.util.StringUtils;
 import cc.redpen.validator.Validator;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 import java.util.*;
 
+import static java.util.Collections.singletonList;
+
 /**
  * Validate the end hyphens of Katakana words in Japanese documents.
  * Japanese Katakana words have variations in end hyphen.
@@ -45,10 +45,7 @@ import java.util.*;
  * <p>
  * Note that KatakanaEndHyphenValidator only checks the rules a) and b).
  */
-final public class KatakanaEndHyphenValidator extends Validator {
-    private static final Logger LOG =
-            LoggerFactory.getLogger(KatakanaEndHyphenValidator.class);
-
+public final class KatakanaEndHyphenValidator extends Validator {
     /**
      * Default Katakana limit length without hypen.
      */
@@ -62,10 +59,8 @@ final public class KatakanaEndHyphenValidator extends Validator {
      */
     private static final char KATAKANA_MIDDLE_DOT = '・';
 
-    private Set<String> customSkipList;
-
     public KatakanaEndHyphenValidator() {
-        super();
+        super("list", new HashSet<>());
     }
 
     public static boolean isKatakanaEndHyphen(String katakana) {
@@ -75,12 +70,12 @@ final public class KatakanaEndHyphenValidator extends Validator {
 
     @Override
     public List<String> getSupportedLanguages() {
-        return Arrays.asList(Locale.JAPANESE.getLanguage());
+        return singletonList(Locale.JAPANESE.getLanguage());
     }
 
     @Override
     public void validate(Sentence sentence) {
-        StringBuilder katakana = new StringBuilder("");
+        StringBuilder katakana = new StringBuilder();
         for (int i = 0; i < sentence.getContent().length(); i++) {
             char c = sentence.getContent().charAt(i);
             if (StringUtils.isKatakana(c) && c != KATAKANA_MIDDLE_DOT) {
@@ -93,10 +88,9 @@ final public class KatakanaEndHyphenValidator extends Validator {
         this.checkKatakanaEndHyphen(sentence, katakana.toString(), sentence.getContent().length() - 1);
     }
 
-    private void checkKatakanaEndHyphen(Sentence sentence,
-                                                         String katakana,
-                                                         int position) {
-        if ( !(customSkipList != null && customSkipList.contains(katakana)) ) {
+    private void checkKatakanaEndHyphen(Sentence sentence, String katakana, int position) {
+        Set<String> customSkipList = getSetAttribute("list");
+        if (customSkipList.isEmpty() || !customSkipList.contains(katakana)) {
             if (isKatakanaEndHyphen(katakana)) {
                 addLocalizedErrorWithPosition(sentence, position, position + 1, katakana);
             }
@@ -105,39 +99,9 @@ final public class KatakanaEndHyphenValidator extends Validator {
 
     @Override
     protected void init() throws RedPenException {
-        customSkipList = new HashSet<>();
-        Optional<String> skipListStr = getConfigAttribute("list");
-        skipListStr.ifPresent(f -> {
-            LOG.info("Found user defined skip list.");
-            customSkipList.addAll(Arrays.asList(f.split(",")));
-            LOG.info("Succeeded to add elements of user defined skip list.");
-        });
-
         Optional<String> confFile = getConfigAttribute("dict");
         if (confFile.isPresent()) {
-            customSkipList.addAll(WORD_LIST.loadCachedFromFile(findFile(confFile.get()), "KatakanaEndHyphenValidator user dictionary"));
+            getSetAttribute("list").addAll(WORD_LIST.loadCachedFromFile(findFile(confFile.get()), "KatakanaEndHyphenValidator user dictionary"));
         }
     }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-
-        KatakanaEndHyphenValidator that = (KatakanaEndHyphenValidator) o;
-
-        return !(customSkipList != null ? !customSkipList.equals(that.customSkipList) : that.customSkipList != null);
-    }
-
-    @Override
-    public int hashCode() {
-        return customSkipList != null ? customSkipList.hashCode() : 0;
-    }
-
-    @Override
-    public String toString() {
-        return "KatakanaEndHyphenValidator{" +
-                "customSkipList=" + customSkipList +
-                '}';
-    }
 }
diff --git a/redpen-core/src/main/java/cc/redpen/validator/sentence/KatakanaSpellCheckValidator.java b/redpen-core/src/main/java/cc/redpen/validator/sentence/KatakanaSpellCheckValidator.java
index e2368ea9..b6efe41b 100644
--- a/redpen-core/src/main/java/cc/redpen/validator/sentence/KatakanaSpellCheckValidator.java
+++ b/redpen-core/src/main/java/cc/redpen/validator/sentence/KatakanaSpellCheckValidator.java
@@ -27,6 +27,8 @@ import org.slf4j.LoggerFactory;
 
 import java.util.*;
 
+import static java.util.Collections.singletonList;
+
 /**
  * Validate the correctness of Katakana word spelling.
  * Japanese Katakana words have orthographical variations.
@@ -47,22 +49,7 @@ import java.util.*;
  * word is smaller than the threshold, we do not detect
  * the similarity.
  */
-final public class KatakanaSpellCheckValidator extends Validator {
-    /**
-     * The default threshold of similarity ratio between the length and the distance. <br>
-     * <p>
-     * The similarities are computed by edit distance.
-     */
-    private static final float DEFAULT_SIMILARITY_RATIO = 0.3f;
-    /**
-     * The default threshold of word frequencies of Katakana Words.
-     */
-    private static final int DEFAULT_MINIMUM_FREQUENCIES = 5;
-    /**
-     * The default threshold value for the length of Katakana word
-     * to ignore.
-     */
-    private static final int MAX_IGNORE_KATAKANA_LENGTH = 3;
+ public final class KatakanaSpellCheckValidator extends Validator {
     /**
      * Default dictionary for Katakana spell checking.
      */
@@ -70,8 +57,7 @@ final public class KatakanaSpellCheckValidator extends Validator {
     /**
      * Logger
      */
-    private static final Logger LOG =
-            LoggerFactory.getLogger(KatakanaSpellCheckValidator.class);
+    private static final Logger LOG = LoggerFactory.getLogger(KatakanaSpellCheckValidator.class);
 
     /**
      * Katakana word dic with line number.
@@ -82,17 +68,19 @@ final public class KatakanaSpellCheckValidator extends Validator {
      */
     private Set<String> exceptions = new HashSet<>();
 
-    private Set<String> customExceptions = new HashSet<>();
-
     private Map<String, Integer> katakanaWordFrequencies = new HashMap<>();
 
-    private float minimumRatio = DEFAULT_SIMILARITY_RATIO;
-
-    private int minimumFrequencies = DEFAULT_MINIMUM_FREQUENCIES;
+    public KatakanaSpellCheckValidator() {
+        super("list", new HashSet<>(),
+              "min_ratio", 0.3f, // The default threshold of similarity ratio between the length and the distance. The similarities are computed by edit distance.
+              "min_freq", 5, // The default threshold of word frequencies of Katakana Words.
+              "max_ignore_len", 3, // The default threshold value for the length of Katakana word to ignore
+              "disable-default", false);
+    }
 
     @Override
     public List<String> getSupportedLanguages() {
-        return Arrays.asList(Locale.JAPANESE.getLanguage());
+        return singletonList(Locale.JAPANESE.getLanguage());
     }
 
     @Override
@@ -138,16 +126,16 @@ final public class KatakanaSpellCheckValidator extends Validator {
     }
 
     private void checkKatakanaSpell(Sentence sentence, String katakana) {
-        if (katakana.length() <= MAX_IGNORE_KATAKANA_LENGTH) {
+        if (katakana.length() <= getIntAttribute("max_ignore_len")) {
             return;
         }
         if (dic.containsKey(katakana) || exceptions.contains(katakana)
-                || customExceptions.contains(katakana) ||
+                || getSetAttribute("list").contains(katakana) ||
                 (katakanaWordFrequencies.get(katakana) != null
-                        && katakanaWordFrequencies.get(katakana) > minimumFrequencies)) {
+                        && katakanaWordFrequencies.get(katakana) > getIntAttribute("min_freq"))) {
             return;
         }
-        final int minLsDistance = Math.round(katakana.length() * minimumRatio);
+        int minLsDistance = Math.round(katakana.length() * getFloatAttribute("min_ratio"));
         boolean found = false;
         for (String key : dic.keySet()) {
             if (LevenshteinDistance.getDistance(key, katakana) <= minLsDistance) {
@@ -162,8 +150,7 @@ final public class KatakanaSpellCheckValidator extends Validator {
 
     @Override
     protected void init() throws RedPenException {
-        boolean disableDefault = getConfigAttributeAsBoolean("disable-default", false);
-        if (!disableDefault) {
+        if (!getBooleanAttribute("disable-default")) {
             String defaultDictionaryFile = DEFAULT_RESOURCE_PATH + "/katakana-spellcheck.dat";
             exceptions = WORD_LIST.loadCachedFromResource(defaultDictionaryFile, "katakana word dictionary");
         }
@@ -171,50 +158,8 @@ final public class KatakanaSpellCheckValidator extends Validator {
         Optional<String> confFile = getConfigAttribute("dict");
         if (confFile.isPresent()) {
             LOG.info("User defined Katakana word dictionary found.");
-            customExceptions.addAll(WORD_LIST.loadCachedFromFile(findFile(confFile.get()),
-                    "KatakanaSpellCheckValidator user dictionary"));
+            getSetAttribute("list").addAll(WORD_LIST.loadCachedFromFile(findFile(confFile.get()), "KatakanaSpellCheckValidator user dictionary"));
             LOG.info("Succeeded to add elements of user defined dictionary.");
         }
-
-        getConfigAttribute("list").ifPresent((f -> {
-            LOG.info("User defined Katakana words list found.");
-            customExceptions.addAll(Arrays.asList(f.split(",")));
-            LOG.info("Succeeded to add elements of user defined list.");
-        }));
-
-        minimumRatio = (float) getConfigAttributeAsDouble("min_ratio", DEFAULT_SIMILARITY_RATIO);
-        minimumFrequencies = getConfigAttributeAsInt("min_freq", DEFAULT_MINIMUM_FREQUENCIES);
-
-        //TODO : configurable MAX_IGNORE_KATAKANA_LENGTH.
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-
-        KatakanaSpellCheckValidator that = (KatakanaSpellCheckValidator) o;
-
-        if (dic != null ? !dic.equals(that.dic) : that.dic != null) return false;
-        if (exceptions != null ? !exceptions.equals(that.exceptions) : that.exceptions != null) return false;
-        return !(customExceptions != null ? !customExceptions.equals(that.customExceptions) : that.customExceptions != null);
-
-    }
-
-    @Override
-    public int hashCode() {
-        int result = dic != null ? dic.hashCode() : 0;
-        result = 31 * result + (exceptions != null ? exceptions.hashCode() : 0);
-        result = 31 * result + (customExceptions != null ? customExceptions.hashCode() : 0);
-        return result;
-    }
-
-    @Override
-    public String toString() {
-        return "KatakanaSpellCheckValidator{" +
-                "dic=" + dic +
-                ", exceptions=" + exceptions +
-                ", customExceptions=" + customExceptions +
-                '}';
     }
 }
diff --git a/redpen-core/src/main/java/cc/redpen/validator/sentence/NumberFormatValidator.java b/redpen-core/src/main/java/cc/redpen/validator/sentence/NumberFormatValidator.java
index 168890d8..f400e155 100644
--- a/redpen-core/src/main/java/cc/redpen/validator/sentence/NumberFormatValidator.java
+++ b/redpen-core/src/main/java/cc/redpen/validator/sentence/NumberFormatValidator.java
@@ -31,17 +31,16 @@ public class NumberFormatValidator extends Validator {
     // specifies which characters delimite the decimal part of a number
     private String decimalDelimiters = DOT_DELIMITERS;
 
-    // should we ignore years (basically four digit integers)
-    boolean ignoreYears = false;
+    public NumberFormatValidator() {
+        super("decimal_delimiter_is_comma", false, "ignore_years", false);
+    }
 
     @Override
     protected void init() throws RedPenException {
         super.init();
 
         // set the following to true to support EU formats such as 1.000,00
-        boolean decimalDelimiterComma = getConfigAttributeAsBoolean("decimal_delimiter_is_comma", false);
-        ignoreYears = getConfigAttributeAsBoolean("ignore_years", false);
-        if (decimalDelimiterComma) {
+        if (getBooleanAttribute("decimal_delimiter_is_comma")) {
             decimalDelimiters = COMMA_DELIMITERS;
         } else {
             decimalDelimiters = DOT_DELIMITERS;
@@ -86,10 +85,6 @@ public class NumberFormatValidator extends Validator {
 
     /**
      * Inspect how a number is formatted and generate an error where appropriate
-     *
-     * @param sentence
-     * @param position
-     * @param number
      */
     private void validateNumber(Sentence sentence, int position, String number) {
         if (!number.isEmpty()) {
@@ -115,7 +110,7 @@ public class NumberFormatValidator extends Validator {
             }
 
             // if it's a four digit integer and we are ignoring years, ignore this
-            if (ignoreYears && isInteger && (integerPortion.length() == 4)) {
+            if (getBooleanAttribute("ignore_years") && isInteger && (integerPortion.length() == 4)) {
                 return;
             }
 
diff --git a/redpen-core/src/main/java/cc/redpen/validator/sentence/OkuriganaValidator.java b/redpen-core/src/main/java/cc/redpen/validator/sentence/OkuriganaValidator.java
index b71adbc0..38bf8acf 100644
--- a/redpen-core/src/main/java/cc/redpen/validator/sentence/OkuriganaValidator.java
+++ b/redpen-core/src/main/java/cc/redpen/validator/sentence/OkuriganaValidator.java
@@ -23,7 +23,13 @@ import cc.redpen.tokenizer.TokenElement;
 import cc.redpen.validator.ExpressionRule;
 import cc.redpen.validator.Validator;
 
-import java.util.*;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+
+import static java.util.Arrays.asList;
+import static java.util.Collections.singletonList;
 
 // Checks if the Japanese input sentences contain the invalid Okurigana style.
 public class OkuriganaValidator extends Validator {
@@ -32,10 +38,10 @@ public class OkuriganaValidator extends Validator {
 
     static {
         invalidOkuriganaTokens = new HashSet<>();
-        invalidOkuriganaTokens.add(new ExpressionRule().addElement(new TokenElement("合さ", Arrays.asList("動詞", "自立"), 0)));
-        invalidOkuriganaTokens.add(new ExpressionRule().addElement(new TokenElement("合し", Arrays.asList("動詞", "自立"), 0)));
-        invalidOkuriganaTokens.add(new ExpressionRule().addElement(new TokenElement("合す", Arrays.asList("動詞", "自立"), 0)));
-        invalidOkuriganaTokens.add(new ExpressionRule().addElement(new TokenElement("合せ", Arrays.asList("動詞", "自立"), 0)));
+        invalidOkuriganaTokens.add(new ExpressionRule().addElement(new TokenElement("合さ", asList("動詞", "自立"), 0)));
+        invalidOkuriganaTokens.add(new ExpressionRule().addElement(new TokenElement("合し", asList("動詞", "自立"), 0)));
+        invalidOkuriganaTokens.add(new ExpressionRule().addElement(new TokenElement("合す", asList("動詞", "自立"), 0)));
+        invalidOkuriganaTokens.add(new ExpressionRule().addElement(new TokenElement("合せ", asList("動詞", "自立"), 0)));
     }
 
     static {
@@ -149,16 +155,11 @@ public class OkuriganaValidator extends Validator {
 
     @Override
     public List<String> getSupportedLanguages() {
-        return Arrays.asList(Locale.JAPANESE.getLanguage());
+        return singletonList(Locale.JAPANESE.getLanguage());
     }
 
     @Override
     protected void init() throws RedPenException {
         // TODO: user dictionary
     }
-
-    @Override
-    public String toString() {
-        return "OkuriganaValidator{}";
-    }
 }
diff --git a/redpen-core/src/main/java/cc/redpen/validator/sentence/ParenthesizedSentenceValidator.java b/redpen-core/src/main/java/cc/redpen/validator/sentence/ParenthesizedSentenceValidator.java
index fef5e5ce..70c642f7 100644
--- a/redpen-core/src/main/java/cc/redpen/validator/sentence/ParenthesizedSentenceValidator.java
+++ b/redpen-core/src/main/java/cc/redpen/validator/sentence/ParenthesizedSentenceValidator.java
@@ -18,7 +18,6 @@
 package cc.redpen.validator.sentence;
 
 
-import cc.redpen.RedPenException;
 import cc.redpen.model.Sentence;
 import cc.redpen.tokenizer.TokenElement;
 import cc.redpen.validator.Validator;
@@ -27,20 +26,13 @@ import cc.redpen.validator.Validator;
  * Warn if too many (or overly long (or nested parenthesized sentences (where you do this))) are used in a sentence
  */
 public class ParenthesizedSentenceValidator extends Validator {
-
     private static final String OPEN_PARENS = "(（";
     private static final String CLOSE_PARENS = ")）";
 
-    private int nestingLevelMax = 2; // the limit on how many parenthesized expressions are permitted
-    private int subsentenceCountMax = 1; // the number of parenthesized expressions allowed
-    private int subsentenceLengthMax = 4; // the maximum number of words in a parenthesized expression
-
-    @Override
-    protected void init() throws RedPenException {
-        super.init();
-        nestingLevelMax = getConfigAttributeAsInt("max_nesting_level", 1);
-        subsentenceCountMax = getConfigAttributeAsInt("max_count", 1);
-        subsentenceLengthMax = getConfigAttributeAsInt("max_length", 3);
+    public ParenthesizedSentenceValidator() {
+        super("max_nesting_level", 1, // the limit on how many parenthesized expressions are permitted
+              "max_count", 1,  // the number of parenthesized expressions allowed
+              "max_length", 3); // the maximum number of words in a parenthesized expression
     }
 
     /**
@@ -57,7 +49,7 @@ public class ParenthesizedSentenceValidator extends Validator {
             if (token.getSurface().length() == 1) {
                 if (OPEN_PARENS.indexOf(token.getSurface().charAt(0)) != -1) {
                     nestingLevel++;
-                    if (nestingLevel > nestingLevelMax) {
+                    if (nestingLevel > getIntAttribute("max_nesting_level")) {
                         addLocalizedErrorWithPosition(
                                 "NestingLevelTooDeep",
                                 sentence,
@@ -68,7 +60,7 @@ public class ParenthesizedSentenceValidator extends Validator {
                     nestingLevel = Math.max(0, nestingLevel - 1);
                     if (nestingLevel == 0) {
                         subsentenceCount++;
-                        if (subsentenceLength > subsentenceLengthMax) {
+                        if (subsentenceLength > getIntAttribute("max_length")) {
                             addLocalizedErrorWithPosition(
                                     "SubsentenceTooLong",
                                     sentence,
@@ -85,9 +77,8 @@ public class ParenthesizedSentenceValidator extends Validator {
             }
         }
 
-        if (subsentenceCount > subsentenceCountMax) {
+        if (subsentenceCount > getIntAttribute("max_count")) {
             addLocalizedError("SubsentenceTooFrequent", sentence);
         }
-
     }
 }
diff --git a/redpen-core/src/main/java/cc/redpen/validator/sentence/QuotationValidator.java b/redpen-core/src/main/java/cc/redpen/validator/sentence/QuotationValidator.java
index df6939b0..29664a5f 100644
--- a/redpen-core/src/main/java/cc/redpen/validator/sentence/QuotationValidator.java
+++ b/redpen-core/src/main/java/cc/redpen/validator/sentence/QuotationValidator.java
@@ -23,11 +23,11 @@ import cc.redpen.model.Sentence;
 import cc.redpen.validator.Validator;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 import java.util.Locale;
 
 import static cc.redpen.config.SymbolType.*;
+import static java.util.Collections.singletonList;
 
 /**
  * Validator to validate quotation characters.
@@ -49,25 +49,28 @@ public class QuotationValidator extends Validator {
     private Symbol rightDoubleQuotationMark;
     private char period;
 
+    public QuotationValidator() {
+        super("use_ascii", false);
+    }
+
     @Override
     public List<String> getSupportedLanguages() {
-        return Arrays.asList(Locale.ENGLISH.getLanguage());
+        return singletonList(Locale.ENGLISH.getLanguage());
     }
 
     @Override
     public void validate(Sentence sentence) {
         // validate single quotation
-        this.checkQuotation(sentence, leftSingleQuotationMark, rightSingleQuotationMark);
+        checkQuotation(sentence, leftSingleQuotationMark, rightSingleQuotationMark);
 
         // validate double quotation
-        this.checkQuotation(sentence, leftDoubleQuotationMark, rightDoubleQuotationMark);
+        checkQuotation(sentence, leftDoubleQuotationMark, rightDoubleQuotationMark);
     }
 
     @Override
     protected void init() throws RedPenException {
         this.period = getSymbolTable().getValueOrFallbackToDefault(FULL_STOP);
-
-        setUseAscii(getConfigAttributeAsBoolean("use_ascii", false));
+        setUseAscii(getBooleanAttribute("use_ascii"));
     }
 
     private void setUseAscii(boolean useAscii) {
diff --git a/redpen-core/src/main/java/cc/redpen/validator/sentence/SentenceLengthValidator.java b/redpen-core/src/main/java/cc/redpen/validator/sentence/SentenceLengthValidator.java
index 0063554e..a271f40b 100644
--- a/redpen-core/src/main/java/cc/redpen/validator/sentence/SentenceLengthValidator.java
+++ b/redpen-core/src/main/java/cc/redpen/validator/sentence/SentenceLengthValidator.java
@@ -17,70 +17,22 @@
  */
 package cc.redpen.validator.sentence;
 
-import cc.redpen.RedPenException;
 import cc.redpen.model.Sentence;
 import cc.redpen.validator.Validator;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 /**
  * Validate input sentences contain more characters more than specified.
  */
-final public class SentenceLengthValidator extends Validator {
-    /**
-     * Default maximum length of sentences.
-     */
-    @SuppressWarnings("WeakerAccess")
-    public static final int DEFAULT_MAX_LENGTH = 120;
-    private static final Logger LOG =
-            LoggerFactory.getLogger(SentenceLengthValidator.class);
-    private int maxLength = DEFAULT_MAX_LENGTH;
+public final class SentenceLengthValidator extends Validator {
+    public SentenceLengthValidator() {
+        super("max_len", 120);
+    }
 
     @Override
     public void validate(Sentence sentence) {
+        int maxLength = getIntAttribute("max_len");
         if (sentence.getContent().length() > maxLength) {
             addLocalizedError(sentence, sentence.getContent().length(), maxLength);
         }
     }
-
-    @Override
-    protected void init() throws RedPenException {
-        this.maxLength = getConfigAttributeAsInt("max_len", DEFAULT_MAX_LENGTH);
-    }
-
-    /**
-     * Set maximum length of sentence.
-     *
-     * @param maxLength max limit of sentence length
-     */
-    protected void setLengthLimit(int maxLength) {
-        this.setMaxLength(maxLength);
-    }
-
-    protected void setMaxLength(int max) {
-        this.maxLength = max;
-    }
-
-    @Override
-    public String toString() {
-        return "SentenceLengthValidator{" +
-                "maxLength=" + maxLength +
-                '}';
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-
-        SentenceLengthValidator that = (SentenceLengthValidator) o;
-
-        return maxLength == that.maxLength;
-
-    }
-
-    @Override
-    public int hashCode() {
-        return maxLength;
-    }
 }
diff --git a/redpen-core/src/main/java/cc/redpen/validator/sentence/SpaceBeginningOfSentenceValidator.java b/redpen-core/src/main/java/cc/redpen/validator/sentence/SpaceBeginningOfSentenceValidator.java
index d5af4d2e..6392894e 100644
--- a/redpen-core/src/main/java/cc/redpen/validator/sentence/SpaceBeginningOfSentenceValidator.java
+++ b/redpen-core/src/main/java/cc/redpen/validator/sentence/SpaceBeginningOfSentenceValidator.java
@@ -29,7 +29,7 @@ import java.util.Map;
  * Validate input sentences except for first sentence of a paragraph start with
  * a space.
  */
-final public class SpaceBeginningOfSentenceValidator extends Validator {
+public final class SpaceBeginningOfSentenceValidator extends Validator {
     private Map<Integer, List<Sentence>> sentencePositions = new HashMap<>();
 
     private boolean isFirstInLine(Sentence sentence) {
@@ -52,27 +52,4 @@ final public class SpaceBeginningOfSentenceValidator extends Validator {
         List<Sentence> list = sentencePositions.get(sentence.getLineNumber());
         list.add(sentence);
     }
-
-    @Override
-    public String toString() {
-        return "SpaceBeginningOfSentenceValidator{" +
-                "sentencePositions=" + sentencePositions +
-                '}';
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-
-        SpaceBeginningOfSentenceValidator that = (SpaceBeginningOfSentenceValidator) o;
-
-        return !(sentencePositions != null ? !sentencePositions.equals(that.sentencePositions) : that.sentencePositions != null);
-
-    }
-
-    @Override
-    public int hashCode() {
-        return sentencePositions != null ? sentencePositions.hashCode() : 0;
-    }
 }
diff --git a/redpen-core/src/main/java/cc/redpen/validator/sentence/SpellingValidator.java b/redpen-core/src/main/java/cc/redpen/validator/sentence/SpellingValidator.java
index 4f893b44..89e26dd7 100644
--- a/redpen-core/src/main/java/cc/redpen/validator/sentence/SpellingValidator.java
+++ b/redpen-core/src/main/java/cc/redpen/validator/sentence/SpellingValidator.java
@@ -22,41 +22,28 @@ import cc.redpen.model.Sentence;
 import cc.redpen.tokenizer.TokenElement;
 import cc.redpen.util.SpellingUtils;
 import cc.redpen.validator.Validator;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
-import java.util.Arrays;
 import java.util.HashSet;
 import java.util.Optional;
 import java.util.Set;
 
 public class SpellingValidator extends Validator {
-
-    private static final Logger LOG = LoggerFactory.getLogger(SpellingValidator.class);
-
     private static String skipCharacters = "[\\!-/:-@\\[-`{-~]";
     // TODO: replace more memory efficient data structure
     private Set<String> defaultDictionary;
-    private Set<String> customDictionary;
 
+    public SpellingValidator() {
+        super("list", new HashSet<>());
+    }
 
     @Override
     protected void init() throws RedPenException {
         defaultDictionary = SpellingUtils.getDictionary(getSymbolTable().getLang());
 
-        customDictionary = new HashSet<>();
-
-        Optional<String> listStr = getConfigAttribute("list");
-        listStr.ifPresent(f -> {
-            LOG.info("User defined valid word list found.");
-            customDictionary.addAll(Arrays.asList(f.split(",")));
-            LOG.info("Succeeded to add elements of user defined list.");
-        });
-
         Optional<String> userDictionaryFile = getConfigAttribute("dict");
         if (userDictionaryFile.isPresent()) {
             String f = userDictionaryFile.get();
-            customDictionary.addAll(WORD_LIST_LOWERCASED.loadCachedFromFile(findFile(f), "SpellingValidator user dictionary"));
+            getSetAttribute("list").addAll(WORD_LIST_LOWERCASED.loadCachedFromFile(findFile(f), "SpellingValidator user dictionary"));
         }
     }
 
@@ -68,7 +55,7 @@ public class SpellingValidator extends Validator {
                 continue;
             }
 
-            if (!this.defaultDictionary.contains(surface) && !this.customDictionary.contains(surface)) {
+            if (!defaultDictionary.contains(surface) && !getSetAttribute("list").contains(surface)) {
                 addLocalizedErrorFromToken(sentence, token);
             }
         }
@@ -77,32 +64,4 @@ public class SpellingValidator extends Validator {
     private String normalize(String token) {
         return token.toLowerCase().replaceAll(skipCharacters, "");
     }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-
-        SpellingValidator that = (SpellingValidator) o;
-
-        if (defaultDictionary != null ? !defaultDictionary.equals(that.defaultDictionary) : that.defaultDictionary != null)
-            return false;
-        return !(customDictionary != null ? !customDictionary.equals(that.customDictionary) : that.customDictionary != null);
-
-    }
-
-    @Override
-    public int hashCode() {
-        int result = defaultDictionary != null ? defaultDictionary.hashCode() : 0;
-        result = 31 * result + (customDictionary != null ? customDictionary.hashCode() : 0);
-        return result;
-    }
-
-    @Override
-    public String toString() {
-        return "SpellingValidator{" +
-                "defaultDictionary=" + defaultDictionary +
-                ", customDictionary=" + customDictionary +
-                '}';
-    }
 }
diff --git a/redpen-core/src/main/java/cc/redpen/validator/sentence/StartWithCapitalLetterValidator.java b/redpen-core/src/main/java/cc/redpen/validator/sentence/StartWithCapitalLetterValidator.java
index 2f9fca04..d7348508 100644
--- a/redpen-core/src/main/java/cc/redpen/validator/sentence/StartWithCapitalLetterValidator.java
+++ b/redpen-core/src/main/java/cc/redpen/validator/sentence/StartWithCapitalLetterValidator.java
@@ -26,6 +26,8 @@ import org.slf4j.LoggerFactory;
 
 import java.util.*;
 
+import static java.util.Collections.singletonList;
+
 /**
  * Check if the input sentence start with a capital letter.
  */
@@ -38,7 +40,7 @@ final public class StartWithCapitalLetterValidator extends Validator {
 
     @Override
     public List<String> getSupportedLanguages() {
-        return Arrays.asList(Locale.ENGLISH.getLanguage());
+        return singletonList(Locale.ENGLISH.getLanguage());
     }
 
     @Override
@@ -84,31 +86,4 @@ final public class StartWithCapitalLetterValidator extends Validator {
             customWhiteList = WORD_LIST.loadCachedFromFile(findFile(confFile.get()), "StartWithCapitalLetterValidator user dictionary");
         }
     }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-
-        StartWithCapitalLetterValidator that = (StartWithCapitalLetterValidator) o;
-
-        if (whiteList != null ? !whiteList.equals(that.whiteList) : that.whiteList != null) return false;
-        return !(customWhiteList != null ? !customWhiteList.equals(that.customWhiteList) : that.customWhiteList != null);
-
-    }
-
-    @Override
-    public int hashCode() {
-        int result = whiteList != null ? whiteList.hashCode() : 0;
-        result = 31 * result + (customWhiteList != null ? customWhiteList.hashCode() : 0);
-        return result;
-    }
-
-    @Override
-    public String toString() {
-        return "StartWithCapitalLetterValidator{" +
-                "whiteList=" + whiteList +
-                ", customWhiteList=" + customWhiteList +
-                '}';
-    }
 }
diff --git a/redpen-core/src/main/java/cc/redpen/validator/sentence/SuggestExpressionValidator.java b/redpen-core/src/main/java/cc/redpen/validator/sentence/SuggestExpressionValidator.java
index 874fd534..82ddd925 100644
--- a/redpen-core/src/main/java/cc/redpen/validator/sentence/SuggestExpressionValidator.java
+++ b/redpen-core/src/main/java/cc/redpen/validator/sentence/SuggestExpressionValidator.java
@@ -62,27 +62,4 @@ final public class SuggestExpressionValidator extends Validator {
     protected void setSynonyms(Map<String, String> synonymMap) {
         this.synonyms = synonymMap;
     }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-
-        SuggestExpressionValidator that = (SuggestExpressionValidator) o;
-
-        return !(synonyms != null ? !synonyms.equals(that.synonyms) : that.synonyms != null);
-
-    }
-
-    @Override
-    public int hashCode() {
-        return synonyms != null ? synonyms.hashCode() : 0;
-    }
-
-    @Override
-    public String toString() {
-        return "SuggestExpressionValidator{" +
-                "synonyms=" + synonyms +
-                '}';
-    }
 }
diff --git a/redpen-core/src/main/java/cc/redpen/validator/sentence/WordNumberValidator.java b/redpen-core/src/main/java/cc/redpen/validator/sentence/WordNumberValidator.java
index f1d02894..5ffceed2 100644
--- a/redpen-core/src/main/java/cc/redpen/validator/sentence/WordNumberValidator.java
+++ b/redpen-core/src/main/java/cc/redpen/validator/sentence/WordNumberValidator.java
@@ -17,57 +17,23 @@
  */
 package cc.redpen.validator.sentence;
 
-import cc.redpen.RedPenException;
 import cc.redpen.model.Sentence;
 import cc.redpen.validator.Validator;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 /**
  * Validate input sentences have more words than specified.
  */
 final public class WordNumberValidator extends Validator {
-    /**
-     * Default maximum number of words in one sentence.
-     */
-    @SuppressWarnings("WeakerAccess")
-    public static final int DEFAULT_MAXIMUM_WORDS_IN_A_SENTENCE = 30;
-    private static final Logger LOG =
-            LoggerFactory.getLogger(WordNumberValidator.class);
-    private int maxWordNumber = DEFAULT_MAXIMUM_WORDS_IN_A_SENTENCE;
+    public WordNumberValidator() {
+        super("max_num", 30); // Default maximum number of words in one sentence.
+    }
 
     @Override
     public void validate(Sentence sentence) {
         int wordNum = sentence.getTokens().size();
-        if (wordNum > maxWordNumber) {
-            addLocalizedError(sentence, wordNum, maxWordNumber);
+        int maxNum = getIntAttribute("max_num");
+        if (wordNum > maxNum) {
+            addLocalizedError(sentence, wordNum, maxNum);
         }
     }
-
-    @Override
-    protected void init() throws RedPenException {
-        this.maxWordNumber = getConfigAttributeAsInt("max_num", DEFAULT_MAXIMUM_WORDS_IN_A_SENTENCE);
-    }
-
-    @Override
-    public String toString() {
-        return "WordNumberValidator{" +
-                "maxWordNumber=" + maxWordNumber +
-                '}';
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-
-        WordNumberValidator that = (WordNumberValidator) o;
-
-        return maxWordNumber == that.maxWordNumber;
-    }
-
-    @Override
-    public int hashCode() {
-        return maxWordNumber;
-    }
 }


Test patch:
diff --git a/redpen-core/src/test/java/cc/redpen/validator/ValidationErrorMessageTest.java b/redpen-core/src/test/java/cc/redpen/validator/ValidationErrorMessageTest.java
index ebaac5bc..5ae6b361 100644
--- a/redpen-core/src/test/java/cc/redpen/validator/ValidationErrorMessageTest.java
+++ b/redpen-core/src/test/java/cc/redpen/validator/ValidationErrorMessageTest.java
@@ -21,6 +21,13 @@ import cc.redpen.model.Sentence;
 
 class ValidationErrorMessageTest extends Validator {
 
+    public ValidationErrorMessageTest() {
+    }
+
+    public ValidationErrorMessageTest(Object...defaultAttributes) {
+        super(defaultAttributes);
+    }
+
     @Override
     public void validate(Sentence sentence) {
         addLocalizedError(sentence, 1, 2, 3, "sentence");
diff --git a/redpen-core/src/test/java/cc/redpen/validator/ValidatorTest.java b/redpen-core/src/test/java/cc/redpen/validator/ValidatorTest.java
index bf2eb7be..e0fb27f3 100644
--- a/redpen-core/src/test/java/cc/redpen/validator/ValidatorTest.java
+++ b/redpen-core/src/test/java/cc/redpen/validator/ValidatorTest.java
@@ -18,27 +18,32 @@
 package cc.redpen.validator;
 
 import cc.redpen.RedPenException;
+import cc.redpen.config.Configuration;
+import cc.redpen.config.ValidatorConfiguration;
 import cc.redpen.model.Sentence;
 import cc.redpen.tokenizer.TokenElement;
 import org.junit.Test;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 import java.util.Locale;
 
+import static java.util.Collections.singletonList;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 
 public class ValidatorTest {
+    private final Configuration globalConfig = Configuration.builder().build();
+
     @Test
-    public void testValidationErrorCreation() throws RedPenException {
+    public void validationErrorCreation() throws RedPenException {
         ValidationErrorMessageTest validationErrorMessageTest = new ValidationErrorMessageTest();
-        validationErrorMessageTest.preInit(null, null);
+        validationErrorMessageTest.preInit(new ValidatorConfiguration("blah"), Configuration.builder().build());
         validationErrorMessageTest.setLocale(Locale.ENGLISH);
         List<ValidationError> validationErrors = new ArrayList<>();
         validationErrorMessageTest.setErrorList(validationErrors);
         Sentence sentence = new Sentence("sentence", 1);
-        sentence.setTokens(Arrays.asList(new TokenElement("word", Arrays.asList(""), 0)));
+        sentence.setTokens(singletonList(new TokenElement("word", singletonList(""), 0)));
         validationErrorMessageTest.validate(sentence);
         assertEquals("error str:sentence 1:1 2:2 3:3", validationErrors.get(0).getMessage());
         assertEquals("with Key :sentence", validationErrors.get(1).getMessage());
@@ -49,7 +54,37 @@ public class ValidatorTest {
         validationErrorMessageTest.validate(sentence);
         assertEquals("エラー ストリング:sentence 1:1 2:2 3:3", validationErrors.get(0).getMessage());
         assertEquals("キー指定 :sentence", validationErrors.get(1).getMessage());
+    }
+
+    @Test
+    public void configOverridesDefaultAttributes() throws Exception {
+        Validator validator = new Validator("hello", 123) {};
+        assertEquals(123, validator.getIntAttribute("hello"));
+
+        validator.preInit(new ValidatorConfiguration("blah").addAttribute("hello", "234"), globalConfig);
+        assertEquals(234, validator.getIntAttribute("hello"));
+    }
 
+    @Test
+    public void equalsAndHashCode() throws Exception {
+        Validator validator = new ValidationErrorMessageTest();
+        Validator validator2 = new ValidationErrorMessageTest();
+        assertEquals(validator, validator2);
+        assertEquals(validator.hashCode(), validator2.hashCode());
+
+        validator.preInit(new ValidatorConfiguration("blah"), globalConfig);
+        assertFalse(validator.equals(validator2));
+        assertFalse(validator.hashCode() == validator2.hashCode());
+
+        validator2.preInit(new ValidatorConfiguration("blah"), globalConfig);
+        assertEquals(validator, validator2);
+        assertEquals(validator.hashCode(), validator2.hashCode());
+    }
+
+    @Test
+    public void testToString() throws Exception {
+        assertEquals("ValidationErrorMessageTest{}", new ValidationErrorMessageTest().toString());
+        assertEquals("ValidationErrorMessageTest{hello=123}", new ValidationErrorMessageTest("hello", 123).toString());
     }
 }
 
diff --git a/redpen-core/src/test/java/cc/redpen/validator/section/ParagraphNumberValidatorTest.java b/redpen-core/src/test/java/cc/redpen/validator/section/ParagraphNumberValidatorTest.java
index f75e0563..11f6c163 100644
--- a/redpen-core/src/test/java/cc/redpen/validator/section/ParagraphNumberValidatorTest.java
+++ b/redpen-core/src/test/java/cc/redpen/validator/section/ParagraphNumberValidatorTest.java
@@ -17,11 +17,13 @@
  */
 package cc.redpen.validator.section;
 
-import cc.redpen.model.Document;
+import cc.redpen.RedPenException;
+import cc.redpen.config.Configuration;
+import cc.redpen.config.ValidatorConfiguration;
 import cc.redpen.model.Paragraph;
 import cc.redpen.model.Section;
 import cc.redpen.validator.ValidationError;
-import org.junit.BeforeClass;
+import org.junit.Before;
 import org.junit.Test;
 
 import java.util.ArrayList;
@@ -30,13 +32,11 @@ import java.util.List;
 import static org.junit.Assert.assertEquals;
 
 public class ParagraphNumberValidatorTest {
+    private ParagraphNumberValidator validator = new ParagraphNumberValidator();
 
-    private static ParagraphNumberValidator validator;
-
-    @BeforeClass
-    public static void setUp() {
-        validator = new ParagraphNumberValidator();
-        validator.setMaxParagraphNumber(3);
+    @Before
+    public void setUp() throws RedPenException {
+        validator.preInit(new ValidatorConfiguration("ParagraphNumber").addAttribute("max_num", "3"), Configuration.builder().build());
     }
 
     @Test
@@ -56,16 +56,12 @@ public class ParagraphNumberValidatorTest {
 
     @Test
     public void testSectionWithOnlyOneSection() {
-
         Section section = new Section(0);
         section.appendParagraph(new Paragraph());
 
-        Document document = Document.builder().appendSection(section).build();
-
         List<ValidationError> errors = new ArrayList<>();
         validator.setErrorList(errors);
         validator.validate(section);
         assertEquals(0, errors.size());
     }
-
 }
diff --git a/redpen-core/src/test/java/cc/redpen/validator/section/SectionLengthValidatorTest.java b/redpen-core/src/test/java/cc/redpen/validator/section/SectionLengthValidatorTest.java
index 70bd0343..3e542cd9 100644
--- a/redpen-core/src/test/java/cc/redpen/validator/section/SectionLengthValidatorTest.java
+++ b/redpen-core/src/test/java/cc/redpen/validator/section/SectionLengthValidatorTest.java
@@ -17,10 +17,13 @@
  */
 package cc.redpen.validator.section;
 
+import cc.redpen.RedPenException;
+import cc.redpen.config.Configuration;
+import cc.redpen.config.ValidatorConfiguration;
 import cc.redpen.model.Paragraph;
 import cc.redpen.model.Section;
 import cc.redpen.validator.ValidationError;
-import org.junit.BeforeClass;
+import org.junit.Before;
 import org.junit.Test;
 
 import java.util.ArrayList;
@@ -29,13 +32,12 @@ import java.util.List;
 import static org.junit.Assert.assertEquals;
 
 public class SectionLengthValidatorTest {
+    private SectionLengthValidator validator;
 
-    private static SectionLengthValidator validator;
-
-    @BeforeClass
-    public static void setUp() {
+    @Before
+    public void setUp() throws RedPenException {
         validator = new SectionLengthValidator();
-        validator.setMaxSectionLength(10);
+        validator.preInit(new ValidatorConfiguration("SectionLength").addAttribute("max_num", "10"), Configuration.builder().build());
     }
 
     @Test
diff --git a/redpen-core/src/test/java/cc/redpen/validator/sentence/EndOfSentenceValidatorTest.java b/redpen-core/src/test/java/cc/redpen/validator/sentence/EndOfSentenceValidatorTest.java
index a09398bc..b18d610c 100644
--- a/redpen-core/src/test/java/cc/redpen/validator/sentence/EndOfSentenceValidatorTest.java
+++ b/redpen-core/src/test/java/cc/redpen/validator/sentence/EndOfSentenceValidatorTest.java
@@ -29,6 +29,7 @@ import cc.redpen.parser.SentenceExtractor;
 import cc.redpen.tokenizer.JapaneseTokenizer;
 import cc.redpen.validator.ValidationError;
 import junit.framework.Assert;
+import org.junit.Before;
 import org.junit.Test;
 
 import java.util.ArrayList;
@@ -38,9 +39,15 @@ import java.util.Map;
 import static org.junit.Assert.assertEquals;
 
 public class EndOfSentenceValidatorTest {
+    private EndOfSentenceValidator validator = new EndOfSentenceValidator();
+
+    @Before
+    public void setUp() throws Exception {
+        validator.preInit(new ValidatorConfiguration("EndOfSentence"), Configuration.builder().build());
+    }
+
     @Test
     public void testInvalidEndOfSentence() {
-        EndOfSentenceValidator validator = new EndOfSentenceValidator();
         List<ValidationError> errors = new ArrayList<>();
         validator.setErrorList(errors);
         validator.validate(new Sentence("He said \"that is right\".", 0));
@@ -49,7 +56,6 @@ public class EndOfSentenceValidatorTest {
 
     @Test
     public void testValidEndOfSentence() {
-        EndOfSentenceValidator validator = new EndOfSentenceValidator();
         List<ValidationError> errors = new ArrayList<>();
         validator.setErrorList(errors);
         validator.validate(new Sentence("He said \"that is right.\"", 0));
@@ -58,16 +64,14 @@ public class EndOfSentenceValidatorTest {
 
     @Test
     public void testInValidEndOfSentenceWithQuestionMark() {
-        EndOfSentenceValidator validator = new EndOfSentenceValidator();
         List<ValidationError> errors = new ArrayList<>();
         validator.setErrorList(errors);
-        validator.validate( new Sentence("He said \"Is it right\"?", 0));
+        validator.validate(new Sentence("He said \"Is it right\"?", 0));
         assertEquals(1, errors.size());
     }
 
     @Test
     public void testVoid() {
-        EndOfSentenceValidator validator = new EndOfSentenceValidator();
         List<ValidationError> errors = new ArrayList<>();
         validator.setErrorList(errors);
         validator.validate(new Sentence("", 0));
diff --git a/redpen-core/src/test/java/cc/redpen/validator/sentence/KatakanaEndHyphenValidatorTest.java b/redpen-core/src/test/java/cc/redpen/validator/sentence/KatakanaEndHyphenValidatorTest.java
index aef1cc08..d63ffb0a 100644
--- a/redpen-core/src/test/java/cc/redpen/validator/sentence/KatakanaEndHyphenValidatorTest.java
+++ b/redpen-core/src/test/java/cc/redpen/validator/sentence/KatakanaEndHyphenValidatorTest.java
@@ -34,10 +34,10 @@ import java.util.Map;
 import static org.junit.Assert.assertEquals;
 
 public class KatakanaEndHyphenValidatorTest {
+    private KatakanaEndHyphenValidator validator = new KatakanaEndHyphenValidator();
+
     @Test
     public void testEmptyString() {
-        KatakanaEndHyphenValidator validator
-                = new KatakanaEndHyphenValidator();
         Sentence str = new Sentence("", 0);
         List<ValidationError> errors = new ArrayList<>();
         validator.setErrorList(errors);
@@ -47,8 +47,6 @@ public class KatakanaEndHyphenValidatorTest {
 
     @Test
     public void testSingleHiragana() {
-        KatakanaEndHyphenValidator validator
-                = new KatakanaEndHyphenValidator();
         Sentence str = new Sentence("あ", 0);
         List<ValidationError> errors = new ArrayList<>();
         validator.setErrorList(errors);
@@ -58,8 +56,6 @@ public class KatakanaEndHyphenValidatorTest {
 
     @Test
     public void testSingleKatakana() {
-        KatakanaEndHyphenValidator validator
-                = new KatakanaEndHyphenValidator();
         Sentence str = new Sentence("ア", 0);
         List<ValidationError> errors = new ArrayList<>();
         validator.setErrorList(errors);
@@ -69,8 +65,6 @@ public class KatakanaEndHyphenValidatorTest {
 
     @Test
     public void testKatakanaOfLength2() {
-        KatakanaEndHyphenValidator validator
-                = new KatakanaEndHyphenValidator();
         Sentence str = new Sentence("ドア", 0);
         List<ValidationError> errors = new ArrayList<>();
         validator.setErrorList(errors);
@@ -80,8 +74,6 @@ public class KatakanaEndHyphenValidatorTest {
 
     @Test
     public void testKatakanaOfLength3andHyphen() {
-        KatakanaEndHyphenValidator validator
-                = new KatakanaEndHyphenValidator();
         Sentence str = new Sentence("ミラー", 0);
         List<ValidationError> errors = new ArrayList<>();
         validator.setErrorList(errors);
@@ -91,8 +83,6 @@ public class KatakanaEndHyphenValidatorTest {
 
     @Test
     public void testKatakanaOfLength4andHyphen() {
-        KatakanaEndHyphenValidator validator
-                = new KatakanaEndHyphenValidator();
         Sentence str = new Sentence("コーヒー", 0); // This is an error.
         List<ValidationError> errors = new ArrayList<>();
         validator.setErrorList(errors);
@@ -102,8 +92,6 @@ public class KatakanaEndHyphenValidatorTest {
 
     @Test
     public void testSentenceBeginningWithKatakanaWithHypen() {
-        KatakanaEndHyphenValidator validator
-                = new KatakanaEndHyphenValidator();
         Sentence str = new Sentence("コンピューターが壊れた。", 0);
         List<ValidationError> errors = new ArrayList<>();
         validator.setErrorList(errors);
@@ -113,8 +101,6 @@ public class KatakanaEndHyphenValidatorTest {
 
     @Test
     public void testSentenceBeginningWithKatakanaWithoutHypen() {
-        KatakanaEndHyphenValidator validator
-                = new KatakanaEndHyphenValidator();
         Sentence str = new Sentence("コンピュータが壊れた。", 0);
         List<ValidationError> errors = new ArrayList<>();
         validator.setErrorList(errors);
@@ -124,8 +110,6 @@ public class KatakanaEndHyphenValidatorTest {
 
     @Test
     public void testSentenceContainKatakanaWithHypen() {
-        KatakanaEndHyphenValidator validator
-                = new KatakanaEndHyphenValidator();
         Sentence str = new Sentence("僕のコンピューターが壊れた。", 0);
         List<ValidationError> errors = new ArrayList<>();
         validator.setErrorList(errors);
@@ -135,8 +119,6 @@ public class KatakanaEndHyphenValidatorTest {
 
     @Test
     public void testSentenceContainKatakanaWitouthHypen() {
-        KatakanaEndHyphenValidator validator
-                = new KatakanaEndHyphenValidator();
         Sentence str = new Sentence("僕のコンピュータが壊れた。", 0);
         List<ValidationError> errors = new ArrayList<>();
         validator.setErrorList(errors);
@@ -146,8 +128,6 @@ public class KatakanaEndHyphenValidatorTest {
 
     @Test
     public void testSentenceEndingWithKatakanaWithHypen() {
-        KatakanaEndHyphenValidator validator
-                = new KatakanaEndHyphenValidator();
         Sentence str = new Sentence("僕のコンピューター", 0);
         List<ValidationError> errors = new ArrayList<>();
         validator.setErrorList(errors);
@@ -157,8 +137,6 @@ public class KatakanaEndHyphenValidatorTest {
 
     @Test
     public void testSentenceEndingWithKatakanaWithoutHypen() {
-        KatakanaEndHyphenValidator validator
-                = new KatakanaEndHyphenValidator();
         Sentence str = new Sentence("僕のコンピュータ", 0);
         List<ValidationError> errors = new ArrayList<>();
         validator.setErrorList(errors);
@@ -168,8 +146,6 @@ public class KatakanaEndHyphenValidatorTest {
 
     @Test
     public void testSentenceContainWithKatakanaMiddleDot() {
-        KatakanaEndHyphenValidator validator
-                = new KatakanaEndHyphenValidator();
         Sentence str = new Sentence("コーヒー・コンピューター", 0);
         List<ValidationError> errors = new ArrayList<>();
         validator.setErrorList(errors);
diff --git a/redpen-core/src/test/java/cc/redpen/validator/sentence/NumberFormatValidatorTest.java b/redpen-core/src/test/java/cc/redpen/validator/sentence/NumberFormatValidatorTest.java
index d9073aff..a0b25474 100644
--- a/redpen-core/src/test/java/cc/redpen/validator/sentence/NumberFormatValidatorTest.java
+++ b/redpen-core/src/test/java/cc/redpen/validator/sentence/NumberFormatValidatorTest.java
@@ -47,5 +47,4 @@ public class NumberFormatValidatorTest {
 
         assertEquals(st1.toString(), 6, errors.size());
     }
-
 }
diff --git a/redpen-core/src/test/java/cc/redpen/validator/sentence/ParenthesizedSentenceValidatorTest.java b/redpen-core/src/test/java/cc/redpen/validator/sentence/ParenthesizedSentenceValidatorTest.java
index 9ed58408..8d70784e 100644
--- a/redpen-core/src/test/java/cc/redpen/validator/sentence/ParenthesizedSentenceValidatorTest.java
+++ b/redpen-core/src/test/java/cc/redpen/validator/sentence/ParenthesizedSentenceValidatorTest.java
@@ -17,6 +17,9 @@
  */
 package cc.redpen.validator.sentence;
 
+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;
@@ -30,8 +33,9 @@ import static org.junit.Assert.assertEquals;
 
 public class ParenthesizedSentenceValidatorTest {
     @Test
-    public void testSingleSentence() {
+    public void testSingleSentence() throws RedPenException {
         ParenthesizedSentenceValidator validator = new ParenthesizedSentenceValidator();
+        validator.preInit(new ValidatorConfiguration("ParenthesizedSentence").addAttribute("max_nesting_level", "2"), Configuration.builder().build());
 
         List<Document> documents = new ArrayList<>();
         documents.add(
diff --git a/redpen-core/src/test/java/cc/redpen/validator/sentence/SentenceLengthValidatorTest.java b/redpen-core/src/test/java/cc/redpen/validator/sentence/SentenceLengthValidatorTest.java
index 4b494003..cb8a299e 100644
--- a/redpen-core/src/test/java/cc/redpen/validator/sentence/SentenceLengthValidatorTest.java
+++ b/redpen-core/src/test/java/cc/redpen/validator/sentence/SentenceLengthValidatorTest.java
@@ -17,8 +17,11 @@
  */
 package cc.redpen.validator.sentence;
 
+import cc.redpen.config.Configuration;
+import cc.redpen.config.ValidatorConfiguration;
 import cc.redpen.model.Sentence;
 import cc.redpen.validator.ValidationError;
+import org.junit.Before;
 import org.junit.Test;
 
 import java.util.ArrayList;
@@ -27,10 +30,15 @@ import java.util.List;
 import static org.junit.Assert.assertEquals;
 
 public class SentenceLengthValidatorTest {
+    private SentenceLengthValidator validator = new SentenceLengthValidator();
+
+    @Before
+    public void setUp() throws Exception {
+        validator.preInit(new ValidatorConfiguration("SentenceLength").addAttribute("max_len", "30"), Configuration.builder().build());
+    }
+
     @Test
     public void testWithLongSentence() {
-        SentenceLengthValidator validator = new SentenceLengthValidator();
-        validator.setLengthLimit(30);
         Sentence str = new Sentence("this is a very long long long long long long"
                 + "long long long long long long sentence.", 0);
         List<ValidationError> errors = new ArrayList<>();
@@ -41,8 +49,6 @@ public class SentenceLengthValidatorTest {
 
     @Test
     public void testWithShortSentence() {
-        SentenceLengthValidator validator = new SentenceLengthValidator();
-        validator.setLengthLimit(30);
         Sentence str = new Sentence("this is a sentence.", 0);
         List<ValidationError> errors = new ArrayList<>();
         validator.setErrorList(errors);
@@ -52,8 +58,6 @@ public class SentenceLengthValidatorTest {
 
     @Test
     public void testWithZeroLengthSentence() {
-        SentenceLengthValidator validator = new SentenceLengthValidator();
-        validator.setLengthLimit(30);
         Sentence str = new Sentence("", 0);
         List<ValidationError> errors = new ArrayList<>();
         validator.setErrorList(errors);