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 exc) {
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, exc);
252         }
253         catch (final IllegalArgumentException | ConversionException exc) {
254             final String message = String.format(Locale.ROOT, "illegal value '%s' for property "
255                     + "'%s'", value, key);
256             throw new CheckstyleException(message, exc);
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         @Override
312         @SuppressWarnings("unchecked")
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         @Override
323         @SuppressWarnings("unchecked")
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         @Override
342         @SuppressWarnings("unchecked")
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         @Override
353         @SuppressWarnings("unchecked")
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         @Nullable
364         @Override
365         @SuppressWarnings("unchecked")
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 exc) {
375                     throw new IllegalArgumentException(exc);
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         @Override
392         @SuppressWarnings("unchecked")
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         @Override
420         @SuppressWarnings("unchecked")
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 }