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