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