Coverage Report - com.puppycrawl.tools.checkstyle.api.AutomaticBean
 
Classes in this File Line Coverage Branch Coverage Complexity
AutomaticBean
100%
75/75
100%
12/12
2.133
AutomaticBean$1
N/A
N/A
2.133
AutomaticBean$OutputStreamOptions
100%
3/3
N/A
2.133
AutomaticBean$PatternConverter
100%
2/2
N/A
2.133
AutomaticBean$RelaxedAccessModifierArrayConverter
100%
9/9
100%
2/2
2.133
AutomaticBean$RelaxedStringArrayConverter
100%
9/9
100%
2/2
2.133
AutomaticBean$ScopeConverter
100%
2/2
N/A
2.133
AutomaticBean$SeverityLevelConverter
100%
2/2
N/A
2.133
AutomaticBean$UriConverter
100%
9/9
100%
2/2
2.133
 
 1  
 ////////////////////////////////////////////////////////////////////////////////
 2  
 // checkstyle: Checks Java source code for adherence to a set of rules.
 3  
 // Copyright (C) 2001-2017 the original author or authors.
 4  
 //
 5  
 // This library is free software; you can redistribute it and/or
 6  
 // modify it under the terms of the GNU Lesser General Public
 7  
 // License as published by the Free Software Foundation; either
 8  
 // version 2.1 of the License, or (at your option) any later version.
 9  
 //
 10  
 // This library is distributed in the hope that it will be useful,
 11  
 // but WITHOUT ANY WARRANTY; without even the implied warranty of
 12  
 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 13  
 // Lesser General Public License for more details.
 14  
 //
 15  
 // You should have received a copy of the GNU Lesser General Public
 16  
 // License along with this library; if not, write to the Free Software
 17  
 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 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.CommonUtils;
 50  
 
 51  
 /**
 52  
  * A Java Bean that implements the component lifecycle interfaces by
 53  
  * calling the bean's setters for all configuration attributes.
 54  
  * @author lkuehne
 55  
  */
 56  8686
 public class AutomaticBean
 57  
     implements Configurable, Contextualizable {
 58  
 
 59  
     /**
 60  
      * Enum to specify behaviour regarding ignored modules.
 61  
      */
 62  12
     public enum OutputStreamOptions {
 63  
         /**
 64  
          * Close stream in the end.
 65  
          */
 66  4
         CLOSE,
 67  
 
 68  
         /**
 69  
          * Do nothing in the end.
 70  
          */
 71  4
         NONE
 72  
     }
 73  
 
 74  
     /** Comma separator for StringTokenizer. */
 75  
     private static final String COMMA_SEPARATOR = ",";
 76  
 
 77  
     /** The configuration of this bean. */
 78  
     private Configuration configuration;
 79  
 
 80  
     /**
 81  
      * Creates a BeanUtilsBean that is configured to use
 82  
      * type converters that throw a ConversionException
 83  
      * instead of using the default value when something
 84  
      * goes wrong.
 85  
      *
 86  
      * @return a configured BeanUtilsBean
 87  
      */
 88  
     private static BeanUtilsBean createBeanUtilsBean() {
 89  19861
         final ConvertUtilsBean cub = new ConvertUtilsBean();
 90  
 
 91  19861
         registerIntegralTypes(cub);
 92  19861
         registerCustomTypes(cub);
 93  
 
 94  19861
         return new BeanUtilsBean(cub, new PropertyUtilsBean());
 95  
     }
 96  
 
 97  
     /**
 98  
      * Register basic types of JDK like boolean, int, and String to use with BeanUtils. All these
 99  
      * types are found in the {@code java.lang} package.
 100  
      * @param cub
 101  
      *            Instance of {@link ConvertUtilsBean} to register types with.
 102  
      */
 103  
     private static void registerIntegralTypes(ConvertUtilsBean cub) {
 104  19862
         cub.register(new BooleanConverter(), Boolean.TYPE);
 105  19862
         cub.register(new BooleanConverter(), Boolean.class);
 106  19862
         cub.register(new ArrayConverter(
 107  
             boolean[].class, new BooleanConverter()), boolean[].class);
 108  19862
         cub.register(new ByteConverter(), Byte.TYPE);
 109  19862
         cub.register(new ByteConverter(), Byte.class);
 110  19862
         cub.register(new ArrayConverter(byte[].class, new ByteConverter()),
 111  
             byte[].class);
 112  19862
         cub.register(new CharacterConverter(), Character.TYPE);
 113  19862
         cub.register(new CharacterConverter(), Character.class);
 114  19862
         cub.register(new ArrayConverter(char[].class, new CharacterConverter()),
 115  
             char[].class);
 116  19862
         cub.register(new DoubleConverter(), Double.TYPE);
 117  19862
         cub.register(new DoubleConverter(), Double.class);
 118  19862
         cub.register(new ArrayConverter(double[].class, new DoubleConverter()),
 119  
             double[].class);
 120  19862
         cub.register(new FloatConverter(), Float.TYPE);
 121  19862
         cub.register(new FloatConverter(), Float.class);
 122  19862
         cub.register(new ArrayConverter(float[].class, new FloatConverter()),
 123  
             float[].class);
 124  19862
         cub.register(new IntegerConverter(), Integer.TYPE);
 125  19862
         cub.register(new IntegerConverter(), Integer.class);
 126  19862
         cub.register(new ArrayConverter(int[].class, new IntegerConverter()),
 127  
             int[].class);
 128  19862
         cub.register(new LongConverter(), Long.TYPE);
 129  19862
         cub.register(new LongConverter(), Long.class);
 130  19862
         cub.register(new ArrayConverter(long[].class, new LongConverter()),
 131  
             long[].class);
 132  19862
         cub.register(new ShortConverter(), Short.TYPE);
 133  19862
         cub.register(new ShortConverter(), Short.class);
 134  19862
         cub.register(new ArrayConverter(short[].class, new ShortConverter()),
 135  
             short[].class);
 136  19862
         cub.register(new RelaxedStringArrayConverter(), String[].class);
 137  
 
 138  
         // BigDecimal, BigInteger, Class, Date, String, Time, TimeStamp
 139  
         // do not use defaults in the default configuration of ConvertUtilsBean
 140  19862
     }
 141  
 
 142  
     /**
 143  
      * Register custom types of JDK like URI and Checkstyle specific classes to use with BeanUtils.
 144  
      * None of these types should be found in the {@code java.lang} package.
 145  
      * @param cub
 146  
      *            Instance of {@link ConvertUtilsBean} to register types with.
 147  
      */
 148  
     private static void registerCustomTypes(ConvertUtilsBean cub) {
 149  19861
         cub.register(new PatternConverter(), Pattern.class);
 150  19861
         cub.register(new SeverityLevelConverter(), SeverityLevel.class);
 151  19861
         cub.register(new ScopeConverter(), Scope.class);
 152  19861
         cub.register(new UriConverter(), URI.class);
 153  19861
         cub.register(new RelaxedAccessModifierArrayConverter(), AccessModifier[].class);
 154  19861
     }
 155  
 
 156  
     /**
 157  
      * Implements the Configurable interface using bean introspection.
 158  
      *
 159  
      * <p>Subclasses are allowed to add behaviour. After the bean
 160  
      * based setup has completed first the method
 161  
      * {@link #finishLocalSetup finishLocalSetup}
 162  
      * is called to allow completion of the bean's local setup,
 163  
      * after that the method {@link #setupChild setupChild}
 164  
      * is called for each {@link Configuration#getChildren child Configuration}
 165  
      * of {@code configuration}.
 166  
      *
 167  
      * @see Configurable
 168  
      */
 169  
     @Override
 170  
     public final void configure(Configuration config)
 171  
             throws CheckstyleException {
 172  5953
         configuration = config;
 173  
 
 174  5953
         final String[] attributes = config.getAttributeNames();
 175  
 
 176  9988
         for (final String key : attributes) {
 177  4067
             final String value = config.getAttribute(key);
 178  
 
 179  4067
             tryCopyProperty(config.getName(), key, value, true);
 180  
         }
 181  
 
 182  5921
         finishLocalSetup();
 183  
 
 184  5920
         final Configuration[] childConfigs = config.getChildren();
 185  9783
         for (final Configuration childConfig : childConfigs) {
 186  3937
             setupChild(childConfig);
 187  
         }
 188  5846
     }
 189  
 
 190  
     /**
 191  
      * Recheck property and try to copy it.
 192  
      * @param moduleName name of the module/class
 193  
      * @param key key of value
 194  
      * @param value value
 195  
      * @param recheck whether to check for property existence before copy
 196  
      * @throws CheckstyleException then property defined incorrectly
 197  
      */
 198  
     private void tryCopyProperty(String moduleName, String key, Object value, boolean recheck)
 199  
             throws CheckstyleException {
 200  
 
 201  19861
         final BeanUtilsBean beanUtils = createBeanUtilsBean();
 202  
 
 203  
         try {
 204  19861
             if (recheck) {
 205  
                 // BeanUtilsBean.copyProperties silently ignores missing setters
 206  
                 // for key, so we have to go through great lengths here to
 207  
                 // figure out if the bean property really exists.
 208  4067
                 final PropertyDescriptor descriptor =
 209  4067
                         PropertyUtils.getPropertyDescriptor(this, key);
 210  4067
                 if (descriptor == null) {
 211  2
                     final String message = String.format(Locale.ROOT, "Property '%s' in module %s "
 212  
                             + "does not exist, please check the documentation", key, moduleName);
 213  2
                     throw new CheckstyleException(message);
 214  
                 }
 215  
             }
 216  
             // finally we can set the bean property
 217  19859
             beanUtils.copyProperty(this, key, value);
 218  
         }
 219  26
         catch (final InvocationTargetException | IllegalAccessException
 220  
                 | NoSuchMethodException ex) {
 221  
             // There is no way to catch IllegalAccessException | NoSuchMethodException
 222  
             // as we do PropertyUtils.getPropertyDescriptor before beanUtils.copyProperty
 223  
             // so we have to join these exceptions with InvocationTargetException
 224  
             // to satisfy UTs coverage
 225  26
             final String message = String.format(Locale.ROOT,
 226  
                     "Cannot set property '%s' to '%s' in module %s", key, value, moduleName);
 227  26
             throw new CheckstyleException(message, ex);
 228  
         }
 229  6
         catch (final IllegalArgumentException | ConversionException ex) {
 230  6
             final String message = String.format(Locale.ROOT, "illegal value '%s' for property "
 231  
                     + "'%s' of module %s", value, key, moduleName);
 232  6
             throw new CheckstyleException(message, ex);
 233  19827
         }
 234  19827
     }
 235  
 
 236  
     /**
 237  
      * Implements the Contextualizable interface using bean introspection.
 238  
      * @see Contextualizable
 239  
      */
 240  
     @Override
 241  
     public final void contextualize(Context context)
 242  
             throws CheckstyleException {
 243  
 
 244  3942
         final Collection<String> attributes = context.getAttributeNames();
 245  
 
 246  3942
         for (final String key : attributes) {
 247  15794
             final Object value = context.get(key);
 248  
 
 249  15794
             tryCopyProperty(getClass().getName(), key, value, false);
 250  15792
         }
 251  3940
     }
 252  
 
 253  
     /**
 254  
      * Returns the configuration that was used to configure this component.
 255  
      * @return the configuration that was used to configure this component.
 256  
      */
 257  
     protected final Configuration getConfiguration() {
 258  72289
         return configuration;
 259  
     }
 260  
 
 261  
     /**
 262  
      * Provides a hook to finish the part of this component's setup that
 263  
      * was not handled by the bean introspection.
 264  
      * <p>
 265  
      * The default implementation does nothing.
 266  
      * </p>
 267  
      * @throws CheckstyleException if there is a configuration error.
 268  
      */
 269  
     protected void finishLocalSetup() throws CheckstyleException {
 270  
         // No code by default, should be overridden only by demand at subclasses
 271  2084
     }
 272  
 
 273  
     /**
 274  
      * Called by configure() for every child of this component's Configuration.
 275  
      * <p>
 276  
      * The default implementation throws {@link CheckstyleException} if
 277  
      * {@code childConf} is {@code null} because it doesn't support children. It
 278  
      * must be overridden to validate and support children that are wanted.
 279  
      * </p>
 280  
      *
 281  
      * @param childConf a child of this component's Configuration
 282  
      * @throws CheckstyleException if there is a configuration error.
 283  
      * @see Configuration#getChildren
 284  
      */
 285  
     protected void setupChild(Configuration childConf)
 286  
             throws CheckstyleException {
 287  5
         if (childConf != null) {
 288  4
             throw new CheckstyleException(childConf.getName() + " is not allowed as a child in "
 289  4
                     + configuration.getName() + ". Please review 'Parent Module' section "
 290  
                     + "for this Check in web documentation if Check is standard.");
 291  
         }
 292  1
     }
 293  
 
 294  
     /** A converter that converts strings to patterns. */
 295  39722
     private static class PatternConverter implements Converter {
 296  
         @SuppressWarnings({"unchecked", "rawtypes"})
 297  
         @Override
 298  
         public Object convert(Class type, Object value) {
 299  293
             return CommonUtils.createPattern(value.toString());
 300  
         }
 301  
     }
 302  
 
 303  
     /** A converter that converts strings to severity level. */
 304  39722
     private static class SeverityLevelConverter implements Converter {
 305  
         @SuppressWarnings({"unchecked", "rawtypes"})
 306  
         @Override
 307  
         public Object convert(Class type, Object value) {
 308  6
             return SeverityLevel.getInstance(value.toString());
 309  
         }
 310  
     }
 311  
 
 312  
     /** A converter that converts strings to scope. */
 313  39722
     private static class ScopeConverter implements Converter {
 314  
         @SuppressWarnings({"unchecked", "rawtypes"})
 315  
         @Override
 316  
         public Object convert(Class type, Object value) {
 317  41
             return Scope.getInstance(value.toString());
 318  
         }
 319  
     }
 320  
 
 321  
     /** A converter that converts strings to uri. */
 322  39722
     private static class UriConverter implements Converter {
 323  
         @SuppressWarnings({"unchecked", "rawtypes"})
 324  
         @Override
 325  
         public Object convert(Class type, Object value) {
 326  60
             final String url = value.toString();
 327  60
             URI result = null;
 328  
 
 329  60
             if (!CommonUtils.isBlank(url)) {
 330  
                 try {
 331  57
                     result = CommonUtils.getUriByFilename(url);
 332  
                 }
 333  3
                 catch (CheckstyleException ex) {
 334  3
                     throw new IllegalArgumentException(ex);
 335  54
                 }
 336  
             }
 337  
 
 338  57
             return result;
 339  
         }
 340  
     }
 341  
 
 342  
     /**
 343  
      * A converter that does not care whether the array elements contain String
 344  
      * characters like '*' or '_'. The normal ArrayConverter class has problems
 345  
      * with this characters.
 346  
      */
 347  39724
     private static class RelaxedStringArrayConverter implements Converter {
 348  
         @SuppressWarnings({"unchecked", "rawtypes"})
 349  
         @Override
 350  
         public Object convert(Class type, Object value) {
 351  
             // Convert to a String and trim it for the tokenizer.
 352  450
             final StringTokenizer tokenizer = new StringTokenizer(
 353  450
                 value.toString().trim(), COMMA_SEPARATOR);
 354  450
             final List<String> result = new ArrayList<>();
 355  
 
 356  1424
             while (tokenizer.hasMoreTokens()) {
 357  974
                 final String token = tokenizer.nextToken();
 358  974
                 result.add(token.trim());
 359  974
             }
 360  
 
 361  450
             return result.toArray(new String[result.size()]);
 362  
         }
 363  
     }
 364  
 
 365  
     /**
 366  
      * A converter that converts strings to {@link AccessModifier}.
 367  
      * This implementation does not care whether the array elements contain characters like '_'.
 368  
      * The normal {@link ArrayConverter} class has problems with this character.
 369  
      */
 370  39722
     private static class RelaxedAccessModifierArrayConverter implements Converter {
 371  
 
 372  
         @SuppressWarnings({"unchecked", "rawtypes"})
 373  
         @Override
 374  
         public Object convert(Class type, Object value) {
 375  
             // Converts to a String and trims it for the tokenizer.
 376  3
             final StringTokenizer tokenizer = new StringTokenizer(
 377  3
                 value.toString().trim(), COMMA_SEPARATOR);
 378  3
             final List<AccessModifier> result = new ArrayList<>();
 379  
 
 380  8
             while (tokenizer.hasMoreTokens()) {
 381  5
                 final String token = tokenizer.nextToken();
 382  5
                 result.add(AccessModifier.getInstance(token.trim()));
 383  5
             }
 384  
 
 385  3
             return result.toArray(new AccessModifier[result.size()]);
 386  
         }
 387  
     }
 388  
 }