Java tutorial
/** * 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.validator; import cc.redpen.RedPenException; import cc.redpen.config.Configuration; import cc.redpen.config.SymbolTable; import cc.redpen.config.ValidatorConfiguration; import cc.redpen.model.Document; import cc.redpen.model.Section; import cc.redpen.model.Sentence; import cc.redpen.parser.LineOffset; import cc.redpen.tokenizer.TokenElement; import cc.redpen.util.DictionaryLoader; import cc.redpen.util.RuleExtractor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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.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(FORMAT_DEFAULT); private Map<String, Object> defaultProps; private ResourceBundle errorMessages = null; protected ValidatorConfiguration config; protected Configuration globalConfig; private Locale locale; private String validatorName = this.getClass().getSimpleName(); public Validator() { this(new Object[0]); } /** * @param keyValues String key and Object value pairs for supported config properties. */ public Validator(Object... keyValues) { setLocale(Locale.getDefault()); setDefaultProperties(keyValues); } protected void setDefaultProperties(Object... keyValues) { defaultProps = new LinkedHashMap<>(); addDefaultProperties(keyValues); } protected void addDefaultProperties(Object... keyValues) { if (keyValues.length % 2 != 0) throw new IllegalArgumentException("Not enough values specified"); for (int i = 0; i < keyValues.length; i += 2) { defaultProps.put(keyValues[i].toString(), keyValues[i + 1]); } } private List<ValidationError> errors; public void setErrorList(List<ValidationError> errors) { this.errors = errors; } /** * Process input blocks before run validation. This method is used to store * the information needed to run Validator before the validation process. * * @param sentence input sentence */ public void preValidate(Sentence sentence) { } /** * Process input blocks before run validation. This method is used to store * the information needed to run Validator before the validation process. * * @param section input section */ public void preValidate(Section section) { } /** * Process input blocks before run validation. This method is used to store * the information needed to run Validator before the validation process. * * @param document input document */ public void preValidate(Document document) { } /** * validate the input document and returns the invalid points. * {@link cc.redpen.validator.Validator} provides empty implementation. Validator implementation validates documents can override this method. * * @param document input */ public void validate(Document document) { } /** * validate the input document and returns the invalid points. * {@link cc.redpen.validator.Validator} provides empty implementation. Validator implementation validates sentences can override this method. * * @param sentence input */ public void validate(Sentence sentence) { } /** * validate the input document and returns the invalid points. * {@link cc.redpen.validator.Validator} provides empty implementation. Validator implementation validates sections can override this method. * * @param section input */ public void validate(Section section) { } /** * Return an array of languages supported by this validator * {@link cc.redpen.validator.Validator} provides empty implementation. Validator implementation validates sections can override this method. * * @return an array of the languages supported by this validator. An empty list implies there are no restrictions on the languages supported by this validator. */ public List<String> getSupportedLanguages() { return Collections.emptyList(); } public void preInit(ValidatorConfiguration config, Configuration globalConfig) throws RedPenException { this.config = config; this.globalConfig = globalConfig; init(); } void setLocale(Locale locale) { this.locale = locale; // getPackage() would return null for default package String packageName = this.getClass().getPackage() != null ? this.getClass().getPackage().getName() : ""; try { errorMessages = ResourceBundle.getBundle(packageName + "." + this.getClass().getSimpleName(), locale, fallbackControl); } catch (MissingResourceException ignore) { try { errorMessages = ResourceBundle.getBundle(packageName + ".error-messages", locale, fallbackControl); } catch (MissingResourceException ignoreAgain) { } } } /** * Return the configuration properties * * @return a map of configuration properties to their values */ @Deprecated public Map<String, String> getConfigAttributes() { return config.getProperties(); } /** * Validation initialization, called after the configuration and symbol tables have been assigned * * @throws RedPenException when failed to initialize */ protected void init() throws RedPenException { } public Map<String, Object> getProperties() { return defaultProps; } Object getOrDefault(String name) { Object value = null; if (config != null) { value = config.getProperty(name); } if (value == null) { value = defaultProps.get(name); } return value; } protected int getInt(String name) { Object value = getOrDefault(name); if (value instanceof Integer) { return (int) value; } else { return Integer.valueOf((String) value); } } protected float getFloat(String name) { Object value = getOrDefault(name); if (value instanceof Float) { return (float) value; } else { return Float.valueOf((String) value); } } protected String getString(String name) { return config.getProperties().getOrDefault(name, (String) defaultProps.get(name)); } protected boolean getBoolean(String name) { Object value = getOrDefault(name); if (value instanceof Boolean) { return (boolean) value; } else { return Boolean.valueOf((String) value); } } @SuppressWarnings("unchecked") protected Set<String> getSet(String name) { Object value = null; if (config != null) { value = config.getProperty(name); } if (isEmpty(((String) value))) { value = defaultProps.get(name); } if (value == null) { return null; } if (value instanceof Set) { return (Set<String>) value; } Set<String> newValue = Arrays.stream(((String) value).split(",")).map(String::toLowerCase).collect(toSet()); defaultProps.put(name, newValue); return newValue; } @SuppressWarnings("unchecked") protected Map<String, String> getMap(String name) { Object value = null; if (config != null) { value = config.getProperty(name); } if (isEmpty(((String) value))) { value = defaultProps.get(name); } if (value == null) { return null; } if (value instanceof Map) { return (Map<String, String>) value; } Map<String, String> newValue = parseMap((String) value); defaultProps.put(name, newValue); return newValue; } private Map<String, String> parseMap(String mapStr) { Map<String, String> map = new HashMap<>(); int start = 0, splitter = 0, end = 0; boolean found = false; for (int i = 0; i < mapStr.length(); i++) { if (mapStr.charAt(i) == '{') { start = i + 1; } else if (mapStr.charAt(i) == '}') { end = i - 1; found = true; } else if (mapStr.charAt(i) == ',') { if (found == false) { splitter = i + 1; // e.g., SVM, SupportVector Machine continue; } // extract key value pair String key = mapStr.substring(start, splitter - 1); while (mapStr.charAt(splitter + 1) == ' ') { ++splitter; } // skip white spaces String value = mapStr.substring(splitter, end + 1); map.put(key, value); // move pivots start = i + 1; end = i + 1; splitter = i + 1; found = false; } } // extract last key value pair if (splitter > 0 && end < mapStr.length()) { // for safe String key = mapStr.substring(start, splitter - 1); String value = mapStr.substring(splitter, end + 1); map.put(key, value); } return map; } protected Optional<String> getConfigAttribute(String name) { return Optional.ofNullable(config.getProperty(name)); } protected void setValidatorName(String validatorName) { this.validatorName = validatorName; } /** @deprecated Please use constructor with default properties instead, and then getXXX() 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() { return globalConfig.getSymbolTable(); } protected File findFile(String relativePath) throws RedPenException { return globalConfig.findFile(relativePath); } protected ValidatorConfiguration.LEVEL getLevel() { if (config == null) { return ValidatorConfiguration.LEVEL.ERROR; } return config.getLevel(); } /** * create a ValidationError for the specified position with specified message * * @param message message * @param sentenceWithError sentence */ protected void addError(String message, Sentence sentenceWithError) { errors.add(new ValidationError(this.validatorName, message, sentenceWithError, getLevel())); } /** * create a ValidationError for the specified position with specified message * * @param message message * @param sentenceWithError sentence * @param start start position * @param end end position */ protected void addErrorWithPosition(String message, Sentence sentenceWithError, int start, int end) { errors.add(new ValidationError(this.validatorName, message, sentenceWithError, start, end, getLevel())); } /** * create a ValidationError for the specified position with localized default error message * * @param sentenceWithError sentence * @param args objects to format */ protected void addLocalizedError(Sentence sentenceWithError, Object... args) { errors.add(new ValidationError(this.validatorName, getLocalizedErrorMessage(null, args), sentenceWithError, getLevel())); } /** * create a ValidationError for the specified position with localized message with specified message key * * @param messageKey messageKey * @param sentenceWithError sentence * @param args objects to format */ protected void addLocalizedError(String messageKey, Sentence sentenceWithError, Object... args) { errors.add(new ValidationError(this.validatorName, getLocalizedErrorMessage(messageKey, args), sentenceWithError, getLevel())); } /** * create a ValidationError using the details within the given token & localized message * * @param sentenceWithError sentence * @param token the TokenElement that has the error */ protected void addLocalizedErrorFromToken(Sentence sentenceWithError, TokenElement token, Object... args) { List<Object> argList = new ArrayList<>(); for (Object o : args) argList.add(o); argList.add(0, token.getSurface()); addLocalizedErrorWithPosition(sentenceWithError, token.getOffset(), token.getOffset() + token.getSurface().length(), argList.toArray()); } /** * create a ValidationError for the specified position with default localized error message * * @param sentenceWithError sentence * @param start start position in parsed sentence * @param end end position in parsed sentence * @param args objects to format */ protected void addLocalizedErrorWithPosition(Sentence sentenceWithError, int start, int end, Object... args) { addLocalizedErrorWithPosition(null, sentenceWithError, start, end, args); } /** * create a ValidationError for the specified position with specified message key * * @param messageKey messageKey * @param sentenceWithError sentence * @param start start position in parsed sentence * @param end end position in parsed sentence * @param args objects to format */ protected void addLocalizedErrorWithPosition(String messageKey, Sentence sentenceWithError, int start, int end, Object... args) { errors.add(new ValidationError(this.validatorName, getLocalizedErrorMessage(messageKey, args), sentenceWithError, start, end, getLevel())); } /** * returns localized error message for the given key formatted with argument * * @param key message key * @param args objects to format * @return localized error message */ protected String getLocalizedErrorMessage(String key, Object... args) { if (errorMessages != null) { String suffix = key != null ? "." + key : ""; MessageFormat fmt = new MessageFormat(errorMessages.getString(this.getClass().getSimpleName() + suffix), locale); return fmt.format(args); } else { throw new AssertionError("message resource not found."); } } /** * create a ValidationError for the specified position with default error message * * @param sentenceWithError sentence * @param args objects to format * @deprecated use {@link #addLocalizedError(Sentence, Object...)} instead */ protected void addValidationError(Sentence sentenceWithError, Object... args) { addLocalizedError(sentenceWithError, args); } /** * create a ValidationError for the specified position with specified message key * * @param messageKey messageKey * @param sentenceWithError sentence * @param args objects to format * @deprecated use {@link #addLocalizedError(String, Sentence, Object...)} instead */ protected void addValidationError(String messageKey, Sentence sentenceWithError, Object... args) { addLocalizedError(messageKey, sentenceWithError, args); } /** * create a ValidationError using the details within the given token * * @param sentenceWithError sentence * @param token the TokenElement that has the error * @deprecated use {@link #addLocalizedErrorFromToken(Sentence, TokenElement)} instead */ protected void addValidationErrorFromToken(Sentence sentenceWithError, TokenElement token) { addLocalizedError(sentenceWithError, token); } /** * create a ValidationError for the specified position with default error message * * @param sentenceWithError sentence * @param start start position * @param end end position * @param args objects to format * @deprecated use {@link #addLocalizedErrorWithPosition(Sentence, int, int, Object...)} instead */ protected void addValidationErrorWithPosition(Sentence sentenceWithError, Optional<LineOffset> start, Optional<LineOffset> end, Object... args) { errors.add(new ValidationError(this.getClass(), getLocalizedErrorMessage(null, args), sentenceWithError, start.get(), end.get())); } /** * create a ValidationError for the specified position with specified message key * * @param messageKey messageKey * @param sentenceWithError sentence * @param start start position * @param end end position * @param args objects to format * @deprecated use {@link #addLocalizedErrorWithPosition(String, Sentence, int, int, Object...)} instead */ protected void addValidationErrorWithPosition(String messageKey, Sentence sentenceWithError, 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() + defaultProps; } @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 */ protected final static DictionaryLoader<Map<String, String>> KEY_VALUE = new DictionaryLoader<>(HashMap::new, (map, line) -> { String[] result = line.split("\t"); if (result.length == 2) { map.put(result[0], result[1]); } else { LOG.error("Skip to load line... Invalid line: " + line); } }); /** * Resource Extractor loads rule dictionary */ protected final static DictionaryLoader<Set<ExpressionRule>> RULE = new DictionaryLoader<>(HashSet::new, (set, line) -> set.add(RuleExtractor.run(line))); /** * Resource Extractor loads word list */ protected final static DictionaryLoader<Set<String>> WORD_LIST = new DictionaryLoader<>(HashSet::new, Set::add); /** * Resource Extractor loads word list while lowercasting lines */ protected final static DictionaryLoader<Set<String>> WORD_LIST_LOWERCASED = new DictionaryLoader<>(HashSet::new, (set, line) -> set.add(line.toLowerCase())); }