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