001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2024 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.io.File; 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 if (!moduleDetails.getViolationMessageKeys().isEmpty()) { 100 final Element messageKeys = doc.createElement("message-keys"); 101 for (String msg : moduleDetails.getViolationMessageKeys()) { 102 final Element messageKey = doc.createElement("message-key"); 103 messageKey.setAttribute("key", msg); 104 messageKeys.appendChild(messageKey); 105 } 106 checkModule.appendChild(messageKeys); 107 } 108 109 writeToFile(doc, moduleDetails); 110 } 111 112 /** 113 * Create the property section of the module detail object. 114 * 115 * @param moduleDetails module details 116 * @param checkModule root doc element 117 * @param doc document object 118 */ 119 private static void createPropertySection(ModuleDetails moduleDetails, Element checkModule, 120 Document doc) { 121 final List<ModulePropertyDetails> moduleProperties = moduleDetails.getProperties(); 122 if (!moduleProperties.isEmpty()) { 123 final Element properties = doc.createElement("properties"); 124 checkModule.appendChild(properties); 125 for (ModulePropertyDetails modulePropertyDetails : moduleProperties) { 126 final Element property = doc.createElement("property"); 127 properties.appendChild(property); 128 property.setAttribute(XML_TAG_NAME, modulePropertyDetails.getName()); 129 property.setAttribute("type", modulePropertyDetails.getType()); 130 final String defaultValue = modulePropertyDetails.getDefaultValue(); 131 if (defaultValue != null) { 132 property.setAttribute("default-value", defaultValue); 133 } 134 final String validationType = modulePropertyDetails.getValidationType(); 135 if (validationType != null) { 136 property.setAttribute("validation-type", validationType); 137 } 138 final Element propertyDesc = doc.createElement(XML_TAG_DESCRIPTION); 139 propertyDesc.appendChild(doc.createCDATASection( 140 modulePropertyDetails.getDescription())); 141 property.appendChild(propertyDesc); 142 } 143 } 144 } 145 146 /** 147 * Function to write the prepared document object into an XML file. 148 * 149 * @param document document updated with all module metadata 150 * @param moduleDetails the corresponding module details object 151 * @throws TransformerException if a transformer exception occurs 152 */ 153 private static void writeToFile(Document document, ModuleDetails moduleDetails) 154 throws TransformerException { 155 String fileSeparator = DEFAULT_FILE_SEPARATOR; 156 if (System.getProperty("os.name").toLowerCase(Locale.ENGLISH).contains("win")) { 157 fileSeparator = "\\" + fileSeparator; 158 } 159 final String modifiedPath; 160 final String xmlExtension = ".xml"; 161 final String rootOutputPath = System.getProperty("user.dir") + "/src/main/resources"; 162 if (moduleDetails.getFullQualifiedName().startsWith("com.puppycrawl.tools.checkstyle")) { 163 final String moduleFilePath = FILEPATH_CONVERSION 164 .matcher(moduleDetails.getFullQualifiedName()) 165 .replaceAll(fileSeparator); 166 final String checkstyleString = "checkstyle"; 167 final int indexOfCheckstyle = 168 moduleFilePath.indexOf(checkstyleString) + checkstyleString.length(); 169 170 modifiedPath = rootOutputPath + DEFAULT_FILE_SEPARATOR 171 + moduleFilePath.substring(0, indexOfCheckstyle) + "/meta/" 172 + moduleFilePath.substring(indexOfCheckstyle + 1) + xmlExtension; 173 } 174 else { 175 String moduleName = moduleDetails.getName(); 176 if (moduleDetails.getModuleType() == ModuleType.CHECK) { 177 moduleName += "Check"; 178 } 179 modifiedPath = rootOutputPath + "/checkstylemeta-" + moduleName + xmlExtension; 180 } 181 182 final TransformerFactory transformerFactory = TransformerFactory.newInstance(); 183 final Transformer transformer = transformerFactory.newTransformer(); 184 transformer.setOutputProperty(OutputKeys.INDENT, "yes"); 185 transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4"); 186 187 final DOMSource source = new DOMSource(document); 188 final StreamResult result = new StreamResult(new File(modifiedPath)); 189 transformer.transform(source, result); 190 191 } 192} 193