View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2024 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ///////////////////////////////////////////////////////////////////////////////////////////////
19  
20  package com.puppycrawl.tools.checkstyle.meta;
21  
22  import java.io.File;
23  import java.util.List;
24  import java.util.Locale;
25  import java.util.regex.Pattern;
26  
27  import javax.xml.XMLConstants;
28  import javax.xml.parsers.DocumentBuilder;
29  import javax.xml.parsers.DocumentBuilderFactory;
30  import javax.xml.parsers.ParserConfigurationException;
31  import javax.xml.transform.OutputKeys;
32  import javax.xml.transform.Transformer;
33  import javax.xml.transform.TransformerException;
34  import javax.xml.transform.TransformerFactory;
35  import javax.xml.transform.dom.DOMSource;
36  import javax.xml.transform.stream.StreamResult;
37  
38  import org.w3c.dom.Document;
39  import org.w3c.dom.Element;
40  import org.w3c.dom.Node;
41  
42  /**
43   * Class to write module details object into an XML file.
44   */
45  public final class XmlMetaWriter {
46  
47      /** Compiled pattern for {@code .} used for generating file paths from package names. */
48      private static final Pattern FILEPATH_CONVERSION = Pattern.compile("\\.");
49  
50      /** Name tag of metadata XML files. */
51      private static final String XML_TAG_NAME = "name";
52  
53      /** Description tag of metadata XML files. */
54      private static final String XML_TAG_DESCRIPTION = "description";
55  
56      /** Default(UNIX) file separator. */
57      private static final String DEFAULT_FILE_SEPARATOR = "/";
58  
59      /**
60       * Do no allow {@code XmlMetaWriter} instances to be created.
61       */
62      private XmlMetaWriter() {
63      }
64  
65      /**
66       * Helper function to write module details to XML file.
67       *
68       * @param moduleDetails module details
69       * @throws TransformerException if a transformer exception occurs
70       * @throws ParserConfigurationException if a parser configuration exception occurs
71       */
72      public static void write(ModuleDetails moduleDetails) throws TransformerException,
73              ParserConfigurationException {
74          final DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
75          dbFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
76          dbFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
77          final DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
78          final Document doc = dBuilder.newDocument();
79  
80          final Element rootElement = doc.createElement("checkstyle-metadata");
81          final Element rootChild = doc.createElement("module");
82          rootElement.appendChild(rootChild);
83  
84          doc.appendChild(rootElement);
85  
86          final Element checkModule = doc.createElement(moduleDetails.getModuleType().getLabel());
87          rootChild.appendChild(checkModule);
88  
89          checkModule.setAttribute(XML_TAG_NAME, moduleDetails.getName());
90          checkModule.setAttribute("fully-qualified-name",
91                  moduleDetails.getFullQualifiedName());
92          checkModule.setAttribute("parent", moduleDetails.getParent());
93  
94          final Element desc = doc.createElement(XML_TAG_DESCRIPTION);
95          final Node cdataDesc = doc.createCDATASection(moduleDetails.getDescription());
96          desc.appendChild(cdataDesc);
97          checkModule.appendChild(desc);
98          createPropertySection(moduleDetails, checkModule, doc);
99          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