XmlMetaWriter.java
////////////////////////////////////////////////////////////////////////////////
// checkstyle: Checks Java source code for adherence to a set of rules.
// Copyright (C) 2001-2021 the original author or authors.
//
// This library 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 2.1 of the License, or (at your option) any later version.
//
// This library 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
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
////////////////////////////////////////////////////////////////////////////////
package com.puppycrawl.tools.checkstyle.meta;
import java.io.File;
import java.util.Locale;
import java.util.regex.Pattern;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
/**
* Class to write module details object into an XML file.
*/
public final class XmlMetaWriter {
/** Compiled pattern for {@code .} used for generating file paths from package names. */
private static final Pattern FILEPATH_CONVERSION = Pattern.compile("\\.");
/** Name tag of metadata XML files. */
private static final String XML_TAG_NAME = "name";
/** Description tag of metadata XML files. */
private static final String XML_TAG_DESCRIPTION = "description";
/** Default(UNIX) file separator. */
private static final String DEFAULT_FILE_SEPARATOR = "/";
/**
* Do no allow {@code XmlMetaWriter} instances to be created.
*/
private XmlMetaWriter() {
}
/**
* Helper function to write module details to XML file.
*
* @param moduleDetails module details
* @throws TransformerException if a transformer exception occurs
* @throws ParserConfigurationException if a parser configuration exception occurs
*/
public static void write(ModuleDetails moduleDetails) throws TransformerException,
ParserConfigurationException {
final DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
dbFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
dbFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
final DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
final Document doc = dBuilder.newDocument();
final Element rootElement = doc.createElement("checkstyle-metadata");
final Element rootChild = doc.createElement("module");
rootElement.appendChild(rootChild);
doc.appendChild(rootElement);
final Element checkModule = doc.createElement(moduleDetails.getModuleType().getLabel());
rootChild.appendChild(checkModule);
checkModule.setAttribute(XML_TAG_NAME, moduleDetails.getName());
checkModule.setAttribute("fully-qualified-name",
moduleDetails.getFullQualifiedName());
checkModule.setAttribute("parent", moduleDetails.getParent());
final Element desc = doc.createElement(XML_TAG_DESCRIPTION);
final Node cdataDesc = doc.createCDATASection(moduleDetails.getDescription());
desc.appendChild(cdataDesc);
checkModule.appendChild(desc);
createPropertySection(moduleDetails, checkModule, doc);
if (!moduleDetails.getViolationMessageKeys().isEmpty()) {
final Element messageKeys = doc.createElement("message-keys");
for (String msg : moduleDetails.getViolationMessageKeys()) {
final Element messageKey = doc.createElement("message-key");
messageKey.setAttribute("key", msg);
messageKeys.appendChild(messageKey);
}
checkModule.appendChild(messageKeys);
}
writeToFile(doc, moduleDetails);
}
/**
* Create the property section of the module detail object.
*
* @param moduleDetails module details
* @param checkModule root doc element
* @param doc document object
*/
private static void createPropertySection(ModuleDetails moduleDetails, Element checkModule,
Document doc) {
if (!moduleDetails.getProperties().isEmpty()) {
final Element properties = doc.createElement("properties");
checkModule.appendChild(properties);
for (ModulePropertyDetails modulePropertyDetails : moduleDetails.getProperties()) {
final Element property = doc.createElement("property");
properties.appendChild(property);
property.setAttribute(XML_TAG_NAME, modulePropertyDetails.getName());
property.setAttribute("type", modulePropertyDetails.getType());
if (modulePropertyDetails.getDefaultValue() != null) {
property.setAttribute("default-value",
modulePropertyDetails.getDefaultValue());
}
if (modulePropertyDetails.getValidationType() != null) {
property.setAttribute("validation-type",
modulePropertyDetails.getValidationType());
}
final Element propertyDesc = doc.createElement(XML_TAG_DESCRIPTION);
propertyDesc.appendChild(doc.createCDATASection(
modulePropertyDetails.getDescription()));
property.appendChild(propertyDesc);
}
}
}
/**
* Function to write the prepared document object into an XML file.
*
* @param document document updated with all module metadata
* @param moduleDetails the corresponding module details object
* @throws TransformerException if a transformer exception occurs
*/
private static void writeToFile(Document document, ModuleDetails moduleDetails)
throws TransformerException {
String fileSeparator = DEFAULT_FILE_SEPARATOR;
if (System.getProperty("os.name").toLowerCase(Locale.ENGLISH).contains("win")) {
fileSeparator = "\\" + fileSeparator;
}
final String modifiedPath;
final String xmlExtension = ".xml";
final String rootOutputPath = System.getProperty("user.dir") + "/src/main/resources";
if (moduleDetails.getFullQualifiedName().startsWith("com.puppycrawl.tools.checkstyle")) {
final String moduleFilePath = FILEPATH_CONVERSION
.matcher(moduleDetails.getFullQualifiedName())
.replaceAll(fileSeparator);
final String checkstyleString = "checkstyle";
final int indexOfCheckstyle =
moduleFilePath.indexOf(checkstyleString) + checkstyleString.length();
modifiedPath = rootOutputPath + DEFAULT_FILE_SEPARATOR
+ moduleFilePath.substring(0, indexOfCheckstyle) + "/meta/"
+ moduleFilePath.substring(indexOfCheckstyle + 1) + xmlExtension;
}
else {
String moduleName = moduleDetails.getName();
if (moduleDetails.getModuleType() == ModuleType.CHECK) {
moduleName += "Check";
}
modifiedPath = rootOutputPath + "/checkstylemeta-" + moduleName + xmlExtension;
}
if (!moduleDetails.getDescription().isEmpty()) {
final TransformerFactory transformerFactory = TransformerFactory.newInstance();
final Transformer transformer = transformerFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
final DOMSource source = new DOMSource(document);
final StreamResult result = new StreamResult(new File(modifiedPath));
transformer.transform(source, result);
}
}
}