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