KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > puppycrawl > tools > checkstyle > ConfigurationLoader


1 ////////////////////////////////////////////////////////////////////////////////
2
// checkstyle: Checks Java source code for adherence to a set of rules.
3
// Copyright (C) 2001-2005 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 java.io.BufferedInputStream JavaDoc;
22 import java.io.FileInputStream JavaDoc;
23 import java.io.FileNotFoundException JavaDoc;
24 import java.io.IOException JavaDoc;
25 import java.io.InputStream JavaDoc;
26 import java.net.MalformedURLException JavaDoc;
27 import java.net.URL JavaDoc;
28 import java.util.ArrayList JavaDoc;
29 import java.util.HashMap JavaDoc;
30 import java.util.Iterator JavaDoc;
31 import java.util.List JavaDoc;
32 import java.util.Map JavaDoc;
33 import java.util.Stack JavaDoc;
34 import javax.xml.parsers.ParserConfigurationException JavaDoc;
35
36 import com.puppycrawl.tools.checkstyle.api.AbstractLoader;
37 import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
38 import com.puppycrawl.tools.checkstyle.api.Configuration;
39 import com.puppycrawl.tools.checkstyle.api.SeverityLevel;
40
41 import org.xml.sax.Attributes JavaDoc;
42 import org.xml.sax.InputSource JavaDoc;
43 import org.xml.sax.SAXException JavaDoc;
44 import org.xml.sax.SAXParseException JavaDoc;
45
46 /**
47  * Loads a configuration from a standard configuration XML file.
48  *
49  * @author Oliver Burn
50  * @version 1.0
51  */

52 public final class ConfigurationLoader
53 {
54     /** the public ID for version 1_0 of the configuration dtd */
55     private static final String JavaDoc DTD_PUBLIC_ID_1_0 =
56         "-//Puppy Crawl//DTD Check Configuration 1.0//EN";
57
58     /** the resource for version 1_0 of the configuration dtd */
59     private static final String JavaDoc DTD_RESOURCE_NAME_1_0 =
60         "com/puppycrawl/tools/checkstyle/configuration_1_0.dtd";
61
62     /** the public ID for version 1_1 of the configuration dtd */
63     private static final String JavaDoc DTD_PUBLIC_ID_1_1 =
64         "-//Puppy Crawl//DTD Check Configuration 1.1//EN";
65
66     /** the resource for version 1_1 of the configuration dtd */
67     private static final String JavaDoc DTD_RESOURCE_NAME_1_1 =
68         "com/puppycrawl/tools/checkstyle/configuration_1_1.dtd";
69
70     /** the public ID for version 1_2 of the configuration dtd */
71     private static final String JavaDoc DTD_PUBLIC_ID_1_2 =
72         "-//Puppy Crawl//DTD Check Configuration 1.2//EN";
73
74     /** the resource for version 1_1 of the configuration dtd */
75     private static final String JavaDoc DTD_RESOURCE_NAME_1_2 =
76         "com/puppycrawl/tools/checkstyle/configuration_1_2.dtd";
77
78     /** constant to specify two kilobyte of data */
79     private static final int TWO_KB = 2048;
80
81     /**
82      * Implements the SAX document handler interfaces, so they do not
83      * appear in the public API of the ConfigurationLoader.
84      */

85     private final class InternalLoader
86         extends AbstractLoader
87     {
88         /** module elements */
89         private static final String JavaDoc MODULE = "module";
90         /** name attribute */
91         private static final String JavaDoc NAME = "name";
92         /** property element */
93         private static final String JavaDoc PROPERTY = "property";
94         /** value attribute */
95         private static final String JavaDoc VALUE = "value";
96         /** default attribute */
97         private static final String JavaDoc DEFAULT = "default";
98         /** name of the severity property */
99         private static final String JavaDoc SEVERITY = "severity";
100
101         /**
102          * Creates a new InternalLoader.
103          * @throws SAXException if an error occurs
104          * @throws ParserConfigurationException if an error occurs
105          */

106         private InternalLoader()
107             throws SAXException JavaDoc, ParserConfigurationException JavaDoc
108         {
109             // super(DTD_PUBLIC_ID_1_1, DTD_RESOURCE_NAME_1_1);
110
super(createIdToResourceNameMap());
111         }
112
113         /** {@inheritDoc} **/
114         public void startElement(String JavaDoc aNamespaceURI,
115                                  String JavaDoc aLocalName,
116                                  String JavaDoc aQName,
117                                  Attributes JavaDoc aAtts)
118             throws SAXException JavaDoc
119         {
120             // TODO: debug logging for support puposes
121
if (aQName.equals(MODULE)) {
122                 //create configuration
123
final String JavaDoc name = aAtts.getValue(NAME);
124                 final DefaultConfiguration conf =
125                     new DefaultConfiguration(name);
126
127                 if (mConfiguration == null) {
128                     mConfiguration = conf;
129                 }
130
131                 //add configuration to it's parent
132
if (!mConfigStack.isEmpty()) {
133                     final DefaultConfiguration top =
134                         (DefaultConfiguration) mConfigStack.peek();
135                     top.addChild(conf);
136                 }
137
138                 mConfigStack.push(conf);
139             }
140             else if (aQName.equals(PROPERTY)) {
141                 //extract name and value
142
final String JavaDoc name = aAtts.getValue(NAME);
143                 final String JavaDoc value;
144                 try {
145                     value = replaceProperties(aAtts.getValue(VALUE),
146                         mOverridePropsResolver, aAtts.getValue(DEFAULT));
147                 }
148                 catch (final CheckstyleException ex) {
149                     throw new SAXException JavaDoc(ex.getMessage());
150                 }
151
152                 //add to attributes of configuration
153
final DefaultConfiguration top =
154                     (DefaultConfiguration) mConfigStack.peek();
155                 top.addAttribute(name, value);
156             }
157         }
158
159         /** {@inheritDoc} **/
160         public void endElement(String JavaDoc aNamespaceURI,
161                                String JavaDoc aLocalName,
162                                String JavaDoc aQName)
163             throws SAXException JavaDoc
164         {
165             if (aQName.equals(MODULE)) {
166
167                 final Configuration recentModule =
168                     (Configuration) mConfigStack.pop();
169
170                 // remove modules with severity ignore if these modules should
171
// be omitted
172
SeverityLevel level = null;
173                 try {
174                     final String JavaDoc severity = recentModule.getAttribute(SEVERITY);
175                     level = SeverityLevel.getInstance(severity);
176                 }
177                 catch (final CheckstyleException e) {
178                     //severity not set -> ignore
179
;
180                 }
181
182                 // omit this module if these should be omitted and the module
183
// has the severity 'ignore'
184
final boolean omitModule = mOmitIgnoredModules
185                     && SeverityLevel.IGNORE.equals(level);
186
187                 if (omitModule && !mConfigStack.isEmpty()) {
188                     final DefaultConfiguration parentModule =
189                         (DefaultConfiguration) mConfigStack.peek();
190                     parentModule.removeChild(recentModule);
191                 }
192             }
193         }
194
195     }
196
197     /** the SAX document handler */
198     private InternalLoader mSaxHandler;
199
200     /** property resolver **/
201     private final PropertyResolver mOverridePropsResolver;
202     /** the loaded configurations **/
203     private final Stack JavaDoc mConfigStack = new Stack JavaDoc();
204     /** the Configuration that is being built */
205     private Configuration mConfiguration;
206
207     /** flags if modules with the severity 'ignore' should be omitted. */
208     private boolean mOmitIgnoredModules;
209
210     /**
211      * Creates mapping between local resources and dtd ids.
212      * @return map between local resources and dtd ids.
213      */

214     private static Map JavaDoc createIdToResourceNameMap()
215     {
216         final Map JavaDoc map = new HashMap JavaDoc();
217         map.put(DTD_PUBLIC_ID_1_0, DTD_RESOURCE_NAME_1_0);
218         map.put(DTD_PUBLIC_ID_1_1, DTD_RESOURCE_NAME_1_1);
219         map.put(DTD_PUBLIC_ID_1_2, DTD_RESOURCE_NAME_1_2);
220         return map;
221     }
222
223     /**
224      * Creates a new <code>ConfigurationLoader</code> instance.
225      * @param aOverrideProps resolver for overriding properties
226      * @param aOmitIgnoredModules <code>true</code> if ignored modules should be
227      * omitted
228      * @throws ParserConfigurationException if an error occurs
229      * @throws SAXException if an error occurs
230      */

231     private ConfigurationLoader(final PropertyResolver aOverrideProps,
232                                 final boolean aOmitIgnoredModules)
233         throws ParserConfigurationException JavaDoc, SAXException JavaDoc
234     {
235         mSaxHandler = new InternalLoader();
236         mOverridePropsResolver = aOverrideProps;
237         mOmitIgnoredModules = aOmitIgnoredModules;
238     }
239
240     /**
241      * Parses the specified stream loading the configuration information.
242      * The stream is NOT closed after parsing, it is the responsibility of
243      * the caller to close the stream.
244      *
245      * @param aStream the stream that contains the configuration data
246      * @throws IOException if an error occurs
247      * @throws SAXException if an error occurs
248      */

249     private void parseInputStream(InputStream JavaDoc aStream)
250         throws IOException JavaDoc, SAXException JavaDoc
251     {
252         final InputStream JavaDoc configStream =
253             new BufferedInputStream JavaDoc(aStream, TWO_KB);
254         final InputSource JavaDoc inputSource = new InputSource JavaDoc(configStream);
255         mSaxHandler.parseInputSource(inputSource);
256     }
257
258     /**
259      * Returns the module configurations in a specified file.
260      * @param aConfig location of config file, can be either a URL or a filename
261      * @param aOverridePropsResolver overriding properties
262      * @return the check configurations
263      * @throws CheckstyleException if an error occurs
264      */

265     public static Configuration loadConfiguration(String JavaDoc aConfig,
266             PropertyResolver aOverridePropsResolver) throws CheckstyleException
267     {
268         return loadConfiguration(aConfig, aOverridePropsResolver, false);
269     }
270
271     /**
272      * Returns the module configurations in a specified file.
273      *
274      * @param aConfig location of config file, can be either a URL or a filename
275      * @param aOverridePropsResolver overriding properties
276      * @param aOmitIgnoredModules <code>true</code> if modules with severity
277      * 'ignore' should be omitted, <code>false</code> otherwise
278      * @return the check configurations
279      * @throws CheckstyleException if an error occurs
280      */

281     public static Configuration loadConfiguration(String JavaDoc aConfig,
282         PropertyResolver aOverridePropsResolver, boolean aOmitIgnoredModules)
283         throws CheckstyleException
284     {
285         InputStream JavaDoc bufferedStream = null;
286         try {
287             // figure out if this is a File or a URL
288
InputStream JavaDoc configStream;
289             try {
290                 final URL JavaDoc url = new URL JavaDoc(aConfig);
291                 configStream = url.openStream();
292             }
293             catch (final MalformedURLException JavaDoc ex) {
294                 configStream = new FileInputStream JavaDoc(aConfig);
295             }
296             bufferedStream = new BufferedInputStream JavaDoc(configStream);
297
298             return loadConfiguration(bufferedStream, aOverridePropsResolver,
299                     aOmitIgnoredModules);
300         }
301         catch (final FileNotFoundException JavaDoc e) {
302             throw new CheckstyleException("unable to find " + aConfig, e);
303         }
304         catch (final IOException JavaDoc e) {
305             throw new CheckstyleException("unable to read " + aConfig, e);
306         }
307         catch (final CheckstyleException e) {
308                 //wrap again to add file name info
309
throw new CheckstyleException("unable to read " + aConfig + " - "
310                     + e.getMessage(), e);
311         }
312         finally {
313             if (bufferedStream != null) {
314                 try {
315                     bufferedStream.close();
316                 }
317                 catch (final IOException JavaDoc e) {
318                     // cannot throw another exception.
319
;
320                 }
321             }
322         }
323     }
324
325     /**
326      * Returns the module configurations from a specified input stream.
327      * Note that clients are required to close the given stream by themselves
328      *
329      * @param aConfigStream the input stream to the Checkstyle configuration
330      * @param aOverridePropsResolver overriding properties
331      * @param aOmitIgnoredModules <code>true</code> if modules with severity
332      * 'ignore' should be omitted, <code>false</code> otherwise
333      * @return the check configurations
334      * @throws CheckstyleException if an error occurs
335      */

336     public static Configuration loadConfiguration(InputStream JavaDoc aConfigStream,
337         PropertyResolver aOverridePropsResolver, boolean aOmitIgnoredModules)
338         throws CheckstyleException
339     {
340         try {
341             final ConfigurationLoader loader =
342                 new ConfigurationLoader(aOverridePropsResolver,
343                                         aOmitIgnoredModules);
344             loader.parseInputStream(aConfigStream);
345             return loader.getConfiguration();
346         }
347         catch (final ParserConfigurationException JavaDoc e) {
348             throw new CheckstyleException(
349                 "unable to parse configuration stream", e);
350         }
351         catch (final SAXParseException JavaDoc e) {
352             throw new CheckstyleException("unable to parse configuration stream"
353                     + " - " + e.getMessage() + ":" + e.getLineNumber()
354                     + ":" + e.getColumnNumber(), e);
355         }
356         catch (final SAXException JavaDoc e) {
357             throw new CheckstyleException("unable to parse configuration stream"
358                     + " - " + e.getMessage(), e);
359         }
360         catch (final IOException JavaDoc e) {
361             throw new CheckstyleException("unable to read from stream", e);
362         }
363     }
364
365     /**
366      * Returns the configuration in the last file parsed.
367      * @return Configuration object
368      */

369     private Configuration getConfiguration()
370     {
371         return mConfiguration;
372     }
373
374     /**
375      * Replaces <code>${xxx}</code> style constructions in the given value
376      * with the string value of the corresponding data types.
377      *
378      * The method is package visible to facilitate testing.
379      *
380      * @param aValue The string to be scanned for property references.
381      * May be <code>null</code>, in which case this
382      * method returns immediately with no effect.
383      * @param aProps Mapping (String to String) of property names to their
384      * values. Must not be <code>null</code>.
385      * @param aDefaultValue default to use if one of the properties in aValue
386      * cannot be resolved from aProps.
387      *
388      * @throws CheckstyleException if the string contains an opening
389      * <code>${</code> without a closing
390      * <code>}</code>
391      * @return the original string with the properties replaced, or
392      * <code>null</code> if the original string is <code>null</code>.
393      *
394      * Code copied from ant -
395      * http://cvs.apache.org/viewcvs/jakarta-ant/src/main/org/apache/tools/ant/ProjectHelper.java
396      */

397     // Package visible for testing purposes
398
static String JavaDoc replaceProperties(
399             String JavaDoc aValue, PropertyResolver aProps, String JavaDoc aDefaultValue)
400         throws CheckstyleException
401     {
402         if (aValue == null) {
403             return null;
404         }
405
406         final List JavaDoc fragments = new ArrayList JavaDoc();
407         final List JavaDoc propertyRefs = new ArrayList JavaDoc();
408         parsePropertyString(aValue, fragments, propertyRefs);
409
410         final StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
411         final Iterator JavaDoc i = fragments.iterator();
412         final Iterator JavaDoc j = propertyRefs.iterator();
413         while (i.hasNext()) {
414             String JavaDoc fragment = (String JavaDoc) i.next();
415             if (fragment == null) {
416                 final String JavaDoc propertyName = (String JavaDoc) j.next();
417                 fragment = aProps.resolve(propertyName);
418                 if (fragment == null) {
419                     if (aDefaultValue != null) {
420                         return aDefaultValue;
421                     }
422                     throw new CheckstyleException(
423                         "Property ${" + propertyName + "} has not been set");
424                 }
425             }
426             sb.append(fragment);
427         }
428
429         return sb.toString();
430     }
431
432     /**
433      * Parses a string containing <code>${xxx}</code> style property
434      * references into two lists. The first list is a collection
435      * of text fragments, while the other is a set of string property names.
436      * <code>null</code> entries in the first list indicate a property
437      * reference from the second list.
438      *
439      * @param aValue Text to parse. Must not be <code>null</code>.
440      * @param aFragments List to add text fragments to.
441      * Must not be <code>null</code>.
442      * @param aPropertyRefs List to add property names to.
443      * Must not be <code>null</code>.
444      *
445      * @throws CheckstyleException if the string contains an opening
446      * <code>${</code> without a closing
447      * <code>}</code>
448      * Code copied from ant -
449      * http://cvs.apache.org/viewcvs/jakarta-ant/src/main/org/apache/tools/ant/ProjectHelper.java
450      */

451     private static void parsePropertyString(String JavaDoc aValue,
452                                            List JavaDoc aFragments,
453                                            List JavaDoc aPropertyRefs)
454         throws CheckstyleException
455     {
456         int prev = 0;
457         int pos;
458         //search for the next instance of $ from the 'prev' position
459
while ((pos = aValue.indexOf("$", prev)) >= 0) {
460
461             //if there was any text before this, add it as a fragment
462
//TODO, this check could be modified to go if pos>prev;
463
//seems like this current version could stick empty strings
464
//into the list
465
if (pos > 0) {
466                 aFragments.add(aValue.substring(prev, pos));
467             }
468             //if we are at the end of the string, we tack on a $
469
//then move past it
470
if (pos == (aValue.length() - 1)) {
471                 aFragments.add("$");
472                 prev = pos + 1;
473             }
474             else if (aValue.charAt(pos + 1) != '{') {
475                 //peek ahead to see if the next char is a property or not
476
//not a property: insert the char as a literal
477
/*
478                 fragments.addElement(value.substring(pos + 1, pos + 2));
479                 prev = pos + 2;
480                 */

481                 if (aValue.charAt(pos + 1) == '$') {
482                     //backwards compatibility two $ map to one mode
483
aFragments.add("$");
484                     prev = pos + 2;
485                 }
486                 else {
487                     //new behaviour: $X maps to $X for all values of X!='$'
488
aFragments.add(aValue.substring(pos, pos + 2));
489                     prev = pos + 2;
490                 }
491
492             }
493             else {
494                 //property found, extract its name or bail on a typo
495
final int endName = aValue.indexOf('}', pos);
496                 if (endName < 0) {
497                     throw new CheckstyleException("Syntax error in property: "
498                                                     + aValue);
499                 }
500                 final String JavaDoc propertyName = aValue.substring(pos + 2, endName);
501                 aFragments.add(null);
502                 aPropertyRefs.add(propertyName);
503                 prev = endName + 1;
504             }
505         }
506         //no more $ signs found
507
//if there is any tail to the file, append it
508
if (prev < aValue.length()) {
509             aFragments.add(aValue.substring(prev));
510         }
511     }
512 }
513
Popular Tags