Coverage Report - com.puppycrawl.tools.checkstyle.api.AutomaticBean
 
Classes in this File Line Coverage Branch Coverage Complexity
AutomaticBean
79%
58/73
90%
9/10
4.857
AutomaticBean$1
N/A
N/A
4.857
AutomaticBean$RelaxedStringArrayConverter
90%
9/10
75%
3/4
4.857
 
 1  
 ////////////////////////////////////////////////////////////////////////////////
 2  
 // checkstyle: Checks Java source code for adherence to a set of rules.
 3  
 // Copyright (C) 2001-2014  Oliver Burn
 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  
 package com.puppycrawl.tools.checkstyle.api;
 20  
 
 21  
 import com.google.common.collect.Lists;
 22  
 import java.beans.PropertyDescriptor;
 23  
 import java.lang.reflect.InvocationTargetException;
 24  
 import java.util.Collection;
 25  
 import java.util.List;
 26  
 import java.util.StringTokenizer;
 27  
 import org.apache.commons.beanutils.BeanUtilsBean;
 28  
 import org.apache.commons.beanutils.ConversionException;
 29  
 import org.apache.commons.beanutils.ConvertUtilsBean;
 30  
 import org.apache.commons.beanutils.Converter;
 31  
 import org.apache.commons.beanutils.PropertyUtils;
 32  
 import org.apache.commons.beanutils.PropertyUtilsBean;
 33  
 import org.apache.commons.beanutils.converters.ArrayConverter;
 34  
 import org.apache.commons.beanutils.converters.BooleanConverter;
 35  
 import org.apache.commons.beanutils.converters.ByteConverter;
 36  
 import org.apache.commons.beanutils.converters.CharacterConverter;
 37  
 import org.apache.commons.beanutils.converters.DoubleConverter;
 38  
 import org.apache.commons.beanutils.converters.FloatConverter;
 39  
 import org.apache.commons.beanutils.converters.IntegerConverter;
 40  
 import org.apache.commons.beanutils.converters.LongConverter;
 41  
 import org.apache.commons.beanutils.converters.ShortConverter;
 42  
 
 43  
 /**
 44  
  * A Java Bean that implements the component lifecycle interfaces by
 45  
  * calling the bean's setters for all configuration attributes.
 46  
  * @author lkuehne
 47  
  */
 48  2419
 public class AutomaticBean
 49  
     implements Configurable, Contextualizable
 50  
 {
 51  
     /** the configuration of this bean */
 52  
     private Configuration mConfiguration;
 53  
 
 54  
 
 55  
     /**
 56  
      * Creates a BeanUtilsBean that is configured to use
 57  
      * type converters that throw a ConversionException
 58  
      * instead of using the default value when something
 59  
      * goes wrong.
 60  
      *
 61  
      * @return a configured BeanUtilsBean
 62  
      */
 63  
     private static BeanUtilsBean createBeanUtilsBean()
 64  
     {
 65  3026
         final ConvertUtilsBean cub = new ConvertUtilsBean();
 66  
         // TODO: is there a smarter way to tell beanutils not to use defaults?
 67  3026
         cub.register(new BooleanConverter(), Boolean.TYPE);
 68  3026
         cub.register(new BooleanConverter(), Boolean.class);
 69  3026
         cub.register(new ArrayConverter(
 70  
             boolean[].class, new BooleanConverter()), boolean[].class);
 71  3026
         cub.register(new ByteConverter(), Byte.TYPE);
 72  3026
         cub.register(new ByteConverter(), Byte.class);
 73  3026
         cub.register(new ArrayConverter(byte[].class, new ByteConverter()),
 74  
             byte[].class);
 75  3026
         cub.register(new CharacterConverter(), Character.TYPE);
 76  3026
         cub.register(new CharacterConverter(), Character.class);
 77  3026
         cub.register(new ArrayConverter(char[].class, new CharacterConverter()),
 78  
             char[].class);
 79  3026
         cub.register(new DoubleConverter(), Double.TYPE);
 80  3026
         cub.register(new DoubleConverter(), Double.class);
 81  3026
         cub.register(new ArrayConverter(double[].class, new DoubleConverter()),
 82  
             double[].class);
 83  3026
         cub.register(new FloatConverter(), Float.TYPE);
 84  3026
         cub.register(new FloatConverter(), Float.class);
 85  3026
         cub.register(new ArrayConverter(float[].class, new FloatConverter()),
 86  
             float[].class);
 87  3026
         cub.register(new IntegerConverter(), Integer.TYPE);
 88  3026
         cub.register(new IntegerConverter(), Integer.class);
 89  3026
         cub.register(new ArrayConverter(int[].class, new IntegerConverter()),
 90  
             int[].class);
 91  3026
         cub.register(new LongConverter(), Long.TYPE);
 92  3026
         cub.register(new LongConverter(), Long.class);
 93  3026
         cub.register(new ArrayConverter(long[].class, new LongConverter()),
 94  
             long[].class);
 95  3026
         cub.register(new ShortConverter(), Short.TYPE);
 96  3026
         cub.register(new ShortConverter(), Short.class);
 97  3026
         cub.register(new ArrayConverter(short[].class, new ShortConverter()),
 98  
             short[].class);
 99  3026
         cub.register(new RelaxedStringArrayConverter(), String[].class);
 100  
 
 101  
         // BigDecimal, BigInteger, Class, Date, String, Time, TimeStamp
 102  
         // do not use defaults in the default configuration of ConvertUtilsBean
 103  
 
 104  3026
         return new BeanUtilsBean(cub, new PropertyUtilsBean());
 105  
     }
 106  
 
 107  
     /**
 108  
      * Implements the Configurable interface using bean introspection.
 109  
      *
 110  
      * Subclasses are allowed to add behaviour. After the bean
 111  
      * based setup has completed first the method
 112  
      * {@link #finishLocalSetup finishLocalSetup}
 113  
      * is called to allow completion of the bean's local setup,
 114  
      * after that the method {@link #setupChild setupChild}
 115  
      * is called for each {@link Configuration#getChildren child Configuration}
 116  
      * of <code>aConfiguration</code>.
 117  
      *
 118  
      * @param aConfiguration {@inheritDoc}
 119  
      * @throws CheckstyleException {@inheritDoc}
 120  
      * @see Configurable
 121  
      */
 122  
     public final void configure(Configuration aConfiguration)
 123  
         throws CheckstyleException
 124  
     {
 125  1812
         mConfiguration = aConfiguration;
 126  
 
 127  1812
         final BeanUtilsBean beanUtils = createBeanUtilsBean();
 128  
 
 129  
         // TODO: debug log messages
 130  1812
         final String[] attributes = aConfiguration.getAttributeNames();
 131  
 
 132  2959
         for (final String key : attributes) {
 133  1155
             final String value = aConfiguration.getAttribute(key);
 134  
 
 135  
             try {
 136  
                 // BeanUtilsBean.copyProperties silently ignores missing setters
 137  
                 // for key, so we have to go through great lengths here to
 138  
                 // figure out if the bean property really exists.
 139  1155
                 final PropertyDescriptor pd =
 140  
                     PropertyUtils.getPropertyDescriptor(this, key);
 141  1155
                 if ((pd == null) || (pd.getWriteMethod() == null)) {
 142  1
                     throw new CheckstyleException(
 143  
                         "Property '" + key + "' in module "
 144  
                         + aConfiguration.getName()
 145  
                         + " does not exist, please check the documentation");
 146  
                 }
 147  
 
 148  
                 // finally we can set the bean property
 149  1154
                 beanUtils.copyProperty(this, key, value);
 150  
             }
 151  6
             catch (final InvocationTargetException e) {
 152  6
                 throw new CheckstyleException(
 153  
                     "Cannot set property '" + key + "' in module "
 154  
                     + aConfiguration.getName() + " to '" + value
 155  
                     + "': " + e.getTargetException().getMessage(), e);
 156  
             }
 157  0
             catch (final IllegalAccessException e) {
 158  0
                 throw new CheckstyleException(
 159  
                     "cannot access " + key + " in "
 160  
                     + this.getClass().getName(), e);
 161  
             }
 162  0
             catch (final NoSuchMethodException e) {
 163  0
                 throw new CheckstyleException(
 164  
                     "cannot access " + key + " in "
 165  
                     + this.getClass().getName(), e);
 166  
             }
 167  0
             catch (final IllegalArgumentException e) {
 168  0
                 throw new CheckstyleException(
 169  
                     "illegal value '" + value + "' for property '" + key
 170  
                     + "' of module " + aConfiguration.getName(), e);
 171  
             }
 172  1
             catch (final ConversionException e) {
 173  1
                 throw new CheckstyleException(
 174  
                     "illegal value '" + value + "' for property '" + key
 175  
                     + "' of module " + aConfiguration.getName(), e);
 176  1147
             }
 177  
 
 178  
         }
 179  
 
 180  1804
         finishLocalSetup();
 181  
 
 182  1801
         final Configuration[] childConfigs = aConfiguration.getChildren();
 183  3002
         for (final Configuration childConfig : childConfigs) {
 184  1214
             setupChild(childConfig);
 185  
         }
 186  1788
     }
 187  
 
 188  
     /**
 189  
      * Implements the Contextualizable interface using bean introspection.
 190  
      * @param aContext {@inheritDoc}
 191  
      * @throws CheckstyleException {@inheritDoc}
 192  
      * @see Contextualizable
 193  
      */
 194  
     public final void contextualize(Context aContext)
 195  
         throws CheckstyleException
 196  
     {
 197  1214
         final BeanUtilsBean beanUtils = createBeanUtilsBean();
 198  
 
 199  
         // TODO: debug log messages
 200  1214
         final Collection<String> attributes = aContext.getAttributeNames();
 201  
 
 202  1214
         for (final String key : attributes) {
 203  5465
             final Object value = aContext.get(key);
 204  
 
 205  
             try {
 206  5465
                 beanUtils.copyProperty(this, key, value);
 207  
             }
 208  0
             catch (final InvocationTargetException e) {
 209  
                 // TODO: log.debug("The bean " + this.getClass()
 210  
                 // + " is not interested in " + value)
 211  0
                 throw new CheckstyleException("cannot set property "
 212  
                     + key + " to value " + value + " in bean "
 213  
                     + this.getClass().getName(), e);
 214  
             }
 215  0
             catch (final IllegalAccessException e) {
 216  0
                 throw new CheckstyleException(
 217  
                     "cannot access " + key + " in "
 218  
                     + this.getClass().getName(), e);
 219  
             }
 220  0
             catch (final IllegalArgumentException e) {
 221  0
                 throw new CheckstyleException(
 222  
                     "illegal value '" + value + "' for property '" + key
 223  
                     + "' of bean " + this.getClass().getName(), e);
 224  
             }
 225  0
             catch (final ConversionException e) {
 226  0
                 throw new CheckstyleException(
 227  
                     "illegal value '" + value + "' for property '" + key
 228  
                     + "' of bean " + this.getClass().getName(), e);
 229  5465
             }
 230  5465
         }
 231  1214
     }
 232  
 
 233  
     /**
 234  
      * Returns the configuration that was used to configure this component.
 235  
      * @return the configuration that was used to configure this component.
 236  
      */
 237  
     protected final Configuration getConfiguration()
 238  
     {
 239  3430
         return mConfiguration;
 240  
     }
 241  
 
 242  
     /**
 243  
      * Provides a hook to finish the part of this component's setup that
 244  
      * was not handled by the bean introspection.
 245  
      * <p>
 246  
      * The default implementation does nothing.
 247  
      * </p>
 248  
      * @throws CheckstyleException if there is a configuration error.
 249  
      */
 250  
     protected void finishLocalSetup() throws CheckstyleException
 251  
     {
 252  651
     }
 253  
 
 254  
     /**
 255  
      * Called by configure() for every child of this component's Configuration.
 256  
      * <p>
 257  
      * The default implementation does nothing.
 258  
      * </p>
 259  
      * @param aChildConf a child of this component's Configuration
 260  
      * @throws CheckstyleException if there is a configuration error.
 261  
      * @see Configuration#getChildren
 262  
      */
 263  
     protected void setupChild(Configuration aChildConf)
 264  
         throws CheckstyleException
 265  
     {
 266  0
     }
 267  
 
 268  
     /**
 269  
      * A converter that does not care whether the array elements contain String
 270  
      * characters like '*' or '_'. The normal ArrayConverter class has problems
 271  
      * with this characters.
 272  
      */
 273  2419
     private static class RelaxedStringArrayConverter implements Converter
 274  
     {
 275  
         /** {@inheritDoc} */
 276  
         public Object convert(@SuppressWarnings("rawtypes") Class aType,
 277  
             Object aValue)
 278  
         {
 279  77
             if (null == aType) {
 280  0
                 throw new ConversionException("Cannot convert from null.");
 281  
             }
 282  
 
 283  
             // Convert to a String and trim it for the tokenizer.
 284  77
             final StringTokenizer st = new StringTokenizer(
 285  
                 aValue.toString().trim(), ",");
 286  77
             final List<String> result = Lists.newArrayList();
 287  
 
 288  199
             while (st.hasMoreTokens()) {
 289  122
                 final String token = st.nextToken();
 290  122
                 result.add(token.trim());
 291  122
             }
 292  
 
 293  77
             return result.toArray(new String[result.size()]);
 294  
         }
 295  
     }
 296  
 }