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