org.nuiton.i18n.plugin.parser.impl.AbstractParserXmlMojo Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of i18n-maven-plugin Show documentation
Show all versions of i18n-maven-plugin Show documentation
Maven plugin to deal with i18n stuff in a project, mainly base on the
nuiton-i18n api (but not only).
The newest version!
/*
* #%L
* I18n :: Maven Plugin
*
* $Id$
* $HeadURL$
* %%
* Copyright (C) 2007 - 2010 CodeLutin, Tony Chemit
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Lesser Public License for more details.
*
* You should have received a copy of the GNU General Lesser Public
* License along with this program. If not, see
* .
* #L%
*/
package org.nuiton.i18n.plugin.parser.impl;
import org.apache.commons.lang3.StringUtils;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.plugins.annotations.Parameter;
import org.nuiton.i18n.plugin.parser.AbstractFileParser;
import org.nuiton.i18n.plugin.parser.AbstractI18nParserMojo;
import org.nuiton.i18n.plugin.parser.FileParser;
import org.nuiton.i18n.plugin.parser.ParserException;
import org.nuiton.io.SortedProperties;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import javax.xml.namespace.NamespaceContext;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.regex.Pattern;
/**
* Abstract xml parser mojo.
*
* @author Tony Chemit - [email protected]
* @author Julien Ruchaud - [email protected]
* @since 2.0
*/
public abstract class AbstractParserXmlMojo extends AbstractI18nParserMojo {
/** Taille du buffer pour les lectures/écritures */
protected static final int BUFFER_SIZE = 8 * 1024;
/** available rules to detect i18n keys in xml files. */
protected String rules;
/** XPath factory used to detect i18n keys in xml files. */
protected XPathFactory factory;
/** Document builder factory to load xml files. */
protected DocumentBuilderFactory documentBuilderFactory;
/**
* Extra rules files to use for detecting i18n keys in xml validation files.
*
* Note : If the {@code coreRulesFile} is not defined, then you must
* fill this parameter.
*
* @since 2.0
*/
@Parameter
protected String[] userRulesFiles;
/**
* Defines namespaces mapping if needed.
*
* Example :
*
* <namespaces>
* <s>http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd</s>
* </namespaces>
*
*
* @since 2.0
*/
@Parameter
protected Map namespaces;
protected abstract String getCoreRuleFile();
protected abstract XmlFileParser newXmlFileParser(XPath xpath,
DocumentBuilder builder);
/**
* Obtain the list of files which define rules to apply.
*
* @return the list of path to rule files to used.
*/
protected List getRulefiles() {
List result = new ArrayList();
// load optional core rule file
String s = getCoreRuleFile();
if (!StringUtils.isEmpty(s)) {
result.add(s);
}
// load user rile files
String[] userFiles = getUserRuleFiles();
if (userFiles != null) {
for (String f : userFiles) {
if (!StringUtils.isEmpty(f)) {
result.add(f);
}
}
}
return result;
}
@Override
public void init() throws Exception {
super.init();
factory = XPathFactory.newInstance();
List files = getRulefiles();
if (files.isEmpty()) {
// no rules file defined
throw new MojoFailureException("No rules files defined, fill the coreRulesFile or userRulesFiles parameters");
}
rules = loadRules(files);
documentBuilderFactory = DocumentBuilderFactory.newInstance();
documentBuilderFactory.setNamespaceAware(true);
documentBuilderFactory.setValidating(false);
documentBuilderFactory.setCoalescing(false);
documentBuilderFactory.setIgnoringComments(true);
}
public final String[] getUserRuleFiles() {
return userRulesFiles;
}
@Override
public final FileParser newFileParser(Pattern acceptPattern) {
DocumentBuilder builder;
try {
// never forget this!
builder = documentBuilderFactory.newDocumentBuilder();
} catch (ParserConfigurationException ex) {
throw new IllegalStateException(
"could not load DocumentBuilder for reason " +
ex.getMessage(), ex);
}
XPath xpath = factory.newXPath();
return newXmlFileParser(xpath, builder);
}
protected String loadRules(List files) {
List rules = new ArrayList();
boolean verbose = !isSilent() && isVerbose();
for (String file : files) {
getLog().info("Load rules file " + file);
try {
String fileRules = loadRulesFile(file);
rules.add(fileRules);
if (verbose) {
getLog().info("Rules for file [" + file + "] : " + fileRules);
}
} catch (IOException e) {
throw new ParserException(e);
}
}
String result = StringUtils.join(rules, " | ");
if (verbose) {
getLog().info("Loaded rules : " + result);
}
return result;
}
protected String loadRulesFile(String fileRules) throws IOException {
File f = new File(fileRules);
InputStream inputStream;
if (f.exists()) {
// load from a file
try {
inputStream = new FileInputStream(f);
} catch (FileNotFoundException e) {
throw new ParserException(e);
}
} else {
// load from classpath
ClassLoader classLoader = getClass().getClassLoader();
inputStream = classLoader.getResourceAsStream(fileRules);
}
if (inputStream == null) {
throw new ParserException(
"could not found file of rules : " + fileRules);
}
inputStream = new BufferedInputStream(inputStream);
try {
// Lecture
String readInputStream;
readInputStream = readInputStream(inputStream);
return readInputStream;
} catch (IOException e) {
throw new ParserException(e);
} finally {
inputStream.close();
}
}
/**
* Permet la lecture d'un InputStream et Suppressions.
*
* @param in le flux entrant
* @return le contenu du flux
* @throws IOException si problème de lecture dans flux entrant
*/
private String readInputStream(InputStream in) throws IOException {
StringBuilder sb = new StringBuilder();
BufferedReader reader = new BufferedReader(
new InputStreamReader(in, encoding));
try {
String line;
while ((line = reader.readLine()) != null) {
line = line.trim();
if (line.startsWith("#")) {
// comment line
continue;
}
sb.append(" ").append(line);
}
} finally {
reader.close();
}
// Suppression des espaces au début et à la fin
String txt = sb.toString().trim();
// contruction du xpath avec des ou
txt = txt.replaceAll("\\s+", " | ");
// suppression des ou de début et de fin
txt = txt.replaceAll("(^ \\| )|( \\| $)", "");
return txt;
}
public static abstract class XmlFileParser extends AbstractFileParser {
private final XPath xpath;
private final String rules;
private final DocumentBuilder builder;
private final Map namespaces;
private final Map namespaces2;
private final boolean verbose;
/**
* Fonction d'extraction de la chaine
*
* @param i18nString le clef i18n
* @return la chaine
*/
public abstract String extract(String i18nString);
public XmlFileParser(Log log,
String encoding,
SortedProperties oldParser,
Pattern acceptKeyPattern,
boolean showTouchedFiles,
String rules,
XPath xpath,
DocumentBuilder builder,
Map namespaces,
boolean verbose) {
super(log, encoding, oldParser, acceptKeyPattern, showTouchedFiles);
this.xpath = xpath;
this.rules = rules;
this.builder = builder;
this.verbose = verbose;
if (namespaces != null && !namespaces.isEmpty()) {
this.namespaces = namespaces;
namespaces2 = new TreeMap();
for (Map.Entry e : namespaces.entrySet()) {
namespaces2.put(e.getValue(), e.getKey());
}
NamespaceContext ctx = new NamespaceContext() {
public String getNamespaceURI(String prefix) {
return XmlFileParser.this.namespaces.get(prefix);
}
@Override
public String getPrefix(String namespaceURI) {
return namespaces2.get(namespaceURI);
}
@Override
public Iterator getPrefixes(String namespaceURI) {
return null;
}
};
xpath.setNamespaceContext(ctx);
} else {
namespaces2 = null;
this.namespaces = null;
}
}
/**
* To prepare the file (if any thing to be done before scanning it).
*
* @param file the incoming file
* @return the real file to process
* @throws IOException if any IO problem while preparing file
* @since 2.0
*/
@Override
public File prepareFile(File file) throws IOException {
// by default, do nothing
return file;
}
@Override
public void parseFile(File file) throws IOException {
File fileToProcess = prepareFile(file);
if (fileToProcess == null) {
// this case means not to treate the file
if (verbose) {
getLog().info("Skip file " + file);
}
return;
}
try {
// Recherche des clés à partir d'un xpath
if (verbose) {
getLog().info("Start parsing file " + fileToProcess);
}
Document doc = fileToDocument(fileToProcess);
XPathExpression expression = xpath.compile(rules);
NodeList list = (NodeList)
expression.evaluate(doc, XPathConstants.NODESET);
if (verbose) {
getLog().info("Detected nodes [" + file + "] : " + list.getLength());
}
for (int index = 0; index < list.getLength(); index++) {
Node node = list.item(index);
parseLine(fileToProcess, node.getTextContent());
}
} catch (Exception e) {
throw new ParserException(e);
}
}
@Override
public void parseLine(File file, String key) throws IOException {
key = extract(key);
if (key != null) {
// setTouched(true);
registerKey(key);
}
}
protected Document fileToDocument(File fileToProcess) throws SAXException, IOException {
return builder.parse(fileToProcess.getAbsolutePath());
}
}
}