com.googlecode.l10nmavenplugin.ValidateMojo Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of l10n-maven-plugin Show documentation
Show all versions of l10n-maven-plugin Show documentation
Maven plugin to validate localization resources in Java properties files
/*******************************************************************************
* Copyright (c) 2012 Romain Quinio (http://code.google.com/p/l10n-maven-plugin)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
******************************************************************************/
package com.googlecode.l10nmavenplugin;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import com.googlecode.l10nmavenplugin.log.L10nValidatorLogger;
import com.googlecode.l10nmavenplugin.model.L10nReportItem;
import com.googlecode.l10nmavenplugin.model.Property;
import com.googlecode.l10nmavenplugin.model.PropertyFamily;
import com.googlecode.l10nmavenplugin.validators.L10nValidationException;
import com.googlecode.l10nmavenplugin.validators.L10nValidator;
import com.googlecode.l10nmavenplugin.validators.bundle.HtmlTagCoherenceValidator;
import com.googlecode.l10nmavenplugin.validators.bundle.IdenticalTranslationValidator;
import com.googlecode.l10nmavenplugin.validators.bundle.MissingTranslationValidator;
import com.googlecode.l10nmavenplugin.validators.bundle.ParametricCoherenceValidator;
import com.googlecode.l10nmavenplugin.validators.orchestrator.DirectoryValidator;
import com.googlecode.l10nmavenplugin.validators.orchestrator.PropertiesFamilyValidator;
import com.googlecode.l10nmavenplugin.validators.orchestrator.PropertyFamilyValidator;
import com.googlecode.l10nmavenplugin.validators.orchestrator.PropertyValidator;
import com.googlecode.l10nmavenplugin.validators.property.DefaultValidator;
import com.googlecode.l10nmavenplugin.validators.property.HtmlValidator;
import com.googlecode.l10nmavenplugin.validators.property.JsValidator;
import com.googlecode.l10nmavenplugin.validators.property.ParametricMessageValidator;
import com.googlecode.l10nmavenplugin.validators.property.PatternValidator;
import com.googlecode.l10nmavenplugin.validators.property.PlainTextValidator;
import com.googlecode.l10nmavenplugin.validators.property.SpellCheckValidator;
import com.googlecode.l10nmavenplugin.validators.property.TrailingWhitespaceValidator;
import com.googlecode.l10nmavenplugin.validators.property.UrlValidator;
/**
* Validate a set of l10n {@link Properties} files against:
*
*
* - Missing javascript escaping for resources evaluated client side
* - Bad escaping for {@link java.text.MessageFormat} in case of parametric replacement
* - Invalid XHTML 1.0 transitional
* - Malformed absolute URLs
* - Plain text resources containing HTML/URL
*
*
* In case multiple checks are performed on a resource (ex: client side resource with parameters), the order above applies.
*
* The syntax of properties file itself is not checked, but it relies on loading them successfully as {@link Properties}.
*
*
* @note References for escape sequences and special characters:
*
* - Java Properties: {@link Properties#load}
* - Java MessageFormat: {@link java.text.MessageFormat}
* - Javascript: {@link http://www.w3schools.com/js/js_special_characters.asp}
* - JSON {@link http://json.org/}
* - XHTML: {@link http://www.w3schools.com/tags/ref_entities.asp}
* - URL: {@link http://www.w3schools.com/tags/ref_urlencode.asp}
*
* Extra references for development:
*
* - Java Pattern: {@link java.util.regex.Pattern}
* - Java String: {@link http://java.sun.com/docs/books/jls/second_edition/html/lexical.doc.html#101089}
*
*
* @goal validate
* @phase test
* @since 1.0
* @author romain.quinio
*
*/
public class ValidateMojo extends AbstractMojo implements L10nValidationConfiguration {
/**
* Directory containing properties file to check
*
* @parameter default-value="src\\main\\resources"
* @since 1.0
*/
private File propertyDir;
/**
* Keys excluded from validation. Default is none.
*
* @parameter
* @since 1.0
*/
private String[] excludedKeys = new String[] {};
/**
* Make validation failure not blocking the build
*
* @parameter default-value="false"
* @since 1.0
*/
private boolean ignoreFailure = false;
/**
* List of keys to match as text resources used from js. Default is ".js.".
*
* @parameter
* @since 1.0
*/
private String[] jsKeys = new String[] { ".js." };
/**
* Declares how the client side resources are loaded in javascript:
*
* - double quoted: var jsResource = "
"
* - single quoted: var jsResource = '
'
*
*
* Default value is true (double quoted), which complies with JSON format.
*
* @parameter default-value="true"
* @since 1.3
*/
private boolean jsDoubleQuoted = true;
/**
* List of keys to match as url resources. Default is ".url.".
*
* @parameter
* @since 1.0
*/
private String[] urlKeys = new String[] { ".url." };
/**
* List of keys to match as html text resources. Default is ".text.".
*
* @parameter
* @since 1.0
*/
private String[] htmlKeys = new String[] { ".text." };
/**
* XML Schema to use for html resource validation. Default value is to use XHTML1 transitional.
*
* @parameter default-value="xhtml1-transitional.xsd"
* @since 1.3
*/
private File xhtmlSchema;
/**
* List of keys to match as non-html text resources. Default is ".title.".
*
* @parameter
* @since 1.1
*/
private String[] textKeys = new String[] { ".title." };
/**
* Custom validation patterns.
*
* @parameter
* @since 1.3
*/
private CustomPattern[] customPatterns = new CustomPattern[] {};
/**
* Directory containing dictionaries for SpellCheck validation.
*
* Defaults to the value of parameter propertyDir.
*
* @parameter
* @since 1.4
*/
private File dictionaryDir;
/**
* Flag allowing to skip plugin exceution for a particular build.
*
* This makes the plugin more controllable from profiles.
*
* @parameter expression="${l10n.skip}"
* @since 1.4
*/
private boolean skip;
/**
* Base directory where all reports are written to.
*
* @parameter expression="${project.build.directory}/l10n-reports"
* @since 1.5
*/
private File reportsDir;
private L10nValidator directoryValidator;
private L10nValidatorLogger logger;
/**
* Default constructor for plugin execution
*
* Can't initialize validators here, because Mojo configuration is injected after constructor has returned
*
*/
public ValidateMojo() {
}
/**
* Initialize from another configured Mojo
*
*/
public ValidateMojo(L10nValidationConfiguration configuration) {
// Propagate configuration
setPropertyDir(configuration.getPropertyDir());
setHtmlKeys(configuration.getHtmlKeys());
setXhtmlSchema(configuration.getXhtmlSchema());
setJsKeys(configuration.getJsKeys());
setJsDoubleQuoted(configuration.getJsDoubleQuoted());
setTextKeys(configuration.getTextKeys());
setUrlKeys(configuration.getUrlKeys());
setCustomPatterns(configuration.getCustomPatterns());
setExcludedKeys(configuration.getExcludedKeys());
setDictionaryDir(configuration.getDictionaryDir());
setReportsDir(configuration.getReportsDir());
}
/**
* Initialize validators from plugin configuration
*
* TODO Should use a DI container to do the wirering
*
*/
protected void initialize() {
logger = new L10nValidatorLogger(getLog());
getLog().info("Initializing l10n validators...");
if (dictionaryDir == null) {
// Default to propertyDir
dictionaryDir = propertyDir;
}
L10nValidator spellCheckValidator = new SpellCheckValidator(logger, dictionaryDir);
L10nValidator htmlValidator;
if (xhtmlSchema != null) {
htmlValidator = new HtmlValidator(xhtmlSchema, logger, spellCheckValidator, htmlKeys);
} else {
htmlValidator = new HtmlValidator(logger, spellCheckValidator, htmlKeys);
}
L10nValidator jsValidator = new JsValidator(jsDoubleQuoted, htmlValidator, logger, jsKeys);
L10nValidator urlValidator = new UrlValidator(logger, urlKeys);
L10nValidator plainTextValidator = new PlainTextValidator(logger, spellCheckValidator, textKeys);
L10nValidator defaultValidator = new DefaultValidator(logger, htmlKeys, urlKeys);
L10nValidator parametricMessageValidator = new ParametricMessageValidator(logger);
L10nValidator trailingWhitespaceValidator = new TrailingWhitespaceValidator(logger);
L10nValidator missingTranslationValidator = new MissingTranslationValidator(logger);
L10nValidator parametricCoherenceValidator = new ParametricCoherenceValidator(logger);
L10nValidator identicalTranslationValidator = new IdenticalTranslationValidator(logger);
L10nValidator htmlTagCoherenceValidator = new HtmlTagCoherenceValidator(logger, htmlKeys);
// L10nValidator duplicationValidator = new DuplicationValidator(logger);
L10nValidator[] patternValidators = null;
if (customPatterns != null) { // Initialize custom pattern validators
patternValidators = new PatternValidator[customPatterns.length];
for (int i = 0; i < customPatterns.length; i++) {
CustomPattern pattern = customPatterns[i];
patternValidators[i] = new PatternValidator(logger, pattern);
}
}
PropertyValidator propertyValidator = new PropertyValidator(logger, excludedKeys);
propertyValidator.setPatternValidators(patternValidators);
propertyValidator.setDefaultValidator(defaultValidator);
propertyValidator.setHtmlValidator(htmlValidator);
propertyValidator.setJsValidator(jsValidator);
propertyValidator.setParametricMessageValidator(parametricMessageValidator);
propertyValidator.setPlainTextValidator(plainTextValidator);
propertyValidator.setTrailingWhitespaceValidator(trailingWhitespaceValidator);
propertyValidator.setUrlValidator(urlValidator);
PropertyFamilyValidator propertyFamilyValidator = new PropertyFamilyValidator(logger, excludedKeys);
propertyFamilyValidator.setHtmlTagCoherenceValidator(htmlTagCoherenceValidator);
propertyFamilyValidator.setIdenticalTranslationValidator(identicalTranslationValidator);
propertyFamilyValidator.setMissingTranslationValidator(missingTranslationValidator);
propertyFamilyValidator.setParametricCoherenceValidator(parametricCoherenceValidator);
propertyFamilyValidator.setPropertyValidator(propertyValidator);
PropertiesFamilyValidator propertiesFamilyValidator = new PropertiesFamilyValidator(logger, reportsDir, propertyFamilyValidator);
directoryValidator = new DirectoryValidator(logger, propertiesFamilyValidator);
}
/**
* Plugin entry point for validate goal.
*
* @throws MojoExecutionException
* in case of unexpected exception during plugin execution
* @throws MojoFailureException
* in case validation detected errors and ignoreFailure is false
*/
public void execute() throws MojoExecutionException, MojoFailureException {
if (!skip) {
initialize();
executeInternal();
} else {
getLog().info("Skipping plugin execution, as per configuration.");
}
}
/**
* Plugin entry point for unit testing to allow re-use of a single initialized Mojo instance, for perf reasons.
*
* @throws MojoExecutionException
* in case of unexpected exception during plugin execution
* @throws MojoFailureException
* in case validation detected errors and ignoreFailure is false
*/
protected void executeInternal() throws MojoExecutionException, MojoFailureException {
List reportItems = new ArrayList();
int nbErrors = validate(propertyDir, reportItems);
if (nbErrors > 0) {
if (ignoreFailure) {
getLog().error("Validation has failed with " + nbErrors + " errors.");
getLog().info("Ignoring failure as ignoreFailure is true.");
} else {
throw new MojoFailureException("Validation has failed with " + nbErrors + " errors.");
}
} else {
getLog().info("Validation was successful.");
}
}
/**
* Validation logic entry point used both by {@link ValidateMojo} and {@link ReportMojo}
*
* @param directory
* the folder containing .properties files to validate
* @param reportItems
* list to update with validation errors/warn/info items
* @return number of validation errors
* @throws MojoExecutionException
*/
protected int validate(File directory, List reportItems) throws MojoExecutionException {
int nbErrors = 0;
try {
nbErrors = directoryValidator.validate(directory, reportItems);
} catch (L10nValidationException e) {
throw new MojoExecutionException("An unexpected exception has occurred while validating properties under directory " + propertyDir.getAbsolutePath(), e);
}
return nbErrors;
}
public void setDirectoryValidator(L10nValidator directoryValidator) {
this.directoryValidator = directoryValidator;
}
public void setPropertyDir(File propertyDir) {
this.propertyDir = propertyDir;
}
public void setExcludedKeys(String[] excludedKeys) {
this.excludedKeys = excludedKeys;
}
public void setIgnoreFailure(boolean ignoreFailure) {
this.ignoreFailure = ignoreFailure;
}
public void setJsKeys(String[] jsKeys) {
this.jsKeys = jsKeys;
}
public void setUrlKeys(String[] urlKeys) {
this.urlKeys = urlKeys;
}
public void setHtmlKeys(String[] htmlKeys) {
this.htmlKeys = htmlKeys;
}
public void setTextKeys(String[] textKeys) {
this.textKeys = textKeys;
}
public void setCustomPatterns(CustomPattern[] customPatterns) {
this.customPatterns = customPatterns;
}
public void setJsDoubleQuoted(boolean jsDoubleQuoted) {
this.jsDoubleQuoted = jsDoubleQuoted;
}
public void setXhtmlSchema(File xhtmlSchema) {
this.xhtmlSchema = xhtmlSchema;
}
public void setDictionaryDir(File dictionaryDir) {
this.dictionaryDir = dictionaryDir;
}
public File getPropertyDir() {
return propertyDir;
}
public String[] getExcludedKeys() {
return excludedKeys;
}
public boolean getIgnoreFailure() {
return ignoreFailure;
}
public String[] getJsKeys() {
return jsKeys;
}
public boolean getJsDoubleQuoted() {
return jsDoubleQuoted;
}
public String[] getUrlKeys() {
return urlKeys;
}
public String[] getHtmlKeys() {
return htmlKeys;
}
public File getXhtmlSchema() {
return xhtmlSchema;
}
public String[] getTextKeys() {
return textKeys;
}
public CustomPattern[] getCustomPatterns() {
return customPatterns;
}
public File getDictionaryDir() {
return dictionaryDir;
}
public void setSkip(boolean skip) {
this.skip = skip;
}
public boolean getSkip() {
return skip;
}
public File getReportsDir() {
return reportsDir;
}
public void setReportsDir(File reportsDir) {
this.reportsDir = reportsDir;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy