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 SeverityLevelConverter(), SeverityLevel.class);
174 cub.register(new ScopeConverter(), Scope.class);
175 cub.register(new UriConverter(), URI.class);
176 cub.register(new RelaxedAccessModifierArrayConverter(), AccessModifierOption[].class);
177 }
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192 @Override
193 public final void configure(Configuration config)
194 throws CheckstyleException {
195 configuration = config;
196
197 final String[] attributes = config.getPropertyNames();
198
199 for (final String key : attributes) {
200 final String value = config.getProperty(key);
201
202 tryCopyProperty(key, value, true);
203 }
204
205 finishLocalSetup();
206
207 final Configuration[] childConfigs = config.getChildren();
208 for (final Configuration childConfig : childConfigs) {
209 setupChild(childConfig);
210 }
211 }
212
213
214
215
216
217
218
219
220
221 private void tryCopyProperty(String key, Object value, boolean recheck)
222 throws CheckstyleException {
223 final BeanUtilsBean beanUtils = createBeanUtilsBean();
224
225 try {
226 if (recheck) {
227
228
229
230 final PropertyDescriptor descriptor =
231 PropertyUtils.getPropertyDescriptor(this, key);
232 if (descriptor == null) {
233 final String message = String.format(Locale.ROOT, "Property '%s' "
234 + "does not exist, please check the documentation", key);
235 throw new CheckstyleException(message);
236 }
237 }
238
239 beanUtils.copyProperty(this, key, value);
240 }
241 catch (final InvocationTargetException | IllegalAccessException
242 | NoSuchMethodException ex) {
243
244
245
246
247 final String message = String.format(Locale.ROOT,
248 "Cannot set property '%s' to '%s'", key, value);
249 throw new CheckstyleException(message, ex);
250 }
251 catch (final IllegalArgumentException | ConversionException ex) {
252 final String message = String.format(Locale.ROOT, "illegal value '%s' for property "
253 + "'%s'", value, key);
254 throw new CheckstyleException(message, ex);
255 }
256 }
257
258
259
260
261
262
263 @Override
264 public final void contextualize(Context context)
265 throws CheckstyleException {
266 final Collection<String> attributes = context.getAttributeNames();
267
268 for (final String key : attributes) {
269 final Object value = context.get(key);
270
271 tryCopyProperty(key, value, false);
272 }
273 }
274
275
276
277
278
279
280 protected final Configuration getConfiguration() {
281 return configuration;
282 }
283
284
285
286
287
288
289
290
291
292
293
294
295
296 protected void setupChild(Configuration childConf)
297 throws CheckstyleException {
298 if (childConf != null) {
299 throw new CheckstyleException(childConf.getName() + " is not allowed as a child in "
300 + configuration.getName() + ". Please review 'Parent Module' section "
301 + "for this Check in web documentation if Check is standard.");
302 }
303 }
304
305
306 private static final class PatternConverter implements Converter {
307
308 @SuppressWarnings("unchecked")
309 @Override
310 public Object convert(Class type, Object value) {
311 return CommonUtil.createPattern(value.toString());
312 }
313
314 }
315
316
317 private static final class SeverityLevelConverter implements Converter {
318
319 @SuppressWarnings("unchecked")
320 @Override
321 public Object convert(Class type, Object value) {
322 return SeverityLevel.getInstance(value.toString());
323 }
324
325 }
326
327
328 private static final class ScopeConverter implements Converter {
329
330 @SuppressWarnings("unchecked")
331 @Override
332 public Object convert(Class type, Object value) {
333 return Scope.getInstance(value.toString());
334 }
335
336 }
337
338
339 private static final class UriConverter implements Converter {
340
341 @SuppressWarnings("unchecked")
342 @Override
343 @Nullable
344 public Object convert(Class type, Object value) {
345 final String url = value.toString();
346 URI result = null;
347
348 if (!CommonUtil.isBlank(url)) {
349 try {
350 result = CommonUtil.getUriByFilename(url);
351 }
352 catch (CheckstyleException ex) {
353 throw new IllegalArgumentException(ex);
354 }
355 }
356
357 return result;
358 }
359
360 }
361
362
363
364
365
366
367 private static final class RelaxedStringArrayConverter implements Converter {
368
369 @SuppressWarnings("unchecked")
370 @Override
371 public Object convert(Class type, Object value) {
372 final StringTokenizer tokenizer = new StringTokenizer(
373 value.toString().trim(), COMMA_SEPARATOR);
374 final List<String> result = new ArrayList<>();
375
376 while (tokenizer.hasMoreTokens()) {
377 final String token = tokenizer.nextToken();
378 result.add(token.trim());
379 }
380
381 return result.toArray(CommonUtil.EMPTY_STRING_ARRAY);
382 }
383
384 }
385
386
387
388
389
390
391 private static final class RelaxedAccessModifierArrayConverter implements Converter {
392
393
394 private static final AccessModifierOption[] EMPTY_MODIFIER_ARRAY =
395 new AccessModifierOption[0];
396
397 @SuppressWarnings("unchecked")
398 @Override
399 public Object convert(Class type, Object value) {
400
401 final StringTokenizer tokenizer = new StringTokenizer(
402 value.toString().trim(), COMMA_SEPARATOR);
403 final List<AccessModifierOption> result = new ArrayList<>();
404
405 while (tokenizer.hasMoreTokens()) {
406 final String token = tokenizer.nextToken();
407 result.add(AccessModifierOption.getInstance(token));
408 }
409
410 return result.toArray(EMPTY_MODIFIER_ARRAY);
411 }
412
413 }
414
415 }