001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2022 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.Locale; 024import java.util.regex.Pattern; 025 026import javax.xml.XMLConstants; 027import javax.xml.parsers.DocumentBuilder; 028import javax.xml.parsers.DocumentBuilderFactory; 029import javax.xml.parsers.ParserConfigurationException; 030import javax.xml.transform.OutputKeys; 031import javax.xml.transform.Transformer; 032import javax.xml.transform.TransformerException; 033import javax.xml.transform.TransformerFactory; 034import javax.xml.transform.dom.DOMSource; 035import javax.xml.transform.stream.StreamResult; 036 037import org.w3c.dom.Document; 038import org.w3c.dom.Element; 039import org.w3c.dom.Node; 040 041/** 042 * Class to write module details object into an XML file. 043 */ 044public final class XmlMetaWriter { 045 046 /** Compiled pattern for {@code .} used for generating file paths from package names. */ 047 private static final Pattern FILEPATH_CONVERSION = Pattern.compile("\\."); 048 049 /** Name tag of metadata XML files. */ 050 private static final String XML_TAG_NAME = "name"; 051 052 /** Description tag of metadata XML files. */ 053 private static final String XML_TAG_DESCRIPTION = "description"; 054 055 /** Default(UNIX) file separator. */ 056 private static final String DEFAULT_FILE_SEPARATOR = "/"; 057 058 /** 059 * Do no allow {@code XmlMetaWriter} instances to be created. 060 */ 061 private XmlMetaWriter() { 062 } 063 064 /** 065 * Helper function to write module details to XML file. 066 * 067 * @param moduleDetails module details 068 * @throws TransformerException if a transformer exception occurs 069 * @throws ParserConfigurationException if a parser configuration exception occurs 070 */ 071 public static void write(ModuleDetails moduleDetails) throws TransformerException, 072 ParserConfigurationException { 073 final DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); 074 dbFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); 075 dbFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); 076 final DocumentBuilder dBuilder = dbFactory.newDocumentBuilder(); 077 final Document doc = dBuilder.newDocument(); 078 079 final Element rootElement = doc.createElement("checkstyle-metadata"); 080 final Element rootChild = doc.createElement("module"); 081 rootElement.appendChild(rootChild); 082 083 doc.appendChild(rootElement); 084 085 final Element checkModule = doc.createElement(moduleDetails.getModuleType().getLabel()); 086 rootChild.appendChild(checkModule); 087 088 checkModule.setAttribute(XML_TAG_NAME, moduleDetails.getName()); 089 checkModule.setAttribute("fully-qualified-name", 090 moduleDetails.getFullQualifiedName()); 091 checkModule.setAttribute("parent", moduleDetails.getParent()); 092 093 final Element desc = doc.createElement(XML_TAG_DESCRIPTION); 094 final Node cdataDesc = doc.createCDATASection(moduleDetails.getDescription()); 095 desc.appendChild(cdataDesc); 096 checkModule.appendChild(desc); 097 createPropertySection(moduleDetails, checkModule, doc); 098 if (!moduleDetails.getViolationMessageKeys().isEmpty()) { 099 final Element messageKeys = doc.createElement("message-keys"); 100 for (String msg : moduleDetails.getViolationMessageKeys()) { 101 final Element messageKey = doc.createElement("message-key"); 102 messageKey.setAttribute("key", msg); 103 messageKeys.appendChild(messageKey); 104 } 105 checkModule.appendChild(messageKeys); 106 } 107 108 writeToFile(doc, moduleDetails); 109 } 110 111 /** 112 * Create the property section of the module detail object. 113 * 114 * @param moduleDetails module details 115 * @param checkModule root doc element 116 * @param doc document object 117 */ 118 private static void createPropertySection(ModuleDetails moduleDetails, Element checkModule, 119 Document doc) { 120 if (!moduleDetails.getProperties().isEmpty()) { 121 final Element properties = doc.createElement("properties"); 122 checkModule.appendChild(properties); 123 for (ModulePropertyDetails modulePropertyDetails : moduleDetails.getProperties()) { 124 final Element property = doc.createElement("property"); 125 properties.appendChild(property); 126 property.setAttribute(XML_TAG_NAME, modulePropertyDetails.getName()); 127 property.setAttribute("type", modulePropertyDetails.getType()); 128 if (modulePropertyDetails.getDefaultValue() != null) { 129 property.setAttribute("default-value", 130 modulePropertyDetails.getDefaultValue()); 131 } 132 if (modulePropertyDetails.getValidationType() != null) { 133 property.setAttribute("validation-type", 134 modulePropertyDetails.getValidationType()); 135 } 136 final Element propertyDesc = doc.createElement(XML_TAG_DESCRIPTION); 137 propertyDesc.appendChild(doc.createCDATASection( 138 modulePropertyDetails.getDescription())); 139 property.appendChild(propertyDesc); 140 } 141 } 142 } 143 144 /** 145 * Function to write the prepared document object into an XML file. 146 * 147 * @param document document updated with all module metadata 148 * @param moduleDetails the corresponding module details object 149 * @throws TransformerException if a transformer exception occurs 150 */ 151 private static void writeToFile(Document document, ModuleDetails moduleDetails) 152 throws TransformerException { 153 String fileSeparator = DEFAULT_FILE_SEPARATOR; 154 if (System.getProperty("os.name").toLowerCase(Locale.ENGLISH).contains("win")) { 155 fileSeparator = "\\" + fileSeparator; 156 } 157 final String modifiedPath; 158 final String xmlExtension = ".xml"; 159 final String rootOutputPath = System.getProperty("user.dir") + "/src/main/resources"; 160 if (moduleDetails.getFullQualifiedName().startsWith("com.puppycrawl.tools.checkstyle")) { 161 final String moduleFilePath = FILEPATH_CONVERSION 162 .matcher(moduleDetails.getFullQualifiedName()) 163 .replaceAll(fileSeparator); 164 final String checkstyleString = "checkstyle"; 165 final int indexOfCheckstyle = 166 moduleFilePath.indexOf(checkstyleString) + checkstyleString.length(); 167 168 modifiedPath = rootOutputPath + DEFAULT_FILE_SEPARATOR 169 + moduleFilePath.substring(0, indexOfCheckstyle) + "/meta/" 170 + moduleFilePath.substring(indexOfCheckstyle + 1) + xmlExtension; 171 } 172 else { 173 String moduleName = moduleDetails.getName(); 174 if (moduleDetails.getModuleType() == ModuleType.CHECK) { 175 moduleName += "Check"; 176 } 177 modifiedPath = rootOutputPath + "/checkstylemeta-" + moduleName + xmlExtension; 178 } 179 180 final TransformerFactory transformerFactory = TransformerFactory.newInstance(); 181 final Transformer transformer = transformerFactory.newTransformer(); 182 transformer.setOutputProperty(OutputKeys.INDENT, "yes"); 183 transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4"); 184 185 final DOMSource source = new DOMSource(document); 186 final StreamResult result = new StreamResult(new File(modifiedPath)); 187 transformer.transform(source, result); 188 189 } 190} 191