Repository: redpen-cc/redpen
Pull request #561 (2016-03-04 11:52:23)

Introduce abstract DictionaryValidator
This simplifies implementation of dictionary-based validators and makes **list** and **dict** attributes more consistent.

Also, validators that previously used SpellingUtils now all extend abstract SpellingDictionaryValidator for nicer code reuse.

This pull request depends on **validator-define-properties** and thus includes all commits from it until the latter is merged. Please review commits starting from https://github.com/redpen-cc/redpen/commit/5a9cb287b68314fde5f7052e597089bc235b85ad

Also, tests will not fail after **all-validators** pull request is merged.

takahi-i: @cbxp it looks build failed.

Patch:
diff --git a/redpen-core/src/main/java/cc/redpen/util/SpellingUtils.java b/redpen-core/src/main/java/cc/redpen/util/SpellingUtils.java
deleted file mode 100644
index 642246b4..00000000
--- a/redpen-core/src/main/java/cc/redpen/util/SpellingUtils.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/**
- * redpen: a text inspection tool
- * Copyright (c) 2014-2015 Recruit Technologies Co., Ltd. and contributors
- * (see CONTRIBUTORS.md)
- * <p>
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * <p>
- * http://www.apache.org/licenses/LICENSE-2.0
- * <p>
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package cc.redpen.util;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.*;
-
-/**
- * Spelling utility
- */
-public class SpellingUtils {
-
-    private static final String DEFAULT_LANGUAGE = "en";
-    private static final Logger LOG = LoggerFactory.getLogger(SpellingUtils.class);
-
-    // a map of dictionaries per language
-    private static Map<String, Set<String>> dictionaries = new HashMap<>();
-    private static final String DEFAULT_RESOURCE_PATH = "default-resources/spellchecker";
-
-    protected static void loadDictionary(String lang) {
-        if (dictionaries.get(lang) == null) {
-            String defaultDictionaryFile = DEFAULT_RESOURCE_PATH + "/spellchecker-" + lang + ".dat";
-            Set<String> dictionary = new DictionaryLoader<Set<String>>(HashSet::new, (set, line) -> set.add(line.toLowerCase()))
-              .loadCachedFromResource(defaultDictionaryFile, "spell dictionary");
-            dictionaries.put(lang, Collections.unmodifiableSet(dictionary));
-        }
-    }
-
-    /**
-     * Get the default dictionary entries for the specified language
-     *
-     * @param lang language to retrieve
-     * @return default dictionary for the specified language
-     */
-    public static Set<String> getDictionary(String lang) {
-        loadDictionary(lang == null ? DEFAULT_LANGUAGE : lang);
-        return dictionaries.get(lang);
-    }
-
-}
diff --git a/redpen-core/src/main/java/cc/redpen/validator/DictionaryValidator.java b/redpen-core/src/main/java/cc/redpen/validator/DictionaryValidator.java
new file mode 100644
index 00000000..e316f1e7
--- /dev/null
+++ b/redpen-core/src/main/java/cc/redpen/validator/DictionaryValidator.java
@@ -0,0 +1,66 @@
+package cc.redpen.validator;
+
+import cc.redpen.RedPenException;
+import cc.redpen.util.DictionaryLoader;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.stream.Stream;
+
+import static java.util.Collections.emptySet;
+import static java.util.stream.Stream.concat;
+import static org.apache.commons.lang3.StringUtils.isNotEmpty;
+
+/**
+ * Base class for dictionary-based validators.
+ */
+public abstract class DictionaryValidator extends Validator {
+  protected DictionaryLoader<Set<String>> loader = WORD_LIST;
+  private String dictionaryPrefix;
+  private Set<String> dictionary = emptySet();
+
+  public DictionaryValidator() {
+    super("list", new HashSet<>(), "dict", "");
+  }
+
+  public DictionaryValidator(Object...attributes) {
+    this();
+    addDefaultAttributes(attributes);
+  }
+
+  public DictionaryValidator(String dictionaryPrefix) {
+    this();
+    this.dictionaryPrefix = dictionaryPrefix;
+  }
+
+  public DictionaryValidator(DictionaryLoader<Set<String>> loader, String dictionaryPrefix) {
+    this(dictionaryPrefix);
+    this.loader = loader;
+  }
+
+  @Override protected void init() throws RedPenException {
+    if (dictionaryPrefix != null) {
+      String defaultDictionaryFile = "default-resources/" + dictionaryPrefix + "-" + getSymbolTable().getLang() + ".dat";
+      dictionary = loader.loadCachedFromResource(defaultDictionaryFile, getClass().getSimpleName() + " default dictionary");
+    }
+
+    String confFile = getStringAttribute("dict");
+    if (isNotEmpty(confFile)) {
+      getSetAttribute("list").addAll(loader.loadCachedFromFile(findFile(confFile), getClass().getSimpleName() + " user dictionary"));
+    }
+  }
+
+  protected boolean inDictionary(String word) {
+    Set<String> customDictionary = getSetAttribute("list");
+    return dictionary.contains(word) || customDictionary != null && customDictionary.contains(word);
+  }
+
+  protected boolean dictionaryExists() {
+    Set<String> customDictionary = getSetAttribute("list");
+    return !dictionary.isEmpty() || customDictionary != null && !customDictionary.isEmpty();
+  }
+
+  protected Stream<String> streamDictionary() {
+    return concat(dictionary.stream(), getSetAttribute("list").stream());
+  }
+}
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..1b545ab5 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,48 @@ 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());
+        setDefaultAttributes(keyValues);
+    }
+
+    protected void setDefaultAttributes(Object...keyValues) {
+        attributes = new HashMap<>();
+        addDefaultAttributes(keyValues);
+    }
+
+    protected void addDefaultAttributes(Object[] keyValues) {
+        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 +143,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 +194,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 +418,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..d39c664e 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
@@ -22,24 +22,18 @@ import cc.redpen.model.Document;
 import cc.redpen.model.Paragraph;
 import cc.redpen.model.Sentence;
 import cc.redpen.tokenizer.TokenElement;
-import cc.redpen.util.SpellingUtils;
-import cc.redpen.validator.Validator;
+import cc.redpen.validator.sentence.SpellingDictionaryValidator;
 
-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
  * acronym ABC then there must exist a sequence of capitalized words such as Axxx Bxx Cxxx.
  */
-public class UnexpandedAcronymValidator extends Validator {
-
-    private static final int MIN_ACRONYM_LENGTH_DEFAULT = 3; // TLA
+public class UnexpandedAcronymValidator extends SpellingDictionaryValidator {
 
-    // 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
@@ -47,14 +41,17 @@ public class UnexpandedAcronymValidator extends Validator {
     // the set of acronyms we found literally within the document
     private Set<String> contractedAcronyms = new HashSet<>();
 
-    // a dictionary to filter out acronyms that are just uppercase words
-    private Set<String> dictionary;
+    public UnexpandedAcronymValidator() {
+        setDefaultAttributes("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,8 +65,9 @@ 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())) {
+                    if ((word.length() >= minAcronymLength) && !inDictionary(word.toLowerCase())) {
                         contractedAcronyms.add(word);
                     }
                 } else if (isCapitalized(word)) {
@@ -93,9 +91,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 +106,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..4bbbc235 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
@@ -23,8 +23,7 @@ import cc.redpen.model.Paragraph;
 import cc.redpen.model.Sentence;
 import cc.redpen.tokenizer.TokenElement;
 import cc.redpen.util.DictionaryLoader;
-import cc.redpen.util.SpellingUtils;
-import cc.redpen.validator.Validator;
+import cc.redpen.validator.sentence.SpellingDictionaryValidator;
 
 import java.text.DecimalFormat;
 import java.util.HashMap;
@@ -33,34 +32,29 @@ import java.util.Map;
 /**
  * Ensure that known (dictionary) words are not used too frequently within the document
  */
-public class WordFrequencyValidator extends Validator {
+public class WordFrequencyValidator extends SpellingDictionaryValidator {
 
     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() {
+        setDefaultAttributes("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,39 +62,37 @@ 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()) {
             String word = token.getSurface().toLowerCase();
             if (referenceWordDeviations.get(word) != null) {
-                Integer occurances = documentWordOccurances.get(word);
-                if (occurances == null) {
+                Integer occurrences = documentWordOccurances.get(word);
+                if (occurrences == null) {
                     documentWordOccurances.put(word, 1);
                 } else {
-                    documentWordOccurances.put(word, occurances + 1);
+                    documentWordOccurances.put(word, occurrences + 1);
                 }
                 wordCount++;
-            } else if (SpellingUtils.getDictionary(getSymbolTable().getLang()).contains(word)) {
+            } else if (!dictionaryExists() || inDictionary(word)) {
                 wordCount++;
             }
         }
     }
 
     /**
-     * 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);
@@ -123,7 +115,6 @@ public class WordFrequencyValidator extends Validator {
 
     @Override
     public void validate(Document document) {
-
         // process each sentence in the document
         lastSentence = null;
         for (int i = 0; i < document.size(); i++) {
@@ -136,7 +127,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 +137,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 +147,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..1249746a 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
@@ -17,86 +17,28 @@
  */
 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 org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import cc.redpen.validator.DictionaryValidator;
 
 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 DictionaryValidator {
 
-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("doubled-word/doubled-word-skiplist");
+    }
 
     @Override
     public void validate(Sentence sentence) {
         Set<String> surfaces = new HashSet<>();
         for (TokenElement token : sentence.getTokens()) {
             String currentSurface = token.getSurface().toLowerCase();
-            if (surfaces.contains(currentSurface) && !skipList.contains(currentSurface)
-                    && !customSkipList.contains(currentSurface)) {
+            if (surfaces.contains(currentSurface) && !inDictionary(currentSurface)) {
                 addLocalizedErrorFromToken(sentence, token);
             }
             surfaces.add(currentSurface);
         }
     }
-
-    @Override
-    protected void init() throws RedPenException {
-        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"));
-        }
-    }
-
-    @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/HyphenationValidator.java b/redpen-core/src/main/java/cc/redpen/validator/sentence/HyphenationValidator.java
index 6601cb2e..893362f3 100644
--- a/redpen-core/src/main/java/cc/redpen/validator/sentence/HyphenationValidator.java
+++ b/redpen-core/src/main/java/cc/redpen/validator/sentence/HyphenationValidator.java
@@ -20,18 +20,14 @@ package cc.redpen.validator.sentence;
 
 import cc.redpen.model.Sentence;
 import cc.redpen.tokenizer.TokenElement;
-import cc.redpen.util.SpellingUtils;
-import cc.redpen.validator.Validator;
 
 /**
  * Ensure groups of words that are hyphenated in the dictionary are hyphenated in the sentence
  */
-public class HyphenationValidator extends Validator {
+public class HyphenationValidator extends SpellingDictionaryValidator {
 
     @Override
     public void validate(Sentence sentence) {
-        String lang = getSymbolTable().getLang();
-
         // consider hyphenated words of this array's length
         TokenElement tokens[] = new TokenElement[]{null, null, null, null};
 
@@ -47,7 +43,7 @@ public class HyphenationValidator extends Validator {
                 String hyphenatedForm = tokens[0].getSurface();
                 for (int j = 1; (j < tokens.length) && (tokens[j] != null); j++) {
                     hyphenatedForm += "-" + tokens[j].getSurface();
-                    if (SpellingUtils.getDictionary(lang).contains(hyphenatedForm.toLowerCase())) {
+                    if (inDictionary(hyphenatedForm.toLowerCase())) {
                         addLocalizedErrorWithPosition(
                                 "HyphenatedInDictionary",
                                 sentence,
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..a1eb1f26 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
@@ -17,88 +17,24 @@
  */
 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 cc.redpen.validator.DictionaryValidator;
 
 /**
  * Validate input sentences contain invalid expression.
  */
-final public 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 final class InvalidExpressionValidator extends DictionaryValidator {
+    public InvalidExpressionValidator() {
+        super("invalid-expression/invalid-expression");
+    }
 
     @Override
     public void validate(Sentence sentence) {
-        Consumer<String> tConsumer = value -> {
+        streamDictionary().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";
-        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"));
-        }
-    }
-
-    @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..67790863 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
@@ -17,85 +17,35 @@
  */
 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 org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import cc.redpen.validator.DictionaryValidator;
 
-import java.util.*;
+import java.util.List;
+import java.util.Locale;
+
+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 Set<String> invalidWords;
-    private Set<String> customInvalidWords = new HashSet<>();
+public final class InvalidWordValidator extends DictionaryValidator {
+    public InvalidWordValidator() {
+        super("invalid-word/invalid-word");
+    }
 
     @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 word = token.getSurface().toLowerCase();
+            if (inDictionary(word)) {
                 addLocalizedErrorFromToken(sentence, token);
             }
         }
     }
-
-    @Override
-    protected void init() throws RedPenException {
-        String lang = getSymbolTable().getLang();
-        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"));
-        }
-    }
-
-    @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..c9f5ae2d 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
@@ -17,14 +17,15 @@
  */
 package cc.redpen.validator.sentence;
 
-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 cc.redpen.validator.DictionaryValidator;
 
-import java.util.*;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+
+import static java.util.Collections.singletonList;
 
 /**
  * Validate the end hyphens of Katakana words in Japanese documents.
@@ -45,10 +46,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 DictionaryValidator {
     /**
      * Default Katakana limit length without hypen.
      */
@@ -62,12 +60,6 @@ final public class KatakanaEndHyphenValidator extends Validator {
      */
     private static final char KATAKANA_MIDDLE_DOT = '・';
 
-    private Set<String> customSkipList;
-
-    public KatakanaEndHyphenValidator() {
-        super();
-    }
-
     public static boolean isKatakanaEndHyphen(String katakana) {
         return (DEFAULT_KATAKANA_LIMIT_LENGTH < katakana.length()
                 && katakana.charAt(katakana.length() - 1) == HYPHEN);
@@ -75,12 +67,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,51 +85,12 @@ 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);
             }
         }
     }
-
-    @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"));
-        }
-    }
-
-    @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..35258e22 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
@@ -21,12 +21,12 @@ import cc.redpen.RedPenException;
 import cc.redpen.model.Sentence;
 import cc.redpen.util.LevenshteinDistance;
 import cc.redpen.util.StringUtils;
-import cc.redpen.validator.Validator;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import cc.redpen.validator.DictionaryValidator;
 
 import java.util.*;
 
+import static java.util.Collections.singletonList;
+
 /**
  * Validate the correctness of Katakana word spelling.
  * Japanese Katakana words have orthographical variations.
@@ -47,31 +47,11 @@ 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 DictionaryValidator {
     /**
      * Default dictionary for Katakana spell checking.
      */
     private static final String DEFAULT_RESOURCE_PATH = "default-resources/katakana";
-    /**
-     * Logger
-     */
-    private static final Logger LOG =
-            LoggerFactory.getLogger(KatakanaSpellCheckValidator.class);
 
     /**
      * Katakana word dic with line number.
@@ -82,17 +62,18 @@ 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("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 +119,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,59 +143,10 @@ final public class KatakanaSpellCheckValidator extends Validator {
 
     @Override
     protected void init() throws RedPenException {
-        boolean disableDefault = getConfigAttributeAsBoolean("disable-default", false);
-        if (!disableDefault) {
+        super.init();
+        if (!getBooleanAttribute("disable-default")) {
             String defaultDictionaryFile = DEFAULT_RESOURCE_PATH + "/katakana-spellcheck.dat";
             exceptions = WORD_LIST.loadCachedFromResource(defaultDictionaryFile, "katakana word dictionary");
         }
-
-        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"));
-            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/SpellingDictionaryValidator.java b/redpen-core/src/main/java/cc/redpen/validator/sentence/SpellingDictionaryValidator.java
new file mode 100644
index 00000000..7767cef0
--- /dev/null
+++ b/redpen-core/src/main/java/cc/redpen/validator/sentence/SpellingDictionaryValidator.java
@@ -0,0 +1,12 @@
+package cc.redpen.validator.sentence;
+
+import cc.redpen.validator.DictionaryValidator;
+
+/**
+ * Base class for Validators using the spelling dictionary.
+ */
+public abstract class SpellingDictionaryValidator extends DictionaryValidator {
+  public SpellingDictionaryValidator() {
+    super(WORD_LIST_LOWERCASED, "spellchecker/spellchecker");
+  }
+}
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..31e37d4c 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
@@ -17,48 +17,11 @@
  */
 package cc.redpen.validator.sentence;
 
-import cc.redpen.RedPenException;
 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);
 
+public final class SpellingValidator extends SpellingDictionaryValidator {
     private static String skipCharacters = "[\\!-/:-@\\[-`{-~]";
-    // TODO: replace more memory efficient data structure
-    private Set<String> defaultDictionary;
-    private Set<String> customDictionary;
-
-
-    @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"));
-        }
-    }
 
     @Override
     public void validate(Sentence sentence) {
@@ -68,7 +31,7 @@ public class SpellingValidator extends Validator {
                 continue;
             }
 
-            if (!this.defaultDictionary.contains(surface) && !this.customDictionary.contains(surface)) {
+            if (dictionaryExists() && !inDictionary(surface)) {
                 addLocalizedErrorFromToken(sentence, token);
             }
         }
@@ -77,32 +40,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..75450536 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
@@ -17,28 +17,26 @@
  */
 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 org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import cc.redpen.validator.DictionaryValidator;
 
-import java.util.*;
+import java.util.List;
+import java.util.Locale;
+
+import static java.util.Collections.singletonList;
 
 /**
  * Check if the input sentence start with a capital letter.
  */
-final public class StartWithCapitalLetterValidator extends Validator {
-    private static final String DEFAULT_RESOURCE_PATH = "default-resources/capital-letter-exception-list";
-    private static final Logger LOG =
-            LoggerFactory.getLogger(SpellingValidator.class);
-    private Set<String> whiteList;
-    private Set<String> customWhiteList = new HashSet<>();
+final public class StartWithCapitalLetterValidator extends DictionaryValidator {
+    public StartWithCapitalLetterValidator() {
+        super("capital-letter-exception-list/capital-case-exception-list");
+    }
 
     @Override
     public List<String> getSupportedLanguages() {
-        return Arrays.asList(Locale.ENGLISH.getLanguage());
+        return singletonList(Locale.ENGLISH.getLanguage());
     }
 
     @Override
@@ -53,7 +51,7 @@ final public class StartWithCapitalLetterValidator extends Validator {
             }
         }
 
-        if (tokens.size() == 0 || this.whiteList.contains(headWord) || this.customWhiteList.contains(headWord)) {
+        if (tokens.size() == 0 || inDictionary(headWord)) {
             return;
         }
 
@@ -73,42 +71,4 @@ final public class StartWithCapitalLetterValidator extends Validator {
             addLocalizedError(sentence, headChar);
         }
     }
-
-    @Override
-    protected void init() throws RedPenException {
-        String defaultDictionaryFile = DEFAULT_RESOURCE_PATH + "/default-capital-case-exception-list.dat";
-        whiteList = WORD_LIST.loadCachedFromResource(defaultDictionaryFile, "capital letter exception dictionary");
-
-        Optional<String> confFile = getConfigAttribute("dict");
-        if (confFile.isPresent()) {
-            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..c8190f72 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
@@ -25,16 +25,21 @@ import org.slf4j.LoggerFactory;
 
 import java.util.HashMap;
 import java.util.Map;
-import java.util.Optional;
+
+import static org.apache.commons.lang3.StringUtils.isNotEmpty;
 
 /**
  * If input sentences contain invalid expressions, this validator
  * returns the errors with corrected expressions.
  */
-final public class SuggestExpressionValidator extends Validator {
+public final class SuggestExpressionValidator extends Validator {
     private static final Logger LOG = LoggerFactory.getLogger(SuggestExpressionValidator.class);
     private Map<String, String> synonyms = new HashMap<>();
 
+    public SuggestExpressionValidator() {
+        super("dict", "");
+    }
+
     @Override
     public void validate(Sentence sentence) {
         synonyms.keySet().stream().forEach(value -> {
@@ -49,10 +54,10 @@ final public class SuggestExpressionValidator extends Validator {
     @Override
     protected void init() throws RedPenException {
         //TODO: support default dictionary.
-        Optional<String> confFile = getConfigAttribute("dict");
-        if (confFile.isPresent()) {
+        String confFile = getStringAttribute("dict");
+        if (isNotEmpty(confFile)) {
             LOG.info("Dictionary file is " + confFile);
-            synonyms = KEY_VALUE.loadCachedFromFile(findFile(confFile.get()), "SuggestExpressionValidator dictionary");
+            synonyms = KEY_VALUE.loadCachedFromFile(findFile(confFile), "SuggestExpressionValidator dictionary");
         }
         else {
             LOG.warn("Dictionary file is not specified");
@@ -62,27 +67,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;
-    }
 }
diff --git a/redpen-core/src/main/resources/default-resources/capital-letter-exception-list/default-capital-case-exception-list.dat b/redpen-core/src/main/resources/default-resources/capital-letter-exception-list/capital-case-exception-list-en.dat
similarity index 70%
rename from redpen-core/src/main/resources/default-resources/capital-letter-exception-list/default-capital-case-exception-list.dat
rename to redpen-core/src/main/resources/default-resources/capital-letter-exception-list/capital-case-exception-list-en.dat
index bc0c4107..8e5d1c23 100644
--- a/redpen-core/src/main/resources/default-resources/capital-letter-exception-list/default-capital-case-exception-list.dat
+++ b/redpen-core/src/main/resources/default-resources/capital-letter-exception-list/capital-case-exception-list-en.dat
@@ -1,4 +1,4 @@
 iPhone
 mixi
 mRNA
-iTune
+iTunes


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/section/WordFrequencyValidatorTest.java b/redpen-core/src/test/java/cc/redpen/validator/section/WordFrequencyValidatorTest.java
index e8fdcd48..b0f1fded 100644
--- a/redpen-core/src/test/java/cc/redpen/validator/section/WordFrequencyValidatorTest.java
+++ b/redpen-core/src/test/java/cc/redpen/validator/section/WordFrequencyValidatorTest.java
@@ -18,6 +18,8 @@
 package cc.redpen.validator.section;
 
 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;
@@ -32,31 +34,26 @@ import static org.junit.Assert.assertEquals;
 
 public class WordFrequencyValidatorTest {
 
+    String text[] = {
+      "Mesa Verde National Park is a U.S. National Park and UNESCO World Heritage Site located in Montezuma County, Colorado.",
+      "It protects some of the best preserved Ancestral Puebloan archeological sites in the United States.",
+      "The park was created by President Theodore Roosevelt in 1906.",
+      "It occupies 52,485 acres (21,240 ha) near the Four Corners region, and with more than 4,000 sites and 600 cliff dwellings, it is the largest archeological preserve in the US.",
+      "Mesa Verde (Spanish for 'green table') is best known for structures such as Cliff Palace, thought to be the largest cliff dwelling in North America.",
+      "Starting c. 7,500 BCE, Mesa Verde was seasonally inhabited by a group of nomadic Paleo-Indians known as the Foothills Mountain Complex.",
+      "The variety of projectile points found in the region indicates they were influenced by surrounding areas, including the Great Basin, the San Juan Basin, and the Rio Grande Valley.",
+      "Later, Archaic people established semi-permanent rockshelters in and around the mesa.",
+      "By 1,000, the Basketmaker culture emerged from the local Archaic population, and by 750 CE the Ancestral Puebloans had developed from the Basketmaker culture.",
+      "The Mesa Verdeans survived by utilizing a combination of hunting, gathering, and subsistence farming of crops such as corn, beans, and squash.",
+      "They built the mesa's first pueblos sometime after 650, and by the end of the 12th century they began to construct the massive cliff dwellings for which the park is best known.",
+      "By 1285, following a period of social and environmental instability driven by a series of severe and prolonged droughts, they abandoned the area and moved south to locations in Arizona and New Mexico, including Rio Chama, Pajarito Plateau, and Santa Fe."
+    };
+
     @Test
     public void testDocument() throws RedPenException {
         WordFrequencyValidator validator = (WordFrequencyValidator) ValidatorFactory.getInstance("WordFrequency");
 
-        String text[] = {
-                "Mesa Verde National Park is a U.S. National Park and UNESCO World Heritage Site located in Montezuma County, Colorado.",
-                "It protects some of the best preserved Ancestral Puebloan archeological sites in the United States.",
-                "The park was created by President Theodore Roosevelt in 1906.",
-                "It occupies 52,485 acres (21,240 ha) near the Four Corners region, and with more than 4,000 sites and 600 cliff dwellings, it is the largest archeological preserve in the US.",
-                "Mesa Verde (Spanish for 'green table') is best known for structures such as Cliff Palace, thought to be the largest cliff dwelling in North America.",
-                "Starting c. 7,500 BCE, Mesa Verde was seasonally inhabited by a group of nomadic Paleo-Indians known as the Foothills Mountain Complex.",
-                "The variety of projectile points found in the region indicates they were influenced by surrounding areas, including the Great Basin, the San Juan Basin, and the Rio Grande Valley.",
-                "Later, Archaic people established semi-permanent rockshelters in and around the mesa.",
-                "By 1,000, the Basketmaker culture emerged from the local Archaic population, and by 750 CE the Ancestral Puebloans had developed from the Basketmaker culture.",
-                "The Mesa Verdeans survived by utilizing a combination of hunting, gathering, and subsistence farming of crops such as corn, beans, and squash.",
-                "They built the mesa's first pueblos sometime after 650, and by the end of the 12th century they began to construct the massive cliff dwellings for which the park is best known.",
-                "By 1285, following a period of social and environmental instability driven by a series of severe and prolonged droughts, they abandoned the area and moved south to locations in Arizona and New Mexico, including Rio Chama, Pajarito Plateau, and Santa Fe."
-        };
-
-        Document.DocumentBuilder builder = Document.builder(new WhiteSpaceTokenizer()).addSection(1).addParagraph();
-        int sentenceNumber = 1;
-        for (String line : text) {
-            builder.addSentence(new Sentence(line, sentenceNumber++));
-        }
-        Document document = builder.build();
+        Document document = buildDocument();
 
         List<ValidationError> errors = new ArrayList<>();
         validator.setErrorList(errors);
@@ -64,4 +61,27 @@ public class WordFrequencyValidatorTest {
 
         assertEquals(1, errors.size());
     }
+
+    @Test
+    public void testEmptySpellingDictionary() throws Exception {
+        WordFrequencyValidator validator = new WordFrequencyValidator();
+        validator.preInit(new ValidatorConfiguration("WordFrequency"), Configuration.builder("za").build());
+
+        Document document = buildDocument();
+
+        List<ValidationError> errors = new ArrayList<>();
+        validator.setErrorList(errors);
+        validator.validate(document);
+
+        assertEquals(0, errors.size());
+    }
+
+    private Document buildDocument() {
+        Document.DocumentBuilder builder = Document.builder(new WhiteSpaceTokenizer()).addSection(1).addParagraph();
+        int sentenceNumber = 1;
+        for (String line : text) {
+            builder.addSentence(new Sentence(line, sentenceNumber++));
+        }
+        return builder.build();
+    }
 }
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);
diff --git a/redpen-core/src/test/java/cc/redpen/validator/sentence/SpellingValidatorTest.java b/redpen-core/src/test/java/cc/redpen/validator/sentence/SpellingValidatorTest.java
index 315fd109..a0bbb47e 100644
--- a/redpen-core/src/test/java/cc/redpen/validator/sentence/SpellingValidatorTest.java
+++ b/redpen-core/src/test/java/cc/redpen/validator/sentence/SpellingValidatorTest.java
@@ -140,4 +140,16 @@ public class SpellingValidatorTest extends BaseValidatorTest {
         Map<Document, List<ValidationError>> errors = redPen.validate(singletonList(document));
         assertEquals(0, errors.get(document).size());
     }
+
+    @Test
+    public void doNotShowErrorsInCaseOfMissingDictionary() throws Exception {
+        Document document = prepareSimpleDocument("test");
+        config = Configuration.builder("foo")
+          .addValidatorConfig(new ValidatorConfiguration(validatorName))
+          .build();
+
+        RedPen redPen = new RedPen(config);
+        Map<Document, List<ValidationError>> errors = redPen.validate(singletonList(document));
+        assertEquals(0, errors.get(document).size());
+    }
 }
diff --git a/redpen-core/src/test/java/cc/redpen/validator/sentence/StartWithCapitalLetterValidatorTest.java b/redpen-core/src/test/java/cc/redpen/validator/sentence/StartWithCapitalLetterValidatorTest.java
index 3cb36277..cd9acc4b 100644
--- a/redpen-core/src/test/java/cc/redpen/validator/sentence/StartWithCapitalLetterValidatorTest.java
+++ b/redpen-core/src/test/java/cc/redpen/validator/sentence/StartWithCapitalLetterValidatorTest.java
@@ -27,7 +27,6 @@ import cc.redpen.tokenizer.JapaneseTokenizer;
 import cc.redpen.validator.ValidationError;
 import cc.redpen.validator.Validator;
 import cc.redpen.validator.ValidatorFactory;
-import junit.framework.Assert;
 import org.junit.Test;
 
 import java.util.ArrayList;
@@ -160,10 +159,9 @@ public class StartWithCapitalLetterValidatorTest {
 
         RedPen redPen = new RedPen(config);
         Map<Document, List<ValidationError>> errors = redPen.validate(documents);
-        Assert.assertEquals(0, errors.get(documents.get(0)).size());
+        assertEquals(0, errors.get(documents.get(0)).size());
     }
 
-
     @Test
     public void testDetectStartWithSmallCharacterInSecondSentence() throws RedPenException {
         Configuration config = Configuration.builder()
@@ -180,6 +178,6 @@ public class StartWithCapitalLetterValidatorTest {
 
         RedPen redPen = new RedPen(config);
         Map<Document, List<ValidationError>> errors = redPen.validate(documents);
-        Assert.assertEquals(1, errors.get(documents.get(0)).size());
+        assertEquals(1, errors.get(documents.get(0)).size());
     }
 }