View Javadoc
1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2014  Oliver Burn
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  package com.puppycrawl.tools.checkstyle;
20  
21  import com.puppycrawl.tools.checkstyle.api.Utils;
22  
23  import com.google.common.collect.Sets;
24  import com.puppycrawl.tools.checkstyle.api.AbstractLoader;
25  import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
26  import com.puppycrawl.tools.checkstyle.api.FastStack;
27  import java.io.BufferedInputStream;
28  import java.io.IOException;
29  import java.io.InputStream;
30  import java.net.URL;
31  import java.util.Enumeration;
32  import java.util.Set;
33  import javax.xml.parsers.ParserConfigurationException;
34  import org.xml.sax.Attributes;
35  import org.xml.sax.InputSource;
36  import org.xml.sax.SAXException;
37  
38  /**
39   * Loads a list of package names from a package name XML file.
40   * @author Rick Giles
41   * @version 4-Dec-2002
42   */
43  public final class PackageNamesLoader
44      extends AbstractLoader
45  {
46      /** the public ID for the configuration dtd */
47      private static final String DTD_PUBLIC_ID =
48          "-//Puppy Crawl//DTD Package Names 1.0//EN";
49  
50      /** the resource for the configuration dtd */
51      private static final String DTD_RESOURCE_NAME =
52          "com/puppycrawl/tools/checkstyle/packages_1_0.dtd";
53  
54      /** Name of default checkstyle package names resource file.
55       * The file must be in the classpath.
56       */
57      private static final String CHECKSTYLE_PACKAGES =
58          "checkstyle_packages.xml";
59  
60      /** The temporary stack of package name parts */
61      private final FastStack<String> packageStack = FastStack.newInstance();
62  
63      /** The fully qualified package names. */
64      private final Set<String> packageNames = Sets.newLinkedHashSet();
65  
66      /**
67       * Creates a new <code>PackageNamesLoader</code> instance.
68       * @throws ParserConfigurationException if an error occurs
69       * @throws SAXException if an error occurs
70       */
71      private PackageNamesLoader()
72          throws ParserConfigurationException, SAXException
73      {
74          super(DTD_PUBLIC_ID, DTD_RESOURCE_NAME);
75      }
76  
77      /**
78       * Returns the set of fully qualified package names this
79       * this loader processed.
80       * @return the set of package names
81       */
82      private Set<String> getPackageNames()
83      {
84          return packageNames;
85      }
86  
87      @Override
88      public void startElement(String namespaceURI,
89                               String localName,
90                               String qName,
91                               Attributes atts)
92          throws SAXException
93      {
94          if ("package".equals(qName)) {
95              //push package name
96              final String name = atts.getValue("name");
97              if (name == null) {
98                  throw new SAXException("missing package name");
99              }
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 }