View Javadoc
1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2015 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  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   */
42  public final class PackageNamesLoader
43      extends AbstractLoader
44  {
45      /** the public ID for the configuration dtd */
46      private static final String DTD_PUBLIC_ID =
47          "-//Puppy Crawl//DTD Package Names 1.0//EN";
48  
49      /** the resource for the configuration dtd */
50      private static final String DTD_RESOURCE_NAME =
51          "com/puppycrawl/tools/checkstyle/packages_1_0.dtd";
52  
53      /** Name of default checkstyle package names resource file.
54       * The file must be in the classpath.
55       */
56      private static final String CHECKSTYLE_PACKAGES =
57          "checkstyle_packages.xml";
58  
59      /** The temporary stack of package name parts */
60      private final FastStack<String> packageStack = FastStack.newInstance();
61  
62      /** The fully qualified package names. */
63      private final Set<String> packageNames = Sets.newLinkedHashSet();
64  
65      /**
66       * Creates a new <code>PackageNamesLoader</code> instance.
67       * @throws ParserConfigurationException if an error occurs
68       * @throws SAXException if an error occurs
69       */
70      private PackageNamesLoader()
71          throws ParserConfigurationException, SAXException
72      {
73          super(DTD_PUBLIC_ID, DTD_RESOURCE_NAME);
74      }
75  
76      /**
77       * Returns the set of fully qualified package names this
78       * this loader processed.
79       * @return the set of package names
80       */
81      private Set<String> getPackageNames()
82      {
83          return packageNames;
84      }
85  
86      @Override
87      public void startElement(String namespaceURI,
88                               String localName,
89                               String qName,
90                               Attributes atts)
91          throws SAXException
92      {
93          if ("package".equals(qName)) {
94              //push package name
95              final String name = atts.getValue("name");
96              if (name == null) {
97                  throw new SAXException("missing package name");
98              }
99              packageStack.push(name);
100         }
101     }
102 
103     /**
104      * Creates a full package name from the package names on the stack.
105      * @return the full name of the current package.
106      */
107     private String getPackageName()
108     {
109         final StringBuilder buf = new StringBuilder();
110         for (String subPackage : packageStack) {
111             buf.append(subPackage);
112             if (!subPackage.endsWith(".")) {
113                 buf.append(".");
114             }
115         }
116         return buf.toString();
117     }
118 
119     @Override
120     public void endElement(String namespaceURI,
121                            String localName,
122                            String qName)
123     {
124         if ("package".equals(qName)) {
125 
126             packageNames.add(getPackageName());
127             packageStack.pop();
128         }
129     }
130 
131     /**
132      * Returns the set of package names, compiled from all
133      * checkstyle_packages.xml files found on the given classloaders
134      * classpath.
135      * @param classLoader the class loader for loading the
136      *          checkstyle_packages.xml files.
137      * @return the set of package names.
138      * @throws CheckstyleException if an error occurs.
139      */
140     public static Set<String> getPackageNames(ClassLoader classLoader)
141         throws CheckstyleException
142     {
143 
144         Enumeration<URL> packageFiles = null;
145         try {
146             packageFiles = classLoader.getResources(CHECKSTYLE_PACKAGES);
147         }
148         catch (IOException e) {
149             throw new CheckstyleException(
150                     "unable to get package file resources", e);
151         }
152 
153         //create the loader outside the loop to prevent PackageObjectFactory
154         //being created anew for each file
155         final PackageNamesLoader namesLoader = newPackageNamesLoader();
156 
157         while (null != packageFiles && packageFiles.hasMoreElements()) {
158             final URL packageFile = packageFiles.nextElement();
159             InputStream stream = null;
160 
161             try {
162                 stream = new BufferedInputStream(packageFile.openStream());
163                 final InputSource source = new InputSource(stream);
164                 loadPackageNamesSource(source, "default package names",
165                     namesLoader);
166             }
167             catch (IOException e) {
168                 throw new CheckstyleException(
169                         "unable to open " + packageFile, e);
170             }
171             finally {
172                 Utils.closeQuietly(stream);
173             }
174         }
175         return namesLoader.getPackageNames();
176     }
177 
178     /**
179      * Creates a PackageNamesLoader instance.
180      * @return the PackageNamesLoader
181      * @throws CheckstyleException if the creation failed
182      */
183     private static PackageNamesLoader newPackageNamesLoader()
184         throws CheckstyleException
185     {
186         try {
187             return new PackageNamesLoader();
188         }
189         catch (final ParserConfigurationException e) {
190             throw new CheckstyleException(
191                     "unable to create PackageNamesLoader ", e);
192         }
193         catch (final SAXException e) {
194             throw new CheckstyleException(
195                     "unable to create PackageNamesLoader - "
196                     + e.getMessage(), e);
197         }
198     }
199 
200     /**
201      * Returns the list of package names in a specified source.
202      * @param source the source for the list.
203      * @param sourceName the name of the source.
204      * @param nameLoader the PackageNamesLoader instance
205      * @throws CheckstyleException if an error occurs.
206      */
207     private static void loadPackageNamesSource(
208             InputSource source, String sourceName,
209             PackageNamesLoader nameLoader)
210         throws CheckstyleException
211     {
212         try {
213             nameLoader.parseInputSource(source);
214         }
215         catch (final SAXException e) {
216             throw new CheckstyleException("unable to parse "
217                     + sourceName + " - " + e.getMessage(), e);
218         }
219         catch (final IOException e) {
220             throw new CheckstyleException("unable to read " + sourceName, e);
221         }
222     }
223 }