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
115 private static BeanUtilsBean createBeanUtilsBean() {
116 final ConvertUtilsBean cub = new ConvertUtilsBean();
117
118 registerIntegralTypes(cub);
119 registerCustomTypes(cub);
120
121 return new BeanUtilsBean(cub, new PropertyUtilsBean());
122 }
123
124
125
126
127
128
129
130
131 private static void registerIntegralTypes(ConvertUtilsBean cub) {
132 cub.register(new BooleanConverter(), Boolean.TYPE);
133 cub.register(new BooleanConverter(), Boolean.class);
134 cub.register(new ArrayConverter(
135 boolean[].class, new BooleanConverter()), boolean[].class);
136 cub.register(new ByteConverter(), Byte.TYPE);
137 cub.register(new ByteConverter(), Byte.class);
138 cub.register(new ArrayConverter(byte[].class, new ByteConverter()),
139 byte[].class);
140 cub.register(new CharacterConverter(), Character.TYPE);
141 cub.register(new CharacterConverter(), Character.class);
142 cub.register(new ArrayConverter(char[].class, new CharacterConverter()),
143 char[].class);
144 cub.register(new DoubleConverter(), Double.TYPE);
145 cub.register(new DoubleConverter(), Double.class);
146 cub.register(new ArrayConverter(double[].class, new DoubleConverter()),
147 double[].class);
148 cub.register(new FloatConverter(), Float.TYPE);
149 cub.register(new FloatConverter(), Float.class);
150 cub.register(new ArrayConverter(float[].class, new FloatConverter()),
151 float[].class);
152 cub.register(new IntegerConverter(), Integer.TYPE);
153 cub.register(new IntegerConverter(), Integer.class);
154 cub.register(new ArrayConverter(int[].class, new IntegerConverter()),
155 int[].class);
156 cub.register(new LongConverter(), Long.TYPE);
157 cub.register(new LongConverter(), Long.class);
158 cub.register(new ArrayConverter(long[].class, new LongConverter()),
159 long[].class);
160 cub.register(new ShortConverter(), Short.TYPE);
161 cub.register(new ShortConverter(), Short.class);
162 cub.register(new ArrayConverter(short[].class, new ShortConverter()),
163 short[].class);
164 cub.register(new RelaxedStringArrayConverter(), String[].class);
165
166
167
168 }
169
170
171
172
173
174
175
176
177 private static void registerCustomTypes(ConvertUtilsBean cub) {
178 cub.register(new PatternConverter(), Pattern.class);
179 cub.register(new PatternArrayConverter(), Pattern[].class);
180 cub.register(new SeverityLevelConverter(), SeverityLevel.class);
181 cub.register(new ScopeConverter(), Scope.class);
182 cub.register(new UriConverter(), URI.class);
183 cub.register(new RelaxedAccessModifierArrayConverter(), AccessModifierOption[].class);
184 }
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199 @Override
200 public final void configure(Configuration config)
201 throws CheckstyleException {
202 configuration = config;
203
204 final String[] attributes = config.getPropertyNames();
205
206 for (final String key : attributes) {
207 final String value = config.getProperty(key);
208
209 tryCopyProperty(key, value, true);
210 }
211
212 finishLocalSetup();
213
214 final Configuration[] childConfigs = config.getChildren();
215 for (final Configuration childConfig : childConfigs) {
216 setupChild(childConfig);
217 }
218 }
219
220
221
222
223
224
225
226
227
228 private void tryCopyProperty(String key, Object value, boolean recheck)
229 throws CheckstyleException {
230 final BeanUtilsBean beanUtils = createBeanUtilsBean();
231
232 try {
233 if (recheck) {
234
235
236
237 final PropertyDescriptor descriptor =
238 PropertyUtils.getPropertyDescriptor(this, key);
239 if (descriptor == null) {
240 final String message = getLocalizedMessage(
241 AbstractAutomaticBean.class,
242 "AbstractAutomaticBean.doesNotExist", key);
243 throw new CheckstyleException(message);
244 }
245 }
246
247 beanUtils.copyProperty(this, key, value);
248 }
249 catch (final InvocationTargetException | IllegalAccessException
250 | NoSuchMethodException exc) {
251
252
253
254
255 final String message = getLocalizedMessage(
256 AbstractAutomaticBean.class,
257 "AbstractAutomaticBean.cannotSet", key, value);
258 throw new CheckstyleException(message, exc);
259 }
260 catch (final IllegalArgumentException | ConversionException exc) {
261 final String message = getLocalizedMessage(
262 AbstractAutomaticBean.class,
263 "AbstractAutomaticBean.illegalValue", value, key);
264 throw new CheckstyleException(message, exc);
265 }
266 }
267
268
269
270
271
272
273 @Override
274 public final void contextualize(Context context)
275 throws CheckstyleException {
276 final Collection<String> attributes = context.getAttributeNames();
277
278 for (final String key : attributes) {
279 final Object value = context.get(key);
280
281 tryCopyProperty(key, value, false);
282 }
283 }
284
285
286
287
288
289
290 protected final Configuration getConfiguration() {
291 return configuration;
292 }
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307 protected void setupChild(Configuration childConf)
308 throws CheckstyleException {
309 if (childConf != null) {
310 final String message = getLocalizedMessage(
311 AbstractAutomaticBean.class,
312 "AbstractAutomaticBean.disallowedChild", childConf.getName(),
313 configuration.getName());
314 throw new CheckstyleException(message);
315 }
316 }
317
318
319
320
321
322
323
324
325
326 private static String getLocalizedMessage(Class<?> caller,
327 String messageKey, Object... args) {
328 final LocalizedMessage localizedMessage = new LocalizedMessage(
329 Definitions.CHECKSTYLE_BUNDLE, caller,
330 messageKey, args);
331
332 return localizedMessage.getMessage();
333 }
334
335
336 private static final class PatternConverter implements Converter {
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 private PatternArrayConverter() {
358
359 }
360
361 @Override
362 @SuppressWarnings("unchecked")
363 public Object convert(Class type, Object value) {
364 final String[] tokens = value.toString().split(COMMA_SEPARATOR, -1);
365 final List<Pattern> result = new ArrayList<>();
366
367 for (String token : tokens) {
368 if (token.isEmpty()) {
369 continue;
370 }
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 String[] tokens = value.toString().trim().split(COMMA_SEPARATOR, -1);
459 final List<String> result = new ArrayList<>();
460
461 for (String token : tokens) {
462 if (token.isEmpty()) {
463 continue;
464 }
465 result.add(token.trim());
466 }
467
468 return result.toArray(CommonUtil.EMPTY_STRING_ARRAY);
469 }
470
471 }
472
473
474
475
476
477
478 private static final class RelaxedAccessModifierArrayConverter implements Converter {
479
480
481 private static final AccessModifierOption[] EMPTY_MODIFIER_ARRAY =
482 new AccessModifierOption[0];
483
484
485
486
487 private RelaxedAccessModifierArrayConverter() {
488
489 }
490
491 @Override
492 @SuppressWarnings("unchecked")
493 public Object convert(Class type, Object value) {
494 final String[] tokens = value.toString().trim().split(COMMA_SEPARATOR, -1);
495 final List<AccessModifierOption> result = new ArrayList<>();
496
497 for (String token : tokens) {
498 if (token.isEmpty()) {
499 continue;
500 }
501 result.add(AccessModifierOption.getInstance(token));
502 }
503
504 return result.toArray(EMPTY_MODIFIER_ARRAY);
505 }
506
507 }
508
509 }