001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2014 Oliver Burn 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//////////////////////////////////////////////////////////////////////////////// 019package com.puppycrawl.tools.checkstyle; 020 021import com.puppycrawl.tools.checkstyle.api.Utils; 022 023import com.google.common.collect.Sets; 024import com.puppycrawl.tools.checkstyle.api.AbstractLoader; 025import com.puppycrawl.tools.checkstyle.api.CheckstyleException; 026import com.puppycrawl.tools.checkstyle.api.FastStack; 027import java.io.BufferedInputStream; 028import java.io.IOException; 029import java.io.InputStream; 030import java.net.URL; 031import java.util.Enumeration; 032import java.util.Set; 033import javax.xml.parsers.ParserConfigurationException; 034import org.xml.sax.Attributes; 035import org.xml.sax.InputSource; 036import org.xml.sax.SAXException; 037 038/** 039 * Loads a list of package names from a package name XML file. 040 * @author Rick Giles 041 * @version 4-Dec-2002 042 */ 043public final class PackageNamesLoader 044 extends AbstractLoader 045{ 046 /** the public ID for the configuration dtd */ 047 private static final String DTD_PUBLIC_ID = 048 "-//Puppy Crawl//DTD Package Names 1.0//EN"; 049 050 /** the resource for the configuration dtd */ 051 private static final String DTD_RESOURCE_NAME = 052 "com/puppycrawl/tools/checkstyle/packages_1_0.dtd"; 053 054 /** Name of default checkstyle package names resource file. 055 * The file must be in the classpath. 056 */ 057 private static final String CHECKSTYLE_PACKAGES = 058 "checkstyle_packages.xml"; 059 060 /** The temporary stack of package name parts */ 061 private final FastStack<String> packageStack = FastStack.newInstance(); 062 063 /** The fully qualified package names. */ 064 private final Set<String> packageNames = Sets.newLinkedHashSet(); 065 066 /** 067 * Creates a new <code>PackageNamesLoader</code> instance. 068 * @throws ParserConfigurationException if an error occurs 069 * @throws SAXException if an error occurs 070 */ 071 private PackageNamesLoader() 072 throws ParserConfigurationException, SAXException 073 { 074 super(DTD_PUBLIC_ID, DTD_RESOURCE_NAME); 075 } 076 077 /** 078 * Returns the set of fully qualified package names this 079 * this loader processed. 080 * @return the set of package names 081 */ 082 private Set<String> getPackageNames() 083 { 084 return packageNames; 085 } 086 087 @Override 088 public void startElement(String namespaceURI, 089 String localName, 090 String qName, 091 Attributes atts) 092 throws SAXException 093 { 094 if ("package".equals(qName)) { 095 //push package name 096 final String name = atts.getValue("name"); 097 if (name == null) { 098 throw new SAXException("missing package name"); 099 } 100 packageStack.push(name); 101 } 102 } 103 104 /** 105 * Creates a full package name from the package names on the stack. 106 * @return the full name of the current package. 107 */ 108 private String getPackageName() 109 { 110 final StringBuffer buf = new StringBuffer(); 111 for (String subPackage : packageStack) { 112 buf.append(subPackage); 113 if (!subPackage.endsWith(".")) { 114 buf.append("."); 115 } 116 } 117 return buf.toString(); 118 } 119 120 @Override 121 public void endElement(String namespaceURI, 122 String localName, 123 String qName) 124 { 125 if ("package".equals(qName)) { 126 127 packageNames.add(getPackageName()); 128 packageStack.pop(); 129 } 130 } 131 132 /** 133 * Returns the set of package names, compiled from all 134 * checkstyle_packages.xml files found on the given classloaders 135 * classpath. 136 * @param classLoader the class loader for loading the 137 * checkstyle_packages.xml files. 138 * @return the set of package names. 139 * @throws CheckstyleException if an error occurs. 140 */ 141 public static Set<String> getPackageNames(ClassLoader classLoader) 142 throws CheckstyleException 143 { 144 145 Enumeration<URL> packageFiles = null; 146 try { 147 packageFiles = classLoader.getResources(CHECKSTYLE_PACKAGES); 148 } 149 catch (IOException e) { 150 throw new CheckstyleException( 151 "unable to get package file resources", e); 152 } 153 154 //create the loader outside the loop to prevent PackageObjectFactory 155 //being created anew for each file 156 final PackageNamesLoader namesLoader = newPackageNamesLoader(); 157 158 while ((null != packageFiles) && packageFiles.hasMoreElements()) { 159 final URL packageFile = packageFiles.nextElement(); 160 InputStream stream = null; 161 162 try { 163 stream = new BufferedInputStream(packageFile.openStream()); 164 final InputSource source = new InputSource(stream); 165 loadPackageNamesSource(source, "default package names", 166 namesLoader); 167 } 168 catch (IOException e) { 169 throw new CheckstyleException( 170 "unable to open " + packageFile, e); 171 } 172 finally { 173 Utils.closeQuietly(stream); 174 } 175 } 176 return namesLoader.getPackageNames(); 177 } 178 179 /** 180 * Creates a PackageNamesLoader instance. 181 * @return the PackageNamesLoader 182 * @throws CheckstyleException if the creation failed 183 */ 184 private static PackageNamesLoader newPackageNamesLoader() 185 throws CheckstyleException 186 { 187 try { 188 return new PackageNamesLoader(); 189 } 190 catch (final ParserConfigurationException e) { 191 throw new CheckstyleException( 192 "unable to create PackageNamesLoader ", e); 193 } 194 catch (final SAXException e) { 195 throw new CheckstyleException( 196 "unable to create PackageNamesLoader - " 197 + e.getMessage(), e); 198 } 199 } 200 201 /** 202 * Returns the list of package names in a specified source. 203 * @param source the source for the list. 204 * @param sourceName the name of the source. 205 * @param nameLoader the PackageNamesLoader instance 206 * @throws CheckstyleException if an error occurs. 207 */ 208 private static void loadPackageNamesSource( 209 InputSource source, String sourceName, 210 PackageNamesLoader nameLoader) 211 throws CheckstyleException 212 { 213 try { 214 nameLoader.parseInputSource(source); 215 } 216 catch (final SAXException e) { 217 throw new CheckstyleException("unable to parse " 218 + sourceName + " - " + e.getMessage(), e); 219 } 220 catch (final IOException e) { 221 throw new CheckstyleException("unable to read " + sourceName, e); 222 } 223 } 224}