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 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 = String.format(Locale.ROOT, "Property '%s' "
235 + "does not exist, please check the documentation", key);
236 throw new CheckstyleException(message);
237 }
238 }
239
240 beanUtils.copyProperty(this, key, value);
241 }
242 catch (final InvocationTargetException | IllegalAccessException
243 | NoSuchMethodException ex) {
244
245
246
247
248 final String message = String.format(Locale.ROOT,
249 "Cannot set property '%s' to '%s'", key, value);
250 throw new CheckstyleException(message, ex);
251 }
252 catch (final IllegalArgumentException | ConversionException ex) {
253 final String message = String.format(Locale.ROOT, "illegal value '%s' for property "
254 + "'%s'", value, key);
255 throw new CheckstyleException(message, ex);
256 }
257 }
258
259
260
261
262
263
264 @Override
265 public final void contextualize(Context context)
266 throws CheckstyleException {
267 final Collection<String> attributes = context.getAttributeNames();
268
269 for (final String key : attributes) {
270 final Object value = context.get(key);
271
272 tryCopyProperty(key, value, false);
273 }
274 }
275
276
277
278
279
280
281 protected final Configuration getConfiguration() {
282 return configuration;
283 }
284
285
286
287
288
289
290
291
292
293
294
295
296
297 protected void setupChild(Configuration childConf)
298 throws CheckstyleException {
299 if (childConf != null) {
300 throw new CheckstyleException(childConf.getName() + " is not allowed as a child in "
301 + configuration.getName() + ". Please review 'Parent Module' section "
302 + "for this Check in web documentation if Check is standard.");
303 }
304 }
305
306
307 private static final class PatternConverter implements Converter {
308
309 @SuppressWarnings("unchecked")
310 @Override
311 public Object convert(Class type, Object value) {
312 return CommonUtil.createPattern(value.toString());
313 }
314
315 }
316
317
318 private static final class PatternArrayConverter implements Converter {
319
320 @SuppressWarnings("unchecked")
321 @Override
322 public Object convert(Class type, Object value) {
323 final StringTokenizer tokenizer = new StringTokenizer(
324 value.toString(), COMMA_SEPARATOR);
325 final List<Pattern> result = new ArrayList<>();
326
327 while (tokenizer.hasMoreTokens()) {
328 final String token = tokenizer.nextToken();
329 result.add(CommonUtil.createPattern(token.trim()));
330 }
331
332 return result.toArray(new Pattern[0]);
333 }
334 }
335
336
337 private static final class SeverityLevelConverter implements Converter {
338
339 @SuppressWarnings("unchecked")
340 @Override
341 public Object convert(Class type, Object value) {
342 return SeverityLevel.getInstance(value.toString());
343 }
344
345 }
346
347
348 private static final class ScopeConverter implements Converter {
349
350 @SuppressWarnings("unchecked")
351 @Override
352 public Object convert(Class type, Object value) {
353 return Scope.getInstance(value.toString());
354 }
355
356 }
357
358
359 private static final class UriConverter implements Converter {
360
361 @SuppressWarnings("unchecked")
362 @Override
363 @Nullable
364 public Object convert(Class type, Object value) {
365 final String url = value.toString();
366 URI result = null;
367
368 if (!CommonUtil.isBlank(url)) {
369 try {
370 result = CommonUtil.getUriByFilename(url);
371 }
372 catch (CheckstyleException ex) {
373 throw new IllegalArgumentException(ex);
374 }
375 }
376
377 return result;
378 }
379
380 }
381
382
383
384
385
386
387 private static final class RelaxedStringArrayConverter implements Converter {
388
389 @SuppressWarnings("unchecked")
390 @Override
391 public Object convert(Class type, Object value) {
392 final StringTokenizer tokenizer = new StringTokenizer(
393 value.toString().trim(), COMMA_SEPARATOR);
394 final List<String> result = new ArrayList<>();
395
396 while (tokenizer.hasMoreTokens()) {
397 final String token = tokenizer.nextToken();
398 result.add(token.trim());
399 }
400
401 return result.toArray(CommonUtil.EMPTY_STRING_ARRAY);
402 }
403
404 }
405
406
407
408
409
410
411 private static final class RelaxedAccessModifierArrayConverter implements Converter {
412
413
414 private static final AccessModifierOption[] EMPTY_MODIFIER_ARRAY =
415 new AccessModifierOption[0];
416
417 @SuppressWarnings("unchecked")
418 @Override
419 public Object convert(Class type, Object value) {
420
421 final StringTokenizer tokenizer = new StringTokenizer(
422 value.toString().trim(), COMMA_SEPARATOR);
423 final List<AccessModifierOption> result = new ArrayList<>();
424
425 while (tokenizer.hasMoreTokens()) {
426 final String token = tokenizer.nextToken();
427 result.add(AccessModifierOption.getInstance(token));
428 }
429
430 return result.toArray(EMPTY_MODIFIER_ARRAY);
431 }
432
433 }
434
435 }