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