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
93
94
95
96
97
98
99 protected abstract void finishLocalSetup() throws CheckstyleException;
100
101
102
103
104
105
106
107
108
109 private static BeanUtilsBean createBeanUtilsBean() {
110 final ConvertUtilsBean cub = new ConvertUtilsBean();
111
112 registerIntegralTypes(cub);
113 registerCustomTypes(cub);
114
115 return new BeanUtilsBean(cub, new PropertyUtilsBean());
116 }
117
118
119
120
121
122
123
124
125 private static void registerIntegralTypes(ConvertUtilsBean cub) {
126 cub.register(new BooleanConverter(), Boolean.TYPE);
127 cub.register(new BooleanConverter(), Boolean.class);
128 cub.register(new ArrayConverter(
129 boolean[].class, new BooleanConverter()), boolean[].class);
130 cub.register(new ByteConverter(), Byte.TYPE);
131 cub.register(new ByteConverter(), Byte.class);
132 cub.register(new ArrayConverter(byte[].class, new ByteConverter()),
133 byte[].class);
134 cub.register(new CharacterConverter(), Character.TYPE);
135 cub.register(new CharacterConverter(), Character.class);
136 cub.register(new ArrayConverter(char[].class, new CharacterConverter()),
137 char[].class);
138 cub.register(new DoubleConverter(), Double.TYPE);
139 cub.register(new DoubleConverter(), Double.class);
140 cub.register(new ArrayConverter(double[].class, new DoubleConverter()),
141 double[].class);
142 cub.register(new FloatConverter(), Float.TYPE);
143 cub.register(new FloatConverter(), Float.class);
144 cub.register(new ArrayConverter(float[].class, new FloatConverter()),
145 float[].class);
146 cub.register(new IntegerConverter(), Integer.TYPE);
147 cub.register(new IntegerConverter(), Integer.class);
148 cub.register(new ArrayConverter(int[].class, new IntegerConverter()),
149 int[].class);
150 cub.register(new LongConverter(), Long.TYPE);
151 cub.register(new LongConverter(), Long.class);
152 cub.register(new ArrayConverter(long[].class, new LongConverter()),
153 long[].class);
154 cub.register(new ShortConverter(), Short.TYPE);
155 cub.register(new ShortConverter(), Short.class);
156 cub.register(new ArrayConverter(short[].class, new ShortConverter()),
157 short[].class);
158 cub.register(new RelaxedStringArrayConverter(), String[].class);
159
160
161
162 }
163
164
165
166
167
168
169
170
171 private static void registerCustomTypes(ConvertUtilsBean cub) {
172 cub.register(new PatternConverter(), Pattern.class);
173 cub.register(new PatternArrayConverter(), Pattern[].class);
174 cub.register(new SeverityLevelConverter(), SeverityLevel.class);
175 cub.register(new ScopeConverter(), Scope.class);
176 cub.register(new UriConverter(), URI.class);
177 cub.register(new RelaxedAccessModifierArrayConverter(), AccessModifierOption[].class);
178 }
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193 @Override
194 public final void configure(Configuration config)
195 throws CheckstyleException {
196 configuration = config;
197
198 final String[] attributes = config.getPropertyNames();
199
200 for (final String key : attributes) {
201 final String value = config.getProperty(key);
202
203 tryCopyProperty(key, value, true);
204 }
205
206 finishLocalSetup();
207
208 final Configuration[] childConfigs = config.getChildren();
209 for (final Configuration childConfig : childConfigs) {
210 setupChild(childConfig);
211 }
212 }
213
214
215
216
217
218
219
220
221
222 private void tryCopyProperty(String key, Object value, boolean recheck)
223 throws CheckstyleException {
224 final BeanUtilsBean beanUtils = createBeanUtilsBean();
225
226 try {
227 if (recheck) {
228
229
230
231 final PropertyDescriptor descriptor =
232 PropertyUtils.getPropertyDescriptor(this, key);
233 if (descriptor == null) {
234 final String message = getLocalizedMessage(
235 AbstractAutomaticBean.class,
236 "AbstractAutomaticBean.doesNotExist", key);
237 throw new CheckstyleException(message);
238 }
239 }
240
241 beanUtils.copyProperty(this, key, value);
242 }
243 catch (final InvocationTargetException | IllegalAccessException
244 | NoSuchMethodException exc) {
245
246
247
248
249 final String message = getLocalizedMessage(
250 AbstractAutomaticBean.class,
251 "AbstractAutomaticBean.cannotSet", key, value);
252 throw new CheckstyleException(message, exc);
253 }
254 catch (final IllegalArgumentException | ConversionException exc) {
255 final String message = getLocalizedMessage(
256 AbstractAutomaticBean.class,
257 "AbstractAutomaticBean.illegalValue", value, key);
258 throw new CheckstyleException(message, exc);
259 }
260 }
261
262
263
264
265
266
267 @Override
268 public final void contextualize(Context context)
269 throws CheckstyleException {
270 final Collection<String> attributes = context.getAttributeNames();
271
272 for (final String key : attributes) {
273 final Object value = context.get(key);
274
275 tryCopyProperty(key, value, false);
276 }
277 }
278
279
280
281
282
283
284 protected final Configuration getConfiguration() {
285 return configuration;
286 }
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301 protected void setupChild(Configuration childConf)
302 throws CheckstyleException {
303 if (childConf != null) {
304 final String message = getLocalizedMessage(
305 AbstractAutomaticBean.class,
306 "AbstractAutomaticBean.disallowedChild", childConf.getName(),
307 configuration.getName());
308 throw new CheckstyleException(message);
309 }
310 }
311
312
313
314
315
316
317
318
319
320 private static String getLocalizedMessage(Class<?> caller,
321 String messageKey, Object... args) {
322 final LocalizedMessage localizedMessage = new LocalizedMessage(
323 Definitions.CHECKSTYLE_BUNDLE, caller,
324 messageKey, args);
325
326 return localizedMessage.getMessage();
327 }
328
329
330 private static final class PatternConverter implements Converter {
331
332 @Override
333 @SuppressWarnings("unchecked")
334 public Object convert(Class type, Object value) {
335 return CommonUtil.createPattern(value.toString());
336 }
337
338 }
339
340
341 private static final class PatternArrayConverter implements Converter {
342
343 @Override
344 @SuppressWarnings("unchecked")
345 public Object convert(Class type, Object value) {
346 final StringTokenizer tokenizer = new StringTokenizer(
347 value.toString(), COMMA_SEPARATOR);
348 final List<Pattern> result = new ArrayList<>();
349
350 while (tokenizer.hasMoreTokens()) {
351 final String token = tokenizer.nextToken();
352 result.add(CommonUtil.createPattern(token.trim()));
353 }
354
355 return result.toArray(new Pattern[0]);
356 }
357 }
358
359
360 private static final class SeverityLevelConverter implements Converter {
361
362 @Override
363 @SuppressWarnings("unchecked")
364 public Object convert(Class type, Object value) {
365 return SeverityLevel.getInstance(value.toString());
366 }
367
368 }
369
370
371 private static final class ScopeConverter implements Converter {
372
373 @Override
374 @SuppressWarnings("unchecked")
375 public Object convert(Class type, Object value) {
376 return Scope.getInstance(value.toString());
377 }
378
379 }
380
381
382 private static final class UriConverter implements Converter {
383
384 @Nullable
385 @Override
386 @SuppressWarnings("unchecked")
387 public Object convert(Class type, Object value) {
388 final String url = value.toString();
389 URI result = null;
390
391 if (!CommonUtil.isBlank(url)) {
392 try {
393 result = CommonUtil.getUriByFilename(url);
394 }
395 catch (CheckstyleException exc) {
396 throw new IllegalArgumentException(exc);
397 }
398 }
399
400 return result;
401 }
402
403 }
404
405
406
407
408
409
410 private static final class RelaxedStringArrayConverter implements Converter {
411
412 @Override
413 @SuppressWarnings("unchecked")
414 public Object convert(Class type, Object value) {
415 final StringTokenizer tokenizer = new StringTokenizer(
416 value.toString().trim(), COMMA_SEPARATOR);
417 final List<String> result = new ArrayList<>();
418
419 while (tokenizer.hasMoreTokens()) {
420 final String token = tokenizer.nextToken();
421 result.add(token.trim());
422 }
423
424 return result.toArray(CommonUtil.EMPTY_STRING_ARRAY);
425 }
426
427 }
428
429
430
431
432
433
434 private static final class RelaxedAccessModifierArrayConverter implements Converter {
435
436
437 private static final AccessModifierOption[] EMPTY_MODIFIER_ARRAY =
438 new AccessModifierOption[0];
439
440 @Override
441 @SuppressWarnings("unchecked")
442 public Object convert(Class type, Object value) {
443
444 final StringTokenizer tokenizer = new StringTokenizer(
445 value.toString().trim(), COMMA_SEPARATOR);
446 final List<AccessModifierOption> result = new ArrayList<>();
447
448 while (tokenizer.hasMoreTokens()) {
449 final String token = tokenizer.nextToken();
450 result.add(AccessModifierOption.getInstance(token));
451 }
452
453 return result.toArray(EMPTY_MODIFIER_ARRAY);
454 }
455
456 }
457
458 }