001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2026 the original author or authors. 004// 005// This library is free software; you can redistribute it and/or 006// modify it under the terms of the GNU Lesser General Public 007// License as published by the Free Software Foundation; either 008// version 2.1 of the License, or (at your option) any later version. 009// 010// This library is distributed in the hope that it will be useful, 011// but WITHOUT ANY WARRANTY; without even the implied warranty of 012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013// Lesser General Public License for more details. 014// 015// You should have received a copy of the GNU Lesser General Public 016// License along with this library; if not, write to the Free Software 017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 018/////////////////////////////////////////////////////////////////////////////////////////////// 019 020package com.puppycrawl.tools.checkstyle.meta; 021 022import java.nio.file.Path; 023import java.util.List; 024import java.util.Locale; 025import java.util.regex.Pattern; 026 027import javax.xml.XMLConstants; 028import javax.xml.parsers.DocumentBuilder; 029import javax.xml.parsers.DocumentBuilderFactory; 030import javax.xml.parsers.ParserConfigurationException; 031import javax.xml.transform.OutputKeys; 032import javax.xml.transform.Transformer; 033import javax.xml.transform.TransformerException; 034import javax.xml.transform.TransformerFactory; 035import javax.xml.transform.dom.DOMSource; 036import javax.xml.transform.stream.StreamResult; 037 038import org.w3c.dom.Document; 039import org.w3c.dom.Element; 040import org.w3c.dom.Node; 041 042/** 043 * Class to write module details object into an XML file. 044 */ 045public final class XmlMetaWriter { 046 047 /** Compiled pattern for {@code .} used for generating file paths from package names. */ 048 private static final Pattern FILEPATH_CONVERSION = Pattern.compile("\\."); 049 050 /** Name tag of metadata XML files. */ 051 private static final String XML_TAG_NAME = "name"; 052 053 /** Description tag of metadata XML files. */ 054 private static final String XML_TAG_DESCRIPTION = "description"; 055 056 /** Default(UNIX) file separator. */ 057 private static final String DEFAULT_FILE_SEPARATOR = "/"; 058 059 /** 060 * Do no allow {@code XmlMetaWriter} instances to be created. 061 */ 062 private XmlMetaWriter() { 063 } 064 065 /** 066 * Helper function to write module details to XML file. 067 * 068 * @param moduleDetails module details 069 * @throws TransformerException if a transformer exception occurs 070 * @throws ParserConfigurationException if a parser configuration exception occurs 071 */ 072 public static void write(ModuleDetails moduleDetails) throws TransformerException, 073 ParserConfigurationException { 074 final DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); 075 dbFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); 076 dbFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); 077 final DocumentBuilder dBuilder = dbFactory.newDocumentBuilder(); 078 final Document doc = dBuilder.newDocument(); 079 080 final Element rootElement = doc.createElement("checkstyle-metadata"); 081 final Element rootChild = doc.createElement("module"); 082 rootElement.appendChild(rootChild); 083 084 doc.appendChild(rootElement); 085 086 final Element checkModule = doc.createElement(moduleDetails.getModuleType().getLabel()); 087 rootChild.appendChild(checkModule); 088 089 checkModule.setAttribute(XML_TAG_NAME, moduleDetails.getName()); 090 checkModule.setAttribute("fully-qualified-name", 091 moduleDetails.getFullQualifiedName()); 092 checkModule.setAttribute("parent", moduleDetails.getParent()); 093 094 final Element desc = doc.createElement(XML_TAG_DESCRIPTION); 095 final Node cdataDesc = doc.createCDATASection(moduleDetails.getDescription()); 096 desc.appendChild(cdataDesc); 097 checkModule.appendChild(desc); 098 createPropertySection(moduleDetails, checkModule, doc); 099 final List<String> violationMessageKeys = moduleDetails.getViolationMessageKeys(); 100 if (!violationMessageKeys.isEmpty()) { 101 final Element messageKeys = doc.createElement("message-keys"); 102 for (String msg : violationMessageKeys) { 103 final Element messageKey = doc.createElement("message-key"); 104 messageKey.setAttribute("key", msg); 105 messageKeys.appendChild(messageKey); 106 } 107 checkModule.appendChild(messageKeys); 108 } 109 110 writeToFile(doc, moduleDetails); 111 } 112 113 /** 114 * Create the property section of the module detail object. 115 * 116 * @param moduleDetails module details 117 * @param checkModule root doc element 118 * @param doc document object 119 */ 120 private static void createPropertySection(ModuleDetails moduleDetails, Element checkModule, 121 Document doc) { 122 final List<ModulePropertyDetails> moduleProperties = moduleDetails.getProperties(); 123 if (!moduleProperties.isEmpty()) { 124 final Element properties = doc.createElement("properties"); 125 checkModule.appendChild(properties); 126 for (ModulePropertyDetails modulePropertyDetails : moduleProperties) { 127 final Element property = doc.createElement("property"); 128 properties.appendChild(property); 129 property.setAttribute(XML_TAG_NAME, modulePropertyDetails.getName()); 130 property.setAttribute("type", modulePropertyDetails.getType()); 131 final String defaultValue = modulePropertyDetails.getDefaultValue(); 132 if (defaultValue != null && !"null".equals(defaultValue)) { 133 property.setAttribute("default-value", defaultValue); 134 } 135 final String validationType = modulePropertyDetails.getValidationType(); 136 if (validationType != null) { 137 property.setAttribute("validation-type", validationType); 138 } 139 final Element propertyDesc = doc.createElement(XML_TAG_DESCRIPTION); 140 propertyDesc.appendChild(doc.createCDATASection( 141 modulePropertyDetails.getDescription())); 142 property.appendChild(propertyDesc); 143 } 144 } 145 } 146 147 /** 148 * Function to write the prepared document object into an XML file. 149 * 150 * @param document document updated with all module metadata 151 * @param moduleDetails the corresponding module details object 152 * @throws TransformerException if a transformer exception occurs 153 */ 154 private static void writeToFile(Document document, ModuleDetails moduleDetails) 155 throws TransformerException { 156 String fileSeparator = DEFAULT_FILE_SEPARATOR; 157 if (System.getProperty("os.name").toLowerCase(Locale.ENGLISH).contains("win")) { 158 fileSeparator = "\\" + fileSeparator; 159 } 160 final String modifiedPath; 161 final String xmlExtension = ".xml"; 162 final String rootOutputPath = System.getProperty("user.dir") + "/src/main/resources"; 163 final String fullQualifiedName = moduleDetails.getFullQualifiedName(); 164 if (fullQualifiedName.startsWith("com.puppycrawl.tools.checkstyle")) { 165 final String moduleFilePath = FILEPATH_CONVERSION 166 .matcher(fullQualifiedName) 167 .replaceAll(fileSeparator); 168 final String checkstyleString = "checkstyle"; 169 final int indexOfCheckstyle = 170 moduleFilePath.indexOf(checkstyleString) + checkstyleString.length(); 171 172 modifiedPath = rootOutputPath + DEFAULT_FILE_SEPARATOR 173 + moduleFilePath.substring(0, indexOfCheckstyle) + "/meta/" 174 + moduleFilePath.substring(indexOfCheckstyle + 1) + xmlExtension; 175 } 176 else { 177 String moduleName = moduleDetails.getName(); 178 if (moduleDetails.getModuleType() == ModuleType.CHECK) { 179 moduleName += "Check"; 180 } 181 modifiedPath = rootOutputPath + "/checkstylemeta-" + moduleName + xmlExtension; 182 } 183 184 final TransformerFactory transformerFactory = TransformerFactory.newInstance(); 185 final Transformer transformer = transformerFactory.newTransformer(); 186 transformer.setOutputProperty(OutputKeys.INDENT, "yes"); 187 transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4"); 188 189 final DOMSource source = new DOMSource(document); 190 final StreamResult result = new StreamResult(Path.of(modifiedPath).toFile()); 191 transformer.transform(source, result); 192 193 } 194}