1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package com.puppycrawl.tools.checkstyle;
21
22 import java.beans.PropertyDescriptor;
23 import java.lang.reflect.InvocationTargetException;
24 import java.net.URI;
25 import java.util.ArrayList;
26 import java.util.Collection;
27 import java.util.List;
28 import java.util.StringTokenizer;
29 import java.util.regex.Pattern;
30
31 import javax.annotation.Nullable;
32
33 import org.apache.commons.beanutils.BeanUtilsBean;
34 import org.apache.commons.beanutils.ConversionException;
35 import org.apache.commons.beanutils.ConvertUtilsBean;
36 import org.apache.commons.beanutils.Converter;
37 import org.apache.commons.beanutils.PropertyUtils;
38 import org.apache.commons.beanutils.PropertyUtilsBean;
39 import org.apache.commons.beanutils.converters.ArrayConverter;
40 import org.apache.commons.beanutils.converters.BooleanConverter;
41 import org.apache.commons.beanutils.converters.ByteConverter;
42 import org.apache.commons.beanutils.converters.CharacterConverter;
43 import org.apache.commons.beanutils.converters.DoubleConverter;
44 import org.apache.commons.beanutils.converters.FloatConverter;
45 import org.apache.commons.beanutils.converters.IntegerConverter;
46 import org.apache.commons.beanutils.converters.LongConverter;
47 import org.apache.commons.beanutils.converters.ShortConverter;
48
49 import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
50 import com.puppycrawl.tools.checkstyle.api.Configurable;
51 import com.puppycrawl.tools.checkstyle.api.Configuration;
52 import com.puppycrawl.tools.checkstyle.api.Context;
53 import com.puppycrawl.tools.checkstyle.api.Contextualizable;
54 import com.puppycrawl.tools.checkstyle.api.Scope;
55 import com.puppycrawl.tools.checkstyle.api.SeverityLevel;
56 import com.puppycrawl.tools.checkstyle.checks.naming.AccessModifierOption;
57 import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
58
59
60
61
62
63 public abstract class AbstractAutomaticBean
64 implements Configurable, Contextualizable {
65
66
67
68
69 public enum OutputStreamOptions {
70
71
72
73
74 CLOSE,
75
76
77
78
79 NONE,
80
81 }
82
83
84 private static final String COMMA_SEPARATOR = ",";
85
86
87 private Configuration configuration;
88
89
90
91
92 protected AbstractAutomaticBean() {
93
94 }
95
96
97
98
99
100
101
102
103
104
105
106 protected abstract void finishLocalSetup() throws CheckstyleException;
107
108
109
110
111
112
113
114
115
116 private static BeanUtilsBean createBeanUtilsBean() {
117 final ConvertUtilsBean cub = new ConvertUtilsBean();
118
119 registerIntegralTypes(cub);
120 registerCustomTypes(cub);
121
122 return new BeanUtilsBean(cub, new PropertyUtilsBean());
123 }
124
125
126
127
128
129
130
131
132 private static void registerIntegralTypes(ConvertUtilsBean cub) {
133 cub.register(new BooleanConverter(), Boolean.TYPE);
134 cub.register(new BooleanConverter(), Boolean.class);
135 cub.register(new ArrayConverter(
136 boolean[].class, new BooleanConverter()), boolean[].class);
137 cub.register(new ByteConverter(), Byte.TYPE);
138 cub.register(new ByteConverter(), Byte.class);
139 cub.register(new ArrayConverter(byte[].class, new ByteConverter()),
140 byte[].class);
141 cub.register(new CharacterConverter(), Character.TYPE);
142 cub.register(new CharacterConverter(), Character.class);
143 cub.register(new ArrayConverter(char[].class, new CharacterConverter()),
144 char[].class);
145 cub.register(new DoubleConverter(), Double.TYPE);
146 cub.register(new DoubleConverter(), Double.class);
147 cub.register(new ArrayConverter(double[].class, new DoubleConverter()),
148 double[].class);
149 cub.register(new FloatConverter(), Float.TYPE);
150 cub.register(new FloatConverter(), Float.class);
151 cub.register(new ArrayConverter(float[].class, new FloatConverter()),
152 float[].class);
153 cub.register(new IntegerConverter(), Integer.TYPE);
154 cub.register(new IntegerConverter(), Integer.class);
155 cub.register(new ArrayConverter(int[].class, new IntegerConverter()),
156 int[].class);
157 cub.register(new LongConverter(), Long.TYPE);
158 cub.register(new LongConverter(), Long.class);
159 cub.register(new ArrayConverter(long[].class, new LongConverter()),
160 long[].class);
161 cub.register(new ShortConverter(), Short.TYPE);
162 cub.register(new ShortConverter(), Short.class);
163 cub.register(new ArrayConverter(short[].class, new ShortConverter()),
164 short[].class);
165 cub.register(new RelaxedStringArrayConverter(), String[].class);
166
167
168
169 }
170
171
172
173
174
175
176
177
178 private static void registerCustomTypes(ConvertUtilsBean cub) {
179 cub.register(new PatternConverter(), Pattern.class);
180 cub.register(new PatternArrayConverter(), Pattern[].class);
181 cub.register(new SeverityLevelConverter(), SeverityLevel.class);
182 cub.register(new ScopeConverter(), Scope.class);
183 cub.register(new UriConverter(), URI.class);
184 cub.register(new RelaxedAccessModifierArrayConverter(), AccessModifierOption[].class);
185 }
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200 @Override
201 public final void configure(Configuration config)
202 throws CheckstyleException {
203 configuration = config;
204
205 final String[] attributes = config.getPropertyNames();
206
207 for (final String key : attributes) {
208 final String value = config.getProperty(key);
209
210 tryCopyProperty(key, value, true);
211 }
212
213 finishLocalSetup();
214
215 final Configuration[] childConfigs = config.getChildren();
216 for (final Configuration childConfig : childConfigs) {
217 setupChild(childConfig);
218 }
219 }
220
221
222
223
224
225
226
227
228
229 private void tryCopyProperty(String key, Object value, boolean recheck)
230 throws CheckstyleException {
231 final BeanUtilsBean beanUtils = createBeanUtilsBean();
232
233 try {
234 if (recheck) {
235
236
237
238 final PropertyDescriptor descriptor =
239 PropertyUtils.getPropertyDescriptor(this, key);
240 if (descriptor == null) {
241 final String message = getLocalizedMessage(
242 AbstractAutomaticBean.class,
243 "AbstractAutomaticBean.doesNotExist", key);
244 throw new CheckstyleException(message);
245 }
246 }
247
248 beanUtils.copyProperty(this, key, value);
249 }
250 catch (final InvocationTargetException | IllegalAccessException
251 | NoSuchMethodException exc) {
252
253
254
255
256 final String message = getLocalizedMessage(
257 AbstractAutomaticBean.class,
258 "AbstractAutomaticBean.cannotSet", key, value);
259 throw new CheckstyleException(message, exc);
260 }
261 catch (final IllegalArgumentException | ConversionException exc) {
262 final String message = getLocalizedMessage(
263 AbstractAutomaticBean.class,
264 "AbstractAutomaticBean.illegalValue", value, key);
265 throw new CheckstyleException(message, exc);
266 }
267 }
268
269
270
271
272
273
274 @Override
275 public final void contextualize(Context context)
276 throws CheckstyleException {
277 final Collection<String> attributes = context.getAttributeNames();
278
279 for (final String key : attributes) {
280 final Object value = context.get(key);
281
282 tryCopyProperty(key, value, false);
283 }
284 }
285
286
287
288
289
290
291 protected final Configuration getConfiguration() {
292 return configuration;
293 }
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308 protected void setupChild(Configuration childConf)
309 throws CheckstyleException {
310 if (childConf != null) {
311 final String message = getLocalizedMessage(
312 AbstractAutomaticBean.class,
313 "AbstractAutomaticBean.disallowedChild", childConf.getName(),
314 configuration.getName());
315 throw new CheckstyleException(message);
316 }
317 }
318
319
320
321
322
323
324
325
326
327 private static String getLocalizedMessage(Class<?> caller,
328 String messageKey, Object... args) {
329 final LocalizedMessage localizedMessage = new LocalizedMessage(
330 Definitions.CHECKSTYLE_BUNDLE, caller,
331 messageKey, args);
332
333 return localizedMessage.getMessage();
334 }
335
336
337 private static final class PatternConverter implements Converter {
338
339
340
341 private PatternConverter() {
342
343 }
344
345 @Override
346 @SuppressWarnings("unchecked")
347 public Object convert(Class type, Object value) {
348 return CommonUtil.createPattern(value.toString());
349 }
350
351 }
352
353
354 private static final class PatternArrayConverter implements Converter {
355
356
357
358 private PatternArrayConverter() {
359
360 }
361
362 @Override
363 @SuppressWarnings("unchecked")
364 public Object convert(Class type, Object value) {
365 final StringTokenizer tokenizer = new StringTokenizer(
366 value.toString(), COMMA_SEPARATOR);
367 final List<Pattern> result = new ArrayList<>();
368
369 while (tokenizer.hasMoreTokens()) {
370 final String token = tokenizer.nextToken();
371 result.add(CommonUtil.createPattern(token.trim()));
372 }
373
374 return result.toArray(new Pattern[0]);
375 }
376 }
377
378
379 private static final class SeverityLevelConverter implements Converter {
380
381
382
383 private SeverityLevelConverter() {
384
385 }
386
387 @Override
388 @SuppressWarnings("unchecked")
389 public Object convert(Class type, Object value) {
390 return SeverityLevel.getInstance(value.toString());
391 }
392
393 }
394
395
396 private static final class ScopeConverter implements Converter {
397
398
399
400 private ScopeConverter() {
401
402 }
403
404 @Override
405 @SuppressWarnings("unchecked")
406 public Object convert(Class type, Object value) {
407 return Scope.getInstance(value.toString());
408 }
409
410 }
411
412
413 private static final class UriConverter implements Converter {
414
415
416
417 private UriConverter() {
418
419 }
420
421 @Nullable
422 @Override
423 @SuppressWarnings("unchecked")
424 public Object convert(Class type, Object value) {
425 final String url = value.toString();
426 URI result = null;
427
428 if (!CommonUtil.isBlank(url)) {
429 try {
430 result = CommonUtil.getUriByFilename(url);
431 }
432 catch (CheckstyleException exc) {
433 throw new IllegalArgumentException(exc);
434 }
435 }
436
437 return result;
438 }
439
440 }
441
442
443
444
445
446
447 private static final class RelaxedStringArrayConverter implements Converter {
448
449
450
451 private RelaxedStringArrayConverter() {
452
453 }
454
455 @Override
456 @SuppressWarnings("unchecked")
457 public Object convert(Class type, Object value) {
458 final StringTokenizer tokenizer = new StringTokenizer(
459 value.toString().trim(), COMMA_SEPARATOR);
460 final List<String> result = new ArrayList<>();
461
462 while (tokenizer.hasMoreTokens()) {
463 final String token = tokenizer.nextToken();
464 result.add(token.trim());
465 }
466
467 return result.toArray(CommonUtil.EMPTY_STRING_ARRAY);
468 }
469
470 }
471
472
473
474
475
476
477 private static final class RelaxedAccessModifierArrayConverter implements Converter {
478
479
480 private static final AccessModifierOption[] EMPTY_MODIFIER_ARRAY =
481 new AccessModifierOption[0];
482
483
484
485
486 private RelaxedAccessModifierArrayConverter() {
487
488 }
489
490 @Override
491 @SuppressWarnings("unchecked")
492 public Object convert(Class type, Object value) {
493
494 final StringTokenizer tokenizer = new StringTokenizer(
495 value.toString().trim(), COMMA_SEPARATOR);
496 final List<AccessModifierOption> result = new ArrayList<>();
497
498 while (tokenizer.hasMoreTokens()) {
499 final String token = tokenizer.nextToken();
500 result.add(AccessModifierOption.getInstance(token));
501 }
502
503 return result.toArray(EMPTY_MODIFIER_ARRAY);
504 }
505
506 }
507
508 }