1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package com.puppycrawl.tools.checkstyle;
20
21 import com.google.common.collect.Lists;
22 import com.google.common.collect.Maps;
23 import com.puppycrawl.tools.checkstyle.api.AbstractLoader;
24 import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
25 import com.puppycrawl.tools.checkstyle.api.Configuration;
26 import com.puppycrawl.tools.checkstyle.api.FastStack;
27 import com.puppycrawl.tools.checkstyle.api.SeverityLevel;
28 import org.xml.sax.Attributes;
29 import org.xml.sax.InputSource;
30 import org.xml.sax.SAXException;
31 import org.xml.sax.SAXParseException;
32
33 import javax.xml.parsers.ParserConfigurationException;
34 import java.io.File;
35 import java.io.FileNotFoundException;
36 import java.io.IOException;
37 import java.io.InputStream;
38 import java.net.MalformedURLException;
39 import java.net.URI;
40 import java.net.URISyntaxException;
41 import java.net.URL;
42 import java.util.Iterator;
43 import java.util.List;
44 import java.util.Map;
45
46
47
48
49
50
51
52 public final class ConfigurationLoader
53 {
54
55 private static final String DTD_PUBLIC_ID_1_0 =
56 "-//Puppy Crawl//DTD Check Configuration 1.0//EN";
57
58
59 private static final String DTD_RESOURCE_NAME_1_0 =
60 "com/puppycrawl/tools/checkstyle/configuration_1_0.dtd";
61
62
63 private static final String DTD_PUBLIC_ID_1_1 =
64 "-//Puppy Crawl//DTD Check Configuration 1.1//EN";
65
66
67 private static final String DTD_RESOURCE_NAME_1_1 =
68 "com/puppycrawl/tools/checkstyle/configuration_1_1.dtd";
69
70
71 private static final String DTD_PUBLIC_ID_1_2 =
72 "-//Puppy Crawl//DTD Check Configuration 1.2//EN";
73
74
75 private static final String DTD_RESOURCE_NAME_1_2 =
76 "com/puppycrawl/tools/checkstyle/configuration_1_2.dtd";
77
78
79 private static final String DTD_PUBLIC_ID_1_3 =
80 "-//Puppy Crawl//DTD Check Configuration 1.3//EN";
81
82
83 private static final String DTD_RESOURCE_NAME_1_3 =
84 "com/puppycrawl/tools/checkstyle/configuration_1_3.dtd";
85
86
87
88
89
90 private final class InternalLoader
91 extends AbstractLoader
92 {
93
94 private static final String MODULE = "module";
95
96 private static final String NAME = "name";
97
98 private static final String PROPERTY = "property";
99
100 private static final String VALUE = "value";
101
102 private static final String DEFAULT = "default";
103
104 private static final String SEVERITY = "severity";
105
106 private static final String MESSAGE = "message";
107
108 private static final String KEY = "key";
109
110
111
112
113
114
115 private InternalLoader()
116 throws SAXException, ParserConfigurationException
117 {
118
119 super(createIdToResourceNameMap());
120 }
121
122 @Override
123 public void startElement(String namespaceURI,
124 String localName,
125 String qName,
126 Attributes atts)
127 throws SAXException
128 {
129
130 if (qName.equals(MODULE)) {
131
132 final String name = atts.getValue(NAME);
133 final DefaultConfiguration conf =
134 new DefaultConfiguration(name);
135
136 if (configuration == null) {
137 configuration = conf;
138 }
139
140
141 if (!configStack.isEmpty()) {
142 final DefaultConfiguration top =
143 configStack.peek();
144 top.addChild(conf);
145 }
146
147 configStack.push(conf);
148 }
149 else if (qName.equals(PROPERTY)) {
150
151 final String name = atts.getValue(NAME);
152 final String value;
153 try {
154 value = replaceProperties(atts.getValue(VALUE),
155 overridePropsResolver, atts.getValue(DEFAULT));
156 }
157 catch (final CheckstyleException ex) {
158 throw new SAXException(ex.getMessage());
159 }
160
161
162 final DefaultConfiguration top =
163 configStack.peek();
164 top.addAttribute(name, value);
165 }
166 else if (qName.equals(MESSAGE)) {
167
168 final String key = atts.getValue(KEY);
169 final String value = atts.getValue(VALUE);
170
171
172 final DefaultConfiguration top = configStack.peek();
173 top.addMessage(key, value);
174 }
175 }
176
177 @Override
178 public void endElement(String namespaceURI,
179 String localName,
180 String qName)
181 throws SAXException
182 {
183 if (qName.equals(MODULE)) {
184
185 final Configuration recentModule =
186 configStack.pop();
187
188
189
190 SeverityLevel level = null;
191 try {
192 final String severity = recentModule.getAttribute(SEVERITY);
193 level = SeverityLevel.getInstance(severity);
194 }
195 catch (final CheckstyleException e) {
196
197 ;
198 }
199
200
201
202 final boolean omitModule = omitIgnoredModules
203 && SeverityLevel.IGNORE.equals(level);
204
205 if (omitModule && !configStack.isEmpty()) {
206 final DefaultConfiguration parentModule =
207 configStack.peek();
208 parentModule.removeChild(recentModule);
209 }
210 }
211 }
212
213 }
214
215
216 private final InternalLoader saxHandler;
217
218
219 private final PropertyResolver overridePropsResolver;
220
221 private final FastStack<DefaultConfiguration> configStack =
222 FastStack.newInstance();
223
224 private Configuration configuration;
225
226
227 private final boolean omitIgnoredModules;
228
229
230
231
232
233 private static Map<String, String> createIdToResourceNameMap()
234 {
235 final Map<String, String> map = Maps.newHashMap();
236 map.put(DTD_PUBLIC_ID_1_0, DTD_RESOURCE_NAME_1_0);
237 map.put(DTD_PUBLIC_ID_1_1, DTD_RESOURCE_NAME_1_1);
238 map.put(DTD_PUBLIC_ID_1_2, DTD_RESOURCE_NAME_1_2);
239 map.put(DTD_PUBLIC_ID_1_3, DTD_RESOURCE_NAME_1_3);
240 return map;
241 }
242
243
244
245
246
247
248
249
250
251 private ConfigurationLoader(final PropertyResolver overrideProps,
252 final boolean omitIgnoredModules)
253 throws ParserConfigurationException, SAXException
254 {
255 saxHandler = new InternalLoader();
256 overridePropsResolver = overrideProps;
257 this.omitIgnoredModules = omitIgnoredModules;
258 }
259
260
261
262
263
264
265
266
267
268
269
270 private void parseInputSource(InputSource source)
271 throws IOException, SAXException
272 {
273 saxHandler.parseInputSource(source);
274 }
275
276
277
278
279
280
281
282
283 public static Configuration loadConfiguration(String config,
284 PropertyResolver overridePropsResolver) throws CheckstyleException
285 {
286 return loadConfiguration(config, overridePropsResolver, false);
287 }
288
289
290
291
292
293
294
295
296
297
298
299 public static Configuration loadConfiguration(String config,
300 PropertyResolver overridePropsResolver, boolean omitIgnoredModules)
301 throws CheckstyleException
302 {
303 try {
304
305 URI uri;
306 try {
307 final URL url = new URL(config);
308 uri = url.toURI();
309 }
310 catch (final MalformedURLException ex) {
311 uri = null;
312 }
313 catch (final URISyntaxException ex) {
314
315 uri = null;
316 }
317 if (uri == null) {
318 final File file = new File(config);
319 if (file.exists()) {
320 uri = file.toURI();
321 }
322 else {
323
324 try {
325 final URL configUrl = ConfigurationLoader.class
326 .getResource(config);
327 if (configUrl == null) {
328 throw new FileNotFoundException(config);
329 }
330 uri = configUrl.toURI();
331 }
332 catch (final URISyntaxException e) {
333 throw new FileNotFoundException(config);
334 }
335 }
336 }
337 final InputSource source = new InputSource(uri.toString());
338 return loadConfiguration(source, overridePropsResolver,
339 omitIgnoredModules);
340 }
341 catch (final FileNotFoundException e) {
342 throw new CheckstyleException("unable to find " + config, e);
343 }
344 catch (final CheckstyleException e) {
345
346 throw new CheckstyleException("unable to read " + config + " - "
347 + e.getMessage(), e);
348 }
349 }
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368 @Deprecated
369 public static Configuration loadConfiguration(InputStream configStream,
370 PropertyResolver overridePropsResolver, boolean omitIgnoredModules)
371 throws CheckstyleException
372 {
373 return loadConfiguration(new InputSource(configStream),
374 overridePropsResolver, omitIgnoredModules);
375 }
376
377
378
379
380
381
382
383
384
385
386
387
388
389 public static Configuration loadConfiguration(InputSource configSource,
390 PropertyResolver overridePropsResolver, boolean omitIgnoredModules)
391 throws CheckstyleException
392 {
393 try {
394 final ConfigurationLoader loader =
395 new ConfigurationLoader(overridePropsResolver,
396 omitIgnoredModules);
397 loader.parseInputSource(configSource);
398 return loader.getConfiguration();
399 }
400 catch (final ParserConfigurationException e) {
401 throw new CheckstyleException(
402 "unable to parse configuration stream", e);
403 }
404 catch (final SAXParseException e) {
405 throw new CheckstyleException("unable to parse configuration stream"
406 + " - " + e.getMessage() + ":" + e.getLineNumber()
407 + ":" + e.getColumnNumber(), e);
408 }
409 catch (final SAXException e) {
410 throw new CheckstyleException("unable to parse configuration stream"
411 + " - " + e.getMessage(), e);
412 }
413 catch (final IOException e) {
414 throw new CheckstyleException("unable to read from stream", e);
415 }
416 }
417
418
419
420
421
422 private Configuration getConfiguration()
423 {
424 return configuration;
425 }
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451 static String replaceProperties(
452 String value, PropertyResolver props, String defaultValue)
453 throws CheckstyleException
454 {
455 if (value == null) {
456 return null;
457 }
458
459 final List<String> fragments = Lists.newArrayList();
460 final List<String> propertyRefs = Lists.newArrayList();
461 parsePropertyString(value, fragments, propertyRefs);
462
463 final StringBuffer sb = new StringBuffer();
464 final Iterator<String> i = fragments.iterator();
465 final Iterator<String> j = propertyRefs.iterator();
466 while (i.hasNext()) {
467 String fragment = i.next();
468 if (fragment == null) {
469 final String propertyName = j.next();
470 fragment = props.resolve(propertyName);
471 if (fragment == null) {
472 if (defaultValue != null) {
473 return defaultValue;
474 }
475 throw new CheckstyleException(
476 "Property ${" + propertyName + "} has not been set");
477 }
478 }
479 sb.append(fragment);
480 }
481
482 return sb.toString();
483 }
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504 private static void parsePropertyString(String value,
505 List<String> fragments,
506 List<String> propertyRefs)
507 throws CheckstyleException
508 {
509 int prev = 0;
510 int pos;
511
512 while ((pos = value.indexOf("$", prev)) >= 0) {
513
514
515
516
517
518 if (pos > 0) {
519 fragments.add(value.substring(prev, pos));
520 }
521
522
523 if (pos == (value.length() - 1)) {
524 fragments.add("$");
525 prev = pos + 1;
526 }
527 else if (value.charAt(pos + 1) != '{') {
528
529
530
531
532
533
534 if (value.charAt(pos + 1) == '$') {
535
536 fragments.add("$");
537 prev = pos + 2;
538 }
539 else {
540
541 fragments.add(value.substring(pos, pos + 2));
542 prev = pos + 2;
543 }
544
545 }
546 else {
547
548 final int endName = value.indexOf('}', pos);
549 if (endName < 0) {
550 throw new CheckstyleException("Syntax error in property: "
551 + value);
552 }
553 final String propertyName = value.substring(pos + 2, endName);
554 fragments.add(null);
555 propertyRefs.add(propertyName);
556 prev = endName + 1;
557 }
558 }
559
560
561 if (prev < value.length()) {
562 fragments.add(value.substring(prev));
563 }
564 }
565 }