Coverage Report - de.smartics.properties.spi.core.convert.FromStringTypeConverter
 
Classes in this File Line Coverage Branch Coverage Complexity
FromStringTypeConverter
70%
40/57
87%
7/8
3,385
 
 1  
 /*
 2  
  * Copyright 2012-2013 smartics, Kronseder & Reiner GmbH
 3  
  *
 4  
  * Licensed under the Apache License, Version 2.0 (the "License");
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  *
 8  
  *     http://www.apache.org/licenses/LICENSE-2.0
 9  
  *
 10  
  * Unless required by applicable law or agreed to in writing, software
 11  
  * distributed under the License is distributed on an "AS IS" BASIS,
 12  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  * See the License for the specific language governing permissions and
 14  
  * limitations under the License.
 15  
  */
 16  
 package de.smartics.properties.spi.core.convert;
 17  
 
 18  
 import java.lang.reflect.Method;
 19  
 import java.lang.reflect.Modifier;
 20  
 
 21  
 import org.apache.commons.beanutils.ConversionException;
 22  
 import org.apache.commons.beanutils.Converter;
 23  
 import org.apache.commons.lang.ObjectUtils;
 24  
 
 25  
 import de.smartics.util.lang.Arg;
 26  
 import de.smartics.util.lang.NullArgumentException;
 27  
 
 28  
 /**
 29  
  * Converter for types that provide a static <code>fromString</code> or
 30  
  * <code>valueOf</code> method.
 31  
  *
 32  
  * <pre>
 33  
  * public static T fromString(String value);
 34  
  * </pre>
 35  
  *
 36  
  * <pre>
 37  
  * public static T valueOf(String value);
 38  
  * </pre>
 39  
  */
 40  
 final class FromStringTypeConverter implements Converter
 41  
 { // NOPMD
 42  
   // ********************************* Fields *********************************
 43  
 
 44  
   // --- constants ------------------------------------------------------------
 45  
 
 46  
   // --- members --------------------------------------------------------------
 47  
 
 48  
   /**
 49  
    * The type to convert.
 50  
    */
 51  
   private final Class<?> type;
 52  
 
 53  
   /**
 54  
    * The static <code>fromString</code> method of {@link #type}.
 55  
    */
 56  
   private final Method fromStringMethod;
 57  
 
 58  
   // ****************************** Initializer *******************************
 59  
 
 60  
   // ****************************** Constructors ******************************
 61  
 
 62  
   /**
 63  
    * Default constructor.
 64  
    *
 65  
    * @param type the type to convert.
 66  
    * @throws NullArgumentException if {@code type} is <code>null</code>.
 67  
    * @throws IllegalArgumentException if {@code type} does not provide a static
 68  
    *           <code>fromString</code> method.
 69  
    */
 70  
   FromStringTypeConverter(final Class<?> type) throws IllegalArgumentException,
 71  
     NullArgumentException
 72  8
   {
 73  8
     this.type = Arg.checkNotNull("type", type);
 74  7
     this.fromStringMethod = calcStaticFromStringMethod(type);
 75  3
   }
 76  
 
 77  
   // ****************************** Inner Classes *****************************
 78  
 
 79  
   // ********************************* Methods ********************************
 80  
 
 81  
   // --- init -----------------------------------------------------------------
 82  
 
 83  
   private static Method calcStaticFromStringMethod(final Class<?> type)
 84  
   {
 85  7
     final Method method = getMethod(type);
 86  
 
 87  6
     checkStatic(type, method);
 88  5
     final Class<?> returnType = method.getReturnType();
 89  5
     checkNonVoid(type, returnType);
 90  4
     checkNonPrimitiveType(type, returnType);
 91  
 
 92  3
     return method;
 93  
   }
 94  
 
 95  
   private static Method getMethod(final Class<?> type)
 96  
   {
 97  
     try
 98  
     {
 99  7
       final Method method = type.getMethod("fromString", String.class);
 100  4
       return method;
 101  
     }
 102  0
     catch (final SecurityException e)
 103  
     {
 104  0
       return retryWithValueOf(type, e);
 105  
     }
 106  3
     catch (final NoSuchMethodException e)
 107  
     {
 108  3
       return retryWithValueOf(type, e);
 109  
     }
 110  
   }
 111  
 
 112  
   private static Method retryWithValueOf(final Class<?> type,
 113  
       final SecurityException e)
 114  
   {
 115  0
     final IllegalArgumentException previous =
 116  
         new IllegalArgumentException(String.format(
 117  
             "Method 'fromString' of type '%s' cannot be accessed.",
 118  
             type.getName()), e);
 119  
     try
 120  
     {
 121  0
       final Method method = type.getMethod("valueOf", String.class);
 122  0
       return method;
 123  
     }
 124  0
     catch (final SecurityException e2)
 125  
     {
 126  0
       throw createValueOfCannotBeAccessed(type, previous); // NOPMD
 127  
     }
 128  0
     catch (final NoSuchMethodException e2)
 129  
     {
 130  0
       throw createValueOfDoesNotExist(type, previous); // NOPMD
 131  
     }
 132  
   }
 133  
 
 134  
   private static Method retryWithValueOf(final Class<?> type,
 135  
       final NoSuchMethodException e)
 136  
   {
 137  3
     final IllegalArgumentException previous =
 138  
         new IllegalArgumentException(
 139  
             String.format("Method 'fromString' of type '%s' does not exist.",
 140  
                 type.getName()), e);
 141  
     try
 142  
     {
 143  3
       final Method method = type.getMethod("valueOf", String.class);
 144  2
       return method;
 145  
     }
 146  0
     catch (final SecurityException e2)
 147  
     {
 148  0
       throw createValueOfCannotBeAccessed(type, previous); // NOPMD
 149  
     }
 150  1
     catch (final NoSuchMethodException e2)
 151  
     {
 152  1
       throw createValueOfDoesNotExist(type, previous); // NOPMD
 153  
     }
 154  
   }
 155  
 
 156  
   private static IllegalArgumentException createValueOfDoesNotExist(
 157  
       final Class<?> type, final IllegalArgumentException previous)
 158  
   {
 159  1
     return new IllegalArgumentException(String.format(
 160  
         "Method 'valueOf' of type '%s' does exist.", type.getName()), previous);
 161  
   }
 162  
 
 163  
   private static IllegalArgumentException createValueOfCannotBeAccessed(
 164  
       final Class<?> type, final IllegalArgumentException previous)
 165  
   {
 166  0
     return new IllegalArgumentException(String.format(
 167  
         "Method 'valueOf' of type '%s' cannot be accessed.", type.getName()),
 168  
         previous);
 169  
   }
 170  
 
 171  
   private static void checkStatic(final Class<?> type, final Method method)
 172  
   {
 173  6
     if (!Modifier.isStatic(method.getModifiers()))
 174  
     {
 175  1
       throw new IllegalArgumentException(String.format(
 176  
           "Method 'fromString' of type '%s' is not static.", type.getName()));
 177  
     }
 178  5
   }
 179  
 
 180  
   private static void checkNonPrimitiveType(final Class<?> type,
 181  
       final Class<?> returnType)
 182  
   {
 183  4
     if (returnType.isPrimitive())
 184  
     {
 185  1
       throw new IllegalArgumentException(
 186  
           String
 187  
               .format(
 188  
                   "Method 'fromString' of type '%s' is required to return a non-primitive type.",
 189  
                   type.getName()));
 190  
     }
 191  3
   }
 192  
 
 193  
   private static void checkNonVoid(final Class<?> type,
 194  
       final Class<?> returnType)
 195  
   {
 196  5
     if (Void.TYPE.equals(returnType))
 197  
     {
 198  1
       throw new IllegalArgumentException(
 199  
           String
 200  
               .format(
 201  
                   "Method 'fromString' of type '%s' is required to return a non-void type.",
 202  
                   type.getName()));
 203  
     }
 204  4
   }
 205  
 
 206  
   // --- get&set --------------------------------------------------------------
 207  
 
 208  
   // --- business -------------------------------------------------------------
 209  
 
 210  
   @Override
 211  
   @SuppressWarnings("rawtypes")
 212  
   public Object convert(final Class toType, final Object value)
 213  
     throws ConversionException
 214  
   {
 215  
     try
 216  
     {
 217  3
       return convertFromString(toType, value);
 218  
     }
 219  1
     catch (final ConversionException e)
 220  
     {
 221  1
       throw e;
 222  
     }
 223  0
     catch (final IllegalArgumentException e)
 224  
     {
 225  0
       throw new ConversionException("Cannot convert value: " + e.getMessage(),
 226  
           e);
 227  
     }
 228  0
     catch (final RuntimeException e)
 229  
     {
 230  0
       throw new ConversionException(
 231  
           "Can only convert to/from String, not to/from " + type.getName(), e);
 232  
     }
 233  
   }
 234  
 
 235  
   @SuppressWarnings("rawtypes")
 236  
   private Object convertFromString(final Class toType, final Object value)
 237  
     throws ConversionException
 238  
   {
 239  
     final Object converted;
 240  
 
 241  3
     final String stringValue = ObjectUtils.toString(value, null);
 242  3
     if (String.class == toType)
 243  
     {
 244  0
       converted = stringValue;
 245  
     }
 246  
     else
 247  
     {
 248  3
       converted = convertFromString(stringValue);
 249  
     }
 250  
 
 251  2
     return converted;
 252  
   }
 253  
 
 254  
   private Object convertFromString(final String stringValue)
 255  
   {
 256  
     try
 257  
     {
 258  3
       final Object instance = fromStringMethod.invoke(type, stringValue);
 259  2
       return instance;
 260  
     }
 261  1
     catch (final Exception e)
 262  
     {
 263  1
       throw new ConversionException(String.format(
 264  
           "Cannot convert '%s' to an instance of type '%s'.", stringValue,
 265  
           type.getName()), e);
 266  
     }
 267  
   }
 268  
 
 269  
   // --- object basics --------------------------------------------------------
 270  
 
 271  
 }