| //////////////////////////////////////////////////////////////////////////////// |
| // checkstyle: Checks Java source code for adherence to a set of rules. |
| // Copyright (C) 2001-2017 the original author or authors. |
| // |
| // This library is free software; you can redistribute it and/or |
| // modify it under the terms of the GNU Lesser General Public |
| // License as published by the Free Software Foundation; either |
| // version 2.1 of the License, or (at your option) any later version. |
| // |
| // This library is distributed in the hope that it will be useful, |
| // but WITHOUT ANY WARRANTY; without even the implied warranty of |
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| // Lesser General Public License for more details. |
| // |
| // You should have received a copy of the GNU Lesser General Public |
| // License along with this library; if not, write to the Free Software |
| // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| package com.puppycrawl.tools.checkstyle; |
| |
| import java.io.BufferedInputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.net.URL; |
| import java.util.ArrayDeque; |
| import java.util.Deque; |
| import java.util.Enumeration; |
| import java.util.Iterator; |
| import java.util.LinkedHashSet; |
| import java.util.Set; |
| |
| import javax.xml.parsers.ParserConfigurationException; |
| |
| import org.xml.sax.Attributes; |
| import org.xml.sax.InputSource; |
| import org.xml.sax.SAXException; |
| |
| import com.google.common.io.Closeables; |
| import com.puppycrawl.tools.checkstyle.api.CheckstyleException; |
| import com.puppycrawl.tools.checkstyle.utils.CommonUtils; |
| |
| /** |
| * Loads a list of package names from a package name XML file. |
| * @author Rick Giles |
| */ |
| public final class PackageNamesLoader |
| extends XmlLoader { |
| /** The public ID for the configuration dtd. */ |
| private static final String DTD_PUBLIC_ID = |
| "-//Puppy Crawl//DTD Package Names 1.0//EN"; |
| |
| /** The resource for the configuration dtd. */ |
| private static final String DTD_RESOURCE_NAME = |
| "com/puppycrawl/tools/checkstyle/packages_1_0.dtd"; |
| |
| /** Name of default checkstyle package names resource file. |
| * The file must be in the classpath. |
| */ |
| private static final String CHECKSTYLE_PACKAGES = |
| "checkstyle_packages.xml"; |
| |
| /** Qualified name for element 'package'. */ |
| private static final String PACKAGE_ELEMENT_NAME = "package"; |
| |
| /** The temporary stack of package name parts. */ |
| private final Deque<String> packageStack = new ArrayDeque<>(); |
| |
| /** The fully qualified package names. */ |
| private final Set<String> packageNames = new LinkedHashSet<>(); |
| |
| /** |
| * Creates a new {@code PackageNamesLoader} instance. |
| * @throws ParserConfigurationException if an error occurs |
| * @throws SAXException if an error occurs |
| */ |
| private PackageNamesLoader() |
| throws ParserConfigurationException, SAXException { |
| super(DTD_PUBLIC_ID, DTD_RESOURCE_NAME); |
| } |
| |
| @Override |
| public void startElement(String uri, |
| String localName, |
| String qName, |
| Attributes attributes) { |
| if (PACKAGE_ELEMENT_NAME.equals(qName)) { |
| //push package name, name is mandatory attribute with not empty value by DTD |
| final String name = attributes.getValue("name"); |
| packageStack.push(name); |
| } |
| } |
| |
| /** |
| * Creates a full package name from the package names on the stack. |
| * @return the full name of the current package. |
| */ |
| private String getPackageName() { |
| final StringBuilder buf = new StringBuilder(256); |
| final Iterator<String> iterator = packageStack.descendingIterator(); |
| while (iterator.hasNext()) { |
| final String subPackage = iterator.next(); |
| buf.append(subPackage); |
| if (!CommonUtils.endsWithChar(subPackage, '.') && iterator.hasNext()) { |
| buf.append('.'); |
| } |
| } |
| return buf.toString(); |
| } |
| |
| @Override |
| public void endElement(String uri, |
| String localName, |
| String qName) { |
| if (PACKAGE_ELEMENT_NAME.equals(qName)) { |
| |
| packageNames.add(getPackageName()); |
| packageStack.pop(); |
| } |
| } |
| |
| /** |
| * Returns the set of package names, compiled from all |
| * checkstyle_packages.xml files found on the given class loaders |
| * classpath. |
| * @param classLoader the class loader for loading the |
| * checkstyle_packages.xml files. |
| * @return the set of package names. |
| * @throws CheckstyleException if an error occurs. |
| */ |
| public static Set<String> getPackageNames(ClassLoader classLoader) |
| throws CheckstyleException { |
| |
| final Set<String> result; |
| try { |
| //create the loader outside the loop to prevent PackageObjectFactory |
| //being created anew for each file |
| final PackageNamesLoader namesLoader = new PackageNamesLoader(); |
| |
| final Enumeration<URL> packageFiles = classLoader.getResources(CHECKSTYLE_PACKAGES); |
| |
| while (packageFiles.hasMoreElements()) { |
| processFile(packageFiles.nextElement(), namesLoader); |
| } |
| |
| result = namesLoader.packageNames; |
| } |
| catch (IOException ex) { |
| throw new CheckstyleException("unable to get package file resources", ex); |
| } |
| catch (ParserConfigurationException | SAXException ex) { |
| throw new CheckstyleException("unable to open one of package files", ex); |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Reads the file provided and parses it with package names loader. |
| * @param packageFile file from package |
| * @param namesLoader package names loader |
| * @throws SAXException if an error while parsing occurs |
| * @throws CheckstyleException if unable to open file |
| */ |
| private static void processFile(URL packageFile, PackageNamesLoader namesLoader) |
| throws SAXException, CheckstyleException { |
| InputStream stream = null; |
| try { |
| stream = new BufferedInputStream(packageFile.openStream()); |
| final InputSource source = new InputSource(stream); |
| namesLoader.parseInputSource(source); |
| } |
| catch (IOException ex) { |
| throw new CheckstyleException("unable to open " + packageFile, ex); |
| } |
| finally { |
| Closeables.closeQuietly(stream); |
| } |
| } |
| } |