au.com.acegi.xmlformat.AbstractXmlPlugin Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of xml-format-maven-plugin Show documentation
Show all versions of xml-format-maven-plugin Show documentation
Automatically formats XML files in a project.
The newest version!
/*-
* #%L
* XML Format Maven Plugin
* %%
* Copyright (C) 2011 - 2024 Acegi Technology Pty Limited
* %%
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
* #L%
*/
package au.com.acegi.xmlformat;
import static java.util.Arrays.asList;
import static java.util.Arrays.copyOf;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Parameter;
import org.codehaus.plexus.util.DirectoryScanner;
import org.dom4j.DocumentException;
/**
* Common infrastructure for the various plugin goals.
*/
@SuppressWarnings("DesignForExtension")
public abstract class AbstractXmlPlugin extends AbstractMojo {
/**
* Quote character to use when writing attributes.
*/
@Parameter(property = "attributeQuoteChar", defaultValue = "\"")
@SuppressWarnings("PMD.ImmutableField")
private char attributeQuoteChar = '"';
/**
* The base directory of the project.
*/
@Parameter(defaultValue = ".", readonly = true, required = true, property = "project.basedir")
private File baseDirectory;
/**
* The encoding format.
*/
@Parameter(property = "encoding", defaultValue = "UTF-8")
@SuppressWarnings("PMD.ImmutableField")
private String encoding = "UTF-8";
/**
* A set of file patterns that allow you to exclude certain files/folders from
* the formatting. In addition to these exclusions, the project build directory
* (typically target
) is always excluded if skipTargetFolder is
* true.
*/
@Parameter(property = "excludes")
private String[] excludes;
/**
* Whether or not to expand empty elements to <tagName></tagName>.
*/
@Parameter(property = "expandEmptyElements", defaultValue = "false")
private boolean expandEmptyElements;
/**
* A set of file patterns that dictate which files should be included in the
* formatting with each file pattern being relative to the base directory.
*/
@Parameter(property = "includes")
private String[] includes;
/**
* Indicates the number of spaces to apply when indenting.
*/
@Parameter(property = "indentSize", defaultValue = "2")
private int indentSize;
/**
* Use tabs instead of spaces for indents. If set to true
,
* indentSize
will be ignored.
*/
@Parameter(property = "tabIndent", defaultValue = "false")
private boolean tabIndent;
/**
* Sets the line-ending of files after formatting. Valid values are:
*
* - "SYSTEM" - Use line endings of current system
* - "LF" - Use Unix and Mac style line endings
* - "CRLF" - Use DOS and Windows style line endings
* - "CR" - Use early Mac style line endings
*
* This property is only used if {@link #lineSeparator} has its default value.
* Do not set any value for {@link #lineSeparator}.
*/
@Parameter(property = "lineEnding", defaultValue = "LF")
@SuppressWarnings("PMD.ImmutableField")
private LineEnding lineEnding = LineEnding.LF;
/**
* New line separator.
*
* @deprecated Please do not set this value; use {@link #lineEnding} instead
*/
@Parameter(property = "lineSeparator", defaultValue = "\n")
@SuppressWarnings("PMD.ImmutableField")
@Deprecated
private String lineSeparator = "\n";
/**
* Whether or not to print new line after the XML declaration.
*/
@Parameter(property = "newLineAfterDeclaration", defaultValue = "false")
private boolean newLineAfterDeclaration;
/**
* Controls when to output a line.separator every so many tags in case of no
* lines and total text trimming.
*/
@Parameter(property = "newLineAfterNTags", defaultValue = "0")
private int newLineAfterNTags;
/**
* The default new line flag, set to do new lines only as in original document.
*/
@Parameter(property = "newlines", defaultValue = "true")
private boolean newlines;
/**
* Whether or not to output the encoding in the XML declaration.
*/
@Parameter(property = "omitEncoding", defaultValue = "false")
private boolean omitEncoding;
/**
* Pad string-element boundaries with whitespace.
*/
@Parameter(property = "padText", defaultValue = "false")
private boolean padText;
/**
* Skip XML formatting.
*/
@Parameter(property = "xml-format.skip", defaultValue = "false")
private boolean skip;
/**
* In addition to the exclusions, the project build directory (typically
* target
) is always excluded if true.
*/
@Parameter(property = "skipTargetFolder", defaultValue = "true")
private boolean skipTargetFolder = true;
/**
* Whether or not to suppress the XML declaration.
*/
@Parameter(property = "suppressDeclaration", defaultValue = "false")
private boolean suppressDeclaration;
/**
* The project target directory. This is always excluded from formatting.
*/
@Parameter(defaultValue = "${project.build.directory}", readonly = true, required = true)
private File targetDirectory;
/**
* Should we preserve whitespace or not in text nodes.
*/
@Parameter(property = "trimText", defaultValue = "true")
private boolean trimText;
/**
* Whether or not to use XHTML standard.
*/
@Parameter(property = "xhtml", defaultValue = "false")
private boolean xhtml;
/**
* Whether to keep blank lines. A maximum of one line is preserved between each
* tag.
*/
@Parameter(property = "keepBlankLines", defaultValue = "false")
private boolean keepBlankLines;
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
assert baseDirectory != null;
assert targetDirectory != null;
if (skip) {
getLog().info("[xml-format] Skipped");
return;
}
initializeIncludes();
initializeExcludes();
final XmlOutputFormat fmt = buildFormatter();
boolean success = true;
boolean neededFormatting = false;
for (final String inputName : find()) {
final File input = new File(baseDirectory, inputName);
try {
neededFormatting |= processFile(input, fmt);
} catch (final DocumentException | IOException ex) {
success = false;
getLog().error("[xml-format] Error for " + input, ex);
}
}
if (!success) {
throw new MojoFailureException("[xml-format] Failed)");
}
afterAllProcessed(neededFormatting);
}
/**
* Processes a single file found in the project.
*
* @param input the file to process
* @param fmt the formatting options
* @return true if the file required changes to match the formatting style
* @throws DocumentException if input XML could not be parsed
* @throws IOException if output XML stream could not be written
*/
protected abstract boolean processFile(File input, XmlOutputFormat fmt)
throws DocumentException, IOException;
/**
* Invoked after all files in the project have been processed.
*
* @param neededFormatting whether any processed file required changes to match
* the formatting style
* @throws MojoExecutionException if the build must be failed
*/
protected abstract void afterAllProcessed(boolean neededFormatting) throws MojoExecutionException;
void setBaseDirectory(final File baseDirectory) {
this.baseDirectory = baseDirectory;
}
void setExcludes(final String... excludes) {
this.excludes = excludes == null ? null : copyOf(excludes, excludes.length);
}
void setIncludes(final String... includes) {
this.includes = includes == null ? null : copyOf(includes, includes.length);
}
void setSkip(final boolean skip) {
this.skip = skip;
}
void setSkipTargetFolder(final boolean skipTargetFolder) {
this.skipTargetFolder = skipTargetFolder;
}
void setTargetDirectory(final File targetDirectory) {
this.targetDirectory = targetDirectory;
}
private XmlOutputFormat buildFormatter() {
final XmlOutputFormat fmt = new XmlOutputFormat();
fmt.setAttributeQuoteCharacter(attributeQuoteChar);
fmt.setEncoding(encoding);
fmt.setExpandEmptyElements(expandEmptyElements);
if (tabIndent) {
fmt.setIndent("\t");
} else {
fmt.setIndentSize(indentSize);
}
fmt.setLineSeparator(determineLineSeparator());
fmt.setNewLineAfterDeclaration(newLineAfterDeclaration);
fmt.setNewLineAfterNTags(newLineAfterNTags);
fmt.setNewlines(newlines);
fmt.setOmitEncoding(omitEncoding);
fmt.setPadText(padText);
fmt.setSuppressDeclaration(suppressDeclaration);
fmt.setTrimText(trimText);
fmt.setXHTML(xhtml);
fmt.setKeepBlankLines(keepBlankLines);
return fmt;
}
private String determineLineSeparator() {
return "\n".equals(lineSeparator) ? lineEnding.getChars() : lineSeparator;
}
private String[] find() {
final DirectoryScanner dirScanner = new DirectoryScanner();
dirScanner.setBasedir(baseDirectory);
dirScanner.setIncludes(includes);
final List exclude = new ArrayList<>(asList(excludes));
if (skipTargetFolder && baseDirectory.equals(targetDirectory.getParentFile())) {
exclude.add(targetDirectory.getName() + "/**");
}
final String[] excluded = new String[exclude.size()];
dirScanner.setExcludes(exclude.toArray(excluded));
dirScanner.scan();
return dirScanner.getIncludedFiles();
}
private void initializeExcludes() {
if (excludes == null || excludes.length == 0) {
excludes = new String[0];
}
}
private void initializeIncludes() {
if (includes == null || includes.length == 0) {
includes = new String[] {"**/*.xml"};
}
}
}