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