001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2018 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; 021 022import java.io.BufferedInputStream; 023import java.io.IOException; 024import java.io.InputStream; 025import java.net.URL; 026import java.util.ArrayDeque; 027import java.util.Deque; 028import java.util.Enumeration; 029import java.util.Iterator; 030import java.util.LinkedHashSet; 031import java.util.Set; 032 033import javax.xml.parsers.ParserConfigurationException; 034 035import org.xml.sax.Attributes; 036import org.xml.sax.InputSource; 037import org.xml.sax.SAXException; 038 039import com.puppycrawl.tools.checkstyle.api.CheckstyleException; 040import com.puppycrawl.tools.checkstyle.utils.CommonUtils; 041 042/** 043 * Loads a list of package names from a package name XML file. 044 */ 045public final class PackageNamesLoader 046 extends XmlLoader { 047 048 /** The public ID for the configuration dtd. */ 049 private static final String DTD_PUBLIC_ID = 050 "-//Puppy Crawl//DTD Package Names 1.0//EN"; 051 052 /** The resource for the configuration dtd. */ 053 private static final String DTD_RESOURCE_NAME = 054 "com/puppycrawl/tools/checkstyle/packages_1_0.dtd"; 055 056 /** Name of default checkstyle package names resource file. 057 * The file must be in the classpath. 058 */ 059 private static final String CHECKSTYLE_PACKAGES = 060 "checkstyle_packages.xml"; 061 062 /** Qualified name for element 'package'. */ 063 private static final String PACKAGE_ELEMENT_NAME = "package"; 064 065 /** The temporary stack of package name parts. */ 066 private final Deque<String> packageStack = new ArrayDeque<>(); 067 068 /** The fully qualified package names. */ 069 private final Set<String> packageNames = new LinkedHashSet<>(); 070 071 /** 072 * Creates a new {@code PackageNamesLoader} instance. 073 * @throws ParserConfigurationException if an error occurs 074 * @throws SAXException if an error occurs 075 */ 076 private PackageNamesLoader() 077 throws ParserConfigurationException, SAXException { 078 super(DTD_PUBLIC_ID, DTD_RESOURCE_NAME); 079 } 080 081 @Override 082 public void startElement(String uri, 083 String localName, 084 String qName, 085 Attributes attributes) { 086 if (PACKAGE_ELEMENT_NAME.equals(qName)) { 087 //push package name, name is mandatory attribute with not empty value by DTD 088 final String name = attributes.getValue("name"); 089 packageStack.push(name); 090 } 091 } 092 093 /** 094 * Creates a full package name from the package names on the stack. 095 * @return the full name of the current package. 096 */ 097 private String getPackageName() { 098 final StringBuilder buf = new StringBuilder(256); 099 final Iterator<String> iterator = packageStack.descendingIterator(); 100 while (iterator.hasNext()) { 101 final String subPackage = iterator.next(); 102 buf.append(subPackage); 103 if (!CommonUtils.endsWithChar(subPackage, '.') && iterator.hasNext()) { 104 buf.append('.'); 105 } 106 } 107 return buf.toString(); 108 } 109 110 @Override 111 public void endElement(String uri, 112 String localName, 113 String qName) { 114 if (PACKAGE_ELEMENT_NAME.equals(qName)) { 115 packageNames.add(getPackageName()); 116 packageStack.pop(); 117 } 118 } 119 120 /** 121 * Returns the set of package names, compiled from all 122 * checkstyle_packages.xml files found on the given class loaders 123 * classpath. 124 * @param classLoader the class loader for loading the 125 * checkstyle_packages.xml files. 126 * @return the set of package names. 127 * @throws CheckstyleException if an error occurs. 128 */ 129 public static Set<String> getPackageNames(ClassLoader classLoader) 130 throws CheckstyleException { 131 final Set<String> result; 132 try { 133 //create the loader outside the loop to prevent PackageObjectFactory 134 //being created anew for each file 135 final PackageNamesLoader namesLoader = new PackageNamesLoader(); 136 137 final Enumeration<URL> packageFiles = classLoader.getResources(CHECKSTYLE_PACKAGES); 138 139 while (packageFiles.hasMoreElements()) { 140 processFile(packageFiles.nextElement(), namesLoader); 141 } 142 143 result = namesLoader.packageNames; 144 } 145 catch (IOException ex) { 146 throw new CheckstyleException("unable to get package file resources", ex); 147 } 148 catch (ParserConfigurationException | SAXException ex) { 149 throw new CheckstyleException("unable to open one of package files", ex); 150 } 151 152 return result; 153 } 154 155 /** 156 * Reads the file provided and parses it with package names loader. 157 * @param packageFile file from package 158 * @param namesLoader package names loader 159 * @throws SAXException if an error while parsing occurs 160 * @throws CheckstyleException if unable to open file 161 */ 162 private static void processFile(URL packageFile, PackageNamesLoader namesLoader) 163 throws SAXException, CheckstyleException { 164 try (InputStream stream = new BufferedInputStream(packageFile.openStream())) { 165 final InputSource source = new InputSource(stream); 166 namesLoader.parseInputSource(source); 167 } 168 catch (IOException ex) { 169 throw new CheckstyleException("unable to open " + packageFile, ex); 170 } 171 } 172 173}