![JAR search and dependency download from the Maven repository](/logo.png)
com.qulice.xml.XmlValidator Maven / Gradle / Ivy
/**
* Copyright (c) 2011-2016, Qulice.com
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met: 1) Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer. 2) Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution. 3) Neither the name of the Qulice.com nor
* the names of its contributors may be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
* NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.qulice.xml;
import com.jcabi.log.Logger;
import com.jcabi.xml.StrictXML;
import com.jcabi.xml.XML;
import com.jcabi.xml.XMLDocument;
import com.qulice.spi.Environment;
import com.qulice.spi.ValidationException;
import com.qulice.spi.Validator;
import difflib.Delta;
import difflib.DiffUtils;
import difflib.Patch;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Pattern;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.xml.sax.SAXException;
/**
* Validates XML files for formatting.
*
* @author Yegor Bugayenko ([email protected])
* @version $Id$
* @since 0.3
*/
@SuppressWarnings({ "PMD.AvoidInstantiatingObjectsInLoops",
"PMD.ExceptionAsFlowControl" })
public final class XmlValidator implements Validator {
/**
* New line escaped, for use in regex.
*/
private static final String ESCAPED_EOL = "\\n";
/**
* Pattern for lines starting with XML attributes.
*/
private static final Pattern ATTRS_PATTERN = Pattern.compile("^ *[^ <].*");
/**
* Should XML format be checked.
*/
private final boolean format;
/**
* Constructor.
*/
public XmlValidator() {
this(true);
}
/**
* Constructor with XML formatting option.
* @param formatting Should XML formatting be checked.
*/
public XmlValidator(final boolean formatting) {
this.format = formatting;
}
@Override
public void validate(final Environment env) throws ValidationException {
try {
for (final File file : env.files("*.xml")) {
final String name = file.getAbsolutePath().substring(
env.basedir().toString().length()
);
if (env.exclude("xml", name)) {
Logger.info(this, "%s: skipped", name);
continue;
}
Logger.info(this, "%s: to be validated", name);
final XML document = new XMLDocument(file);
try {
new StrictXML(document);
} catch (final IllegalStateException ex) {
if (ex.getCause() != null
&& ex.getCause() instanceof SAXException) {
Logger.warn(
// @checkstyle LineLength (1 line)
this, "Failed to validate file %s against schema(s). Cause: %s",
name,
ex.toString()
);
} else {
throw new IllegalStateException(ex);
}
} catch (final IllegalArgumentException ex) {
throw new ValidationException(ex);
}
if (this.format) {
XmlValidator.formatting(
file.toString(), FileUtils.readFileToString(file)
);
}
}
} catch (final IOException ex) {
throw new IllegalStateException(ex);
}
}
@Override
public String name() {
return "XML";
}
/**
* Check formatting of given XML.
* @param name Name of XML file.
* @param before XML to check.
* @throws ValidationException In case of validation error.
*/
private static void formatting(final String name, final String before)
throws ValidationException {
// @checkstyle MultipleStringLiterals (3 lines)
final String after = new Prettifier(before).prettify()
.replace("\r\n", "\n");
final String bnormalized = before.replace("\r\n", "\n");
final List blines =
Arrays.asList(bnormalized.split(XmlValidator.ESCAPED_EOL, -1));
final Patch filter = XmlValidator.filter(
DiffUtils.diff(
blines,
Arrays.asList(after.split(XmlValidator.ESCAPED_EOL, -1))
)
);
if (!filter.getDeltas().isEmpty()) {
final int context = 5;
throw new ValidationException(
// @checkstyle LineLength (1 line)
"The provided XML %s is not well formatted, it should look like this:\n%s\npatch:\n%s",
name, after,
StringUtils.join(
DiffUtils.generateUnifiedDiff(
"before", "after", blines,
filter, context
), "\n"
)
);
}
}
/**
* Remove unwanted deltas.
* @param diff Patch to filter.
* @return Patch with unwanted deltas removed.
* @todo #469:30min Remove the method below and find a way to format tags
* correctly in XML. Attributes should be indented by 4 spaces, just like
* XML tags, but in IT xml-violations there is a tag that our Prettifier
* want to be indented by 3 spaces which is wrong. Another problem is
* that in the parent tag, attributes are indented to match the first
* attribute, this is also wrong - all attributes on new line should be
* indented by 4 spaces.
*/
private static Patch filter(final Patch diff) {
final Patch patch = new Patch();
for (final Delta delta : diff.getDeltas()) {
final List> prev = delta.getOriginal().getLines();
if (
prev.size() != 1 || delta.getRevised().getLines().size() != 1
|| !XmlValidator.ATTRS_PATTERN
.matcher(prev.get(0).toString()).matches()
) {
patch.addDelta(delta);
}
}
return patch;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy