Coverage Report - org.apache.shiro.config.ReflectionBuilder
 
Classes in this File Line Coverage Branch Coverage Complexity
ReflectionBuilder
80%
212/263
71%
117/164
5.032
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one
 3  
  * or more contributor license agreements.  See the NOTICE file
 4  
  * distributed with this work for additional information
 5  
  * regarding copyright ownership.  The ASF licenses this file
 6  
  * to you under the Apache License, Version 2.0 (the
 7  
  * "License"); you may not use this file except in compliance
 8  
  * with the License.  You may obtain a copy of the License at
 9  
  *
 10  
  *     http://www.apache.org/licenses/LICENSE-2.0
 11  
  *
 12  
  * Unless required by applicable law or agreed to in writing,
 13  
  * software distributed under the License is distributed on an
 14  
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 15  
  * KIND, either express or implied.  See the License for the
 16  
  * specific language governing permissions and limitations
 17  
  * under the License.
 18  
  */
 19  
 package org.apache.shiro.config;
 20  
 
 21  
 import org.apache.commons.beanutils.BeanUtils;
 22  
 import org.apache.commons.beanutils.PropertyUtils;
 23  
 import org.apache.shiro.codec.Base64;
 24  
 import org.apache.shiro.codec.Hex;
 25  
 import org.apache.shiro.util.*;
 26  
 import org.slf4j.Logger;
 27  
 import org.slf4j.LoggerFactory;
 28  
 
 29  
 import java.beans.PropertyDescriptor;
 30  
 import java.util.*;
 31  
 
 32  
 
 33  
 /**
 34  
  * Object builder that uses reflection and Apache Commons BeanUtils to build objects given a
 35  
  * map of "property values".  Typically these come from the Shiro INI configuration and are used
 36  
  * to construct or modify the SecurityManager, its dependencies, and web-based security filters.
 37  
  * <p/>
 38  
  * Recognizes {@link Factory} implementations and will call
 39  
  * {@link org.apache.shiro.util.Factory#getInstance() getInstance} to satisfy any reference to this bean.
 40  
  *
 41  
  * @since 0.9
 42  
  */
 43  
 public class ReflectionBuilder {
 44  
 
 45  
     //TODO - complete JavaDoc
 46  
 
 47  1
     private static final Logger log = LoggerFactory.getLogger(ReflectionBuilder.class);
 48  
 
 49  
     private static final String OBJECT_REFERENCE_BEGIN_TOKEN = "$";
 50  
     private static final String ESCAPED_OBJECT_REFERENCE_BEGIN_TOKEN = "\\$";
 51  
     private static final String GLOBAL_PROPERTY_PREFIX = "shiro";
 52  
     private static final char MAP_KEY_VALUE_DELIMITER = ':';
 53  
     private static final String HEX_BEGIN_TOKEN = "0x";
 54  
     private static final String NULL_VALUE_TOKEN = "null";
 55  
     private static final String EMPTY_STRING_VALUE_TOKEN = "\"\"";
 56  
     private static final char STRING_VALUE_DELIMETER = '"';
 57  
     private static final char MAP_PROPERTY_BEGIN_TOKEN = '[';
 58  
     private static final char MAP_PROPERTY_END_TOKEN = ']';
 59  
 
 60  
     private Map<String, ?> objects;
 61  
 
 62  22
     public ReflectionBuilder() {
 63  22
         this.objects = new LinkedHashMap<String, Object>();
 64  22
     }
 65  
 
 66  26
     public ReflectionBuilder(Map<String, ?> defaults) {
 67  26
         this.objects = CollectionUtils.isEmpty(defaults) ? new LinkedHashMap<String, Object>() : defaults;
 68  26
     }
 69  
 
 70  
     public Map<String, ?> getObjects() {
 71  0
         return objects;
 72  
     }
 73  
 
 74  
     public void setObjects(Map<String, ?> objects) {
 75  0
         this.objects = CollectionUtils.isEmpty(objects) ? new LinkedHashMap<String, Object>() : objects;
 76  0
     }
 77  
 
 78  
     public Object getBean(String id) {
 79  21
         return objects.get(id);
 80  
     }
 81  
 
 82  
     @SuppressWarnings({"unchecked"})
 83  
     public <T> T getBean(String id, Class<T> requiredType) {
 84  21
         if (requiredType == null) {
 85  0
             throw new NullPointerException("requiredType argument cannot be null.");
 86  
         }
 87  21
         Object bean = getBean(id);
 88  21
         if (bean == null) {
 89  0
             return null;
 90  
         }
 91  21
         if (!requiredType.isAssignableFrom(bean.getClass())) {
 92  0
             throw new IllegalStateException("Bean with id [" + id + "] is not of the required type [" +
 93  
                     requiredType.getName() + "].");
 94  
         }
 95  21
         return (T) bean;
 96  
     }
 97  
 
 98  
     @SuppressWarnings({"unchecked"})
 99  
     public Map<String, ?> buildObjects(Map<String, String> kvPairs) {
 100  44
         if (kvPairs != null && !kvPairs.isEmpty()) {
 101  
 
 102  
             // Separate key value pairs into object declarations and property assignment
 103  
             // so that all objects can be created up front
 104  
 
 105  
             //https://issues.apache.org/jira/browse/SHIRO-85 - need to use LinkedHashMaps here:
 106  40
             Map<String, String> instanceMap = new LinkedHashMap<String, String>();
 107  40
             Map<String, String> propertyMap = new LinkedHashMap<String, String>();
 108  
 
 109  40
             for (Map.Entry<String, String> entry : kvPairs.entrySet()) {
 110  137
                 if (entry.getKey().indexOf('.') < 0 || entry.getKey().endsWith(".class")) {
 111  66
                     instanceMap.put(entry.getKey(), entry.getValue());
 112  
                 } else {
 113  71
                     propertyMap.put(entry.getKey(), entry.getValue());
 114  
                 }
 115  137
             }
 116  
 
 117  
             // Create all instances
 118  40
             for (Map.Entry<String, String> entry : instanceMap.entrySet()) {
 119  66
                 createNewInstance((Map<String, Object>) objects, entry.getKey(), entry.getValue());
 120  66
             }
 121  
 
 122  
             // Set all properties
 123  40
             for (Map.Entry<String, String> entry : propertyMap.entrySet()) {
 124  71
                 applyProperty(entry.getKey(), entry.getValue(), objects);
 125  69
             }
 126  
         }
 127  
 
 128  
         //SHIRO-413: init method must be called for constructed objects that are Initializable
 129  42
         LifecycleUtils.init(objects.values());
 130  
 
 131  42
         return objects;
 132  
     }
 133  
 
 134  
     protected void createNewInstance(Map<String, Object> objects, String name, String value) {
 135  
 
 136  66
         Object currentInstance = objects.get(name);
 137  66
         if (currentInstance != null) {
 138  0
             log.info("An instance with name '{}' already exists.  " +
 139  
                     "Redefining this object as a new instance of type {}", name, value);
 140  
         }
 141  
 
 142  
         Object instance;//name with no property, assume right hand side of equals sign is the class name:
 143  
         try {
 144  66
             instance = ClassUtils.newInstance(value);
 145  66
             if (instance instanceof Nameable) {
 146  14
                 ((Nameable) instance).setName(name);
 147  
             }
 148  0
         } catch (Exception e) {
 149  0
             String msg = "Unable to instantiate class [" + value + "] for object named '" + name + "'.  " +
 150  
                     "Please ensure you've specified the fully qualified class name correctly.";
 151  0
             throw new ConfigurationException(msg, e);
 152  66
         }
 153  66
         objects.put(name, instance);
 154  66
     }
 155  
 
 156  
     protected void applyProperty(String key, String value, Map objects) {
 157  
 
 158  71
         int index = key.indexOf('.');
 159  
 
 160  71
         if (index >= 0) {
 161  71
             String name = key.substring(0, index);
 162  71
             String property = key.substring(index + 1, key.length());
 163  
 
 164  71
             if (GLOBAL_PROPERTY_PREFIX.equalsIgnoreCase(name)) {
 165  0
                 applyGlobalProperty(objects, property, value);
 166  
             } else {
 167  71
                 applySingleProperty(objects, name, property, value);
 168  
             }
 169  
 
 170  69
         } else {
 171  0
             throw new IllegalArgumentException("All property keys must contain a '.' character. " +
 172  
                     "(e.g. myBean.property = value)  These should already be separated out by buildObjects().");
 173  
         }
 174  69
     }
 175  
 
 176  
     protected void applyGlobalProperty(Map objects, String property, String value) {
 177  0
         for (Object instance : objects.values()) {
 178  
             try {
 179  0
                 PropertyDescriptor pd = PropertyUtils.getPropertyDescriptor(instance, property);
 180  0
                 if (pd != null) {
 181  0
                     applyProperty(instance, property, value);
 182  
                 }
 183  0
             } catch (Exception e) {
 184  0
                 String msg = "Error retrieving property descriptor for instance " +
 185  
                         "of type [" + instance.getClass().getName() + "] " +
 186  
                         "while setting property [" + property + "]";
 187  0
                 throw new ConfigurationException(msg, e);
 188  0
             }
 189  0
         }
 190  0
     }
 191  
 
 192  
     protected void applySingleProperty(Map objects, String name, String property, String value) {
 193  71
         Object instance = objects.get(name);
 194  71
         if (property.equals("class")) {
 195  0
             throw new IllegalArgumentException("Property keys should not contain 'class' properties since these " +
 196  
                     "should already be separated out by buildObjects().");
 197  
 
 198  71
         } else if (instance == null) {
 199  0
             String msg = "Configuration error.  Specified object [" + name + "] with property [" +
 200  
                     property + "] without first defining that object's class.  Please first " +
 201  
                     "specify the class property first, e.g. myObject = fully_qualified_class_name " +
 202  
                     "and then define additional properties.";
 203  0
             throw new IllegalArgumentException(msg);
 204  
 
 205  
         } else {
 206  71
             applyProperty(instance, property, value);
 207  
         }
 208  69
     }
 209  
 
 210  
     protected boolean isReference(String value) {
 211  84
         return value != null && value.startsWith(OBJECT_REFERENCE_BEGIN_TOKEN);
 212  
     }
 213  
 
 214  
     protected String getId(String referenceToken) {
 215  38
         return referenceToken.substring(OBJECT_REFERENCE_BEGIN_TOKEN.length());
 216  
     }
 217  
 
 218  
     protected Object getReferencedObject(String id) {
 219  38
         Object o = objects != null && !objects.isEmpty() ? objects.get(id) : null;
 220  38
         if (o == null) {
 221  1
             String msg = "The object with id [" + id + "] has not yet been defined and therefore cannot be " +
 222  
                     "referenced.  Please ensure objects are defined in the order in which they should be " +
 223  
                     "created and made available for future reference.";
 224  1
             throw new UnresolveableReferenceException(msg);
 225  
         }
 226  37
         return o;
 227  
     }
 228  
 
 229  
     protected String unescapeIfNecessary(String value) {
 230  46
         if (value != null && value.startsWith(ESCAPED_OBJECT_REFERENCE_BEGIN_TOKEN)) {
 231  1
             return value.substring(ESCAPED_OBJECT_REFERENCE_BEGIN_TOKEN.length() - 1);
 232  
         }
 233  45
         return value;
 234  
     }
 235  
 
 236  
     protected Object resolveReference(String reference) {
 237  38
         String id = getId(reference);
 238  38
         log.debug("Encountered object reference '{}'.  Looking up object with id '{}'", reference, id);
 239  38
         final Object referencedObject = getReferencedObject(id);
 240  37
         if (referencedObject instanceof Factory) {
 241  1
             return ((Factory) referencedObject).getInstance();
 242  
         }
 243  36
         return referencedObject;
 244  
     }
 245  
 
 246  
     protected boolean isTypedProperty(Object object, String propertyName, Class clazz) {
 247  376
         if (clazz == null) {
 248  0
             throw new NullPointerException("type (class) argument cannot be null.");
 249  
         }
 250  
         try {
 251  376
             PropertyDescriptor descriptor = PropertyUtils.getPropertyDescriptor(object, propertyName);
 252  376
             if (descriptor == null) {
 253  0
                 String msg = "Property '" + propertyName + "' does not exist for object of " +
 254  
                         "type " + object.getClass().getName() + ".";
 255  0
                 throw new ConfigurationException(msg);
 256  
             }
 257  376
             Class propertyClazz = descriptor.getPropertyType();
 258  376
             return clazz.isAssignableFrom(propertyClazz);
 259  0
         } catch (ConfigurationException ce) {
 260  
             //let it propagate:
 261  0
             throw ce;
 262  0
         } catch (Exception e) {
 263  0
             String msg = "Unable to determine if property [" + propertyName + "] represents a " + clazz.getName();
 264  0
             throw new ConfigurationException(msg, e);
 265  
         }
 266  
     }
 267  
 
 268  
     protected Set<?> toSet(String sValue) {
 269  2
         String[] tokens = StringUtils.split(sValue);
 270  2
         if (tokens == null || tokens.length <= 0) {
 271  0
             return null;
 272  
         }
 273  
 
 274  
         //SHIRO-423: check to see if the value is a referenced Set already, and if so, return it immediately:
 275  2
         if (tokens.length == 1 && isReference(tokens[0])) {
 276  1
             Object reference = resolveReference(tokens[0]);
 277  1
             if (reference instanceof Set) {
 278  1
                 return (Set)reference;
 279  
             }
 280  
         }
 281  
 
 282  1
         Set<String> setTokens = new LinkedHashSet<String>(Arrays.asList(tokens));
 283  
 
 284  
         //now convert into correct values and/or references:
 285  1
         Set<Object> values = new LinkedHashSet<Object>(setTokens.size());
 286  1
         for (String token : setTokens) {
 287  2
             Object value = resolveValue(token);
 288  2
             values.add(value);
 289  2
         }
 290  1
         return values;
 291  
     }
 292  
 
 293  
     protected Map<?, ?> toMap(String sValue) {
 294  2
         String[] tokens = StringUtils.split(sValue, StringUtils.DEFAULT_DELIMITER_CHAR,
 295  
                 StringUtils.DEFAULT_QUOTE_CHAR, StringUtils.DEFAULT_QUOTE_CHAR, true, true);
 296  2
         if (tokens == null || tokens.length <= 0) {
 297  0
             return null;
 298  
         }
 299  
 
 300  
         //SHIRO-423: check to see if the value is a referenced Map already, and if so, return it immediately:
 301  2
         if (tokens.length == 1 && isReference(tokens[0])) {
 302  1
             Object reference = resolveReference(tokens[0]);
 303  1
             if (reference instanceof Map) {
 304  1
                 return (Map)reference;
 305  
             }
 306  
         }
 307  
 
 308  1
         Map<String, String> mapTokens = new LinkedHashMap<String, String>(tokens.length);
 309  3
         for (String token : tokens) {
 310  2
             String[] kvPair = StringUtils.split(token, MAP_KEY_VALUE_DELIMITER);
 311  2
             if (kvPair == null || kvPair.length != 2) {
 312  0
                 String msg = "Map property value [" + sValue + "] contained key-value pair token [" +
 313  
                         token + "] that does not properly split to a single key and pair.  This must be the " +
 314  
                         "case for all map entries.";
 315  0
                 throw new ConfigurationException(msg);
 316  
             }
 317  2
             mapTokens.put(kvPair[0], kvPair[1]);
 318  
         }
 319  
 
 320  
         //now convert into correct values and/or references:
 321  1
         Map<Object, Object> map = new LinkedHashMap<Object, Object>(mapTokens.size());
 322  1
         for (Map.Entry<String, String> entry : mapTokens.entrySet()) {
 323  2
             Object key = resolveValue(entry.getKey());
 324  2
             Object value = resolveValue(entry.getValue());
 325  2
             map.put(key, value);
 326  2
         }
 327  1
         return map;
 328  
     }
 329  
 
 330  
     // @since 1.2.2
 331  
     // TODO: make protected in 1.3+
 332  
     private Collection<?> toCollection(String sValue) {
 333  
 
 334  3
         String[] tokens = StringUtils.split(sValue);
 335  3
         if (tokens == null || tokens.length <= 0) {
 336  0
             return null;
 337  
         }
 338  
 
 339  
         //SHIRO-423: check to see if the value is a referenced Collection already, and if so, return it immediately:
 340  3
         if (tokens.length == 1 && isReference(tokens[0])) {
 341  2
             Object reference = resolveReference(tokens[0]);
 342  2
             if (reference instanceof Collection) {
 343  1
                 return (Collection)reference;
 344  
             }
 345  
         }
 346  
 
 347  
         //now convert into correct values and/or references:
 348  2
         List<Object> values = new ArrayList<Object>(tokens.length);
 349  6
         for (String token : tokens) {
 350  4
             Object value = resolveValue(token);
 351  4
             values.add(value);
 352  
         }
 353  2
         return values;
 354  
     }
 355  
 
 356  
     protected List<?> toList(String sValue) {
 357  3
         String[] tokens = StringUtils.split(sValue);
 358  3
         if (tokens == null || tokens.length <= 0) {
 359  0
             return null;
 360  
         }
 361  
 
 362  
         //SHIRO-423: check to see if the value is a referenced List already, and if so, return it immediately:
 363  3
         if (tokens.length == 1 && isReference(tokens[0])) {
 364  1
             Object reference = resolveReference(tokens[0]);
 365  1
             if (reference instanceof List) {
 366  1
                 return (List)reference;
 367  
             }
 368  
         }
 369  
 
 370  
         //now convert into correct values and/or references:
 371  2
         List<Object> values = new ArrayList<Object>(tokens.length);
 372  7
         for (String token : tokens) {
 373  5
             Object value = resolveValue(token);
 374  5
             values.add(value);
 375  
         }
 376  2
         return values;
 377  
     }
 378  
 
 379  
     protected byte[] toBytes(String sValue) {
 380  2
         if (sValue == null) {
 381  0
             return null;
 382  
         }
 383  
         byte[] bytes;
 384  2
         if (sValue.startsWith(HEX_BEGIN_TOKEN)) {
 385  1
             String hex = sValue.substring(HEX_BEGIN_TOKEN.length());
 386  1
             bytes = Hex.decode(hex);
 387  1
         } else {
 388  
             //assume base64 encoded:
 389  1
             bytes = Base64.decode(sValue);
 390  
         }
 391  2
         return bytes;
 392  
     }
 393  
 
 394  
     protected Object resolveValue(String stringValue) {
 395  
         Object value;
 396  79
         if (isReference(stringValue)) {
 397  33
             value = resolveReference(stringValue);
 398  
         } else {
 399  46
             value = unescapeIfNecessary(stringValue);
 400  
         }
 401  78
         return value;
 402  
     }
 403  
 
 404  
     protected String checkForNullOrEmptyLiteral(String stringValue) {
 405  58
         if (stringValue == null) {
 406  0
             return null;
 407  
         }
 408  
         //check if the value is the actual literal string 'null' (expected to be wrapped in quotes):
 409  58
         if (stringValue.equals("\"null\"")) {
 410  1
             return NULL_VALUE_TOKEN;
 411  
         }
 412  
         //or the actual literal string of two quotes '""' (expected to be wrapped in quotes):
 413  57
         else if (stringValue.equals("\"\"\"\"")) {
 414  1
             return EMPTY_STRING_VALUE_TOKEN;
 415  
         } else {
 416  56
             return stringValue;
 417  
         }
 418  
     }
 419  
     
 420  
     protected void applyProperty(Object object, String propertyPath, Object value) {
 421  
 
 422  81
         int mapBegin = propertyPath.indexOf(MAP_PROPERTY_BEGIN_TOKEN);
 423  81
         int mapEnd = -1;
 424  81
         String mapPropertyPath = null;
 425  81
         String keyString = null;
 426  
 
 427  81
         String remaining = null;
 428  
         
 429  81
         if (mapBegin >= 0) {
 430  
             //a map is being referenced in the overall property path.  Find just the map's path:
 431  9
             mapPropertyPath = propertyPath.substring(0, mapBegin);
 432  
             //find the end of the map reference:
 433  9
             mapEnd = propertyPath.indexOf(MAP_PROPERTY_END_TOKEN, mapBegin);
 434  
             //find the token in between the [ and the ] (the map/array key or index):
 435  9
             keyString = propertyPath.substring(mapBegin+1, mapEnd);
 436  
 
 437  
             //find out if there is more path reference to follow.  If not, we're at a terminal of the OGNL expression
 438  9
             if (propertyPath.length() > (mapEnd+1)) {
 439  3
                 remaining = propertyPath.substring(mapEnd+1);
 440  3
                 if (remaining.startsWith(".")) {
 441  3
                     remaining = StringUtils.clean(remaining.substring(1));
 442  
                 }
 443  
             }
 444  
         }
 445  
         
 446  81
         if (remaining == null) {
 447  
             //we've terminated the OGNL expression.  Check to see if we're assigning a property or a map entry:
 448  78
             if (keyString == null) {
 449  
                 //not a map or array value assignment - assign the property directly:
 450  72
                 setProperty(object, propertyPath, value);
 451  
             } else {
 452  
                 //we're assigning a map or array entry.  Check to see which we should call:
 453  6
                 if (isTypedProperty(object, mapPropertyPath, Map.class)) {
 454  4
                     Map map = (Map)getProperty(object, mapPropertyPath);
 455  4
                     Object mapKey = resolveValue(keyString);
 456  
                     //noinspection unchecked
 457  4
                     map.put(mapKey, value);
 458  4
                 } else {
 459  
                     //must be an array property.  Convert the key string to an index:
 460  2
                     int index = Integer.valueOf(keyString);
 461  2
                     setIndexedProperty(object, mapPropertyPath, index, value);
 462  2
                 }
 463  
             }
 464  
         } else {
 465  
             //property is being referenced as part of a nested path.  Find the referenced map/array entry and
 466  
             //recursively call this method with the remaining property path
 467  3
             Object referencedValue = null;
 468  3
             if (isTypedProperty(object, mapPropertyPath, Map.class)) {
 469  2
                 Map map = (Map)getProperty(object, mapPropertyPath);
 470  2
                 Object mapKey = resolveValue(keyString);
 471  2
                 referencedValue = map.get(mapKey);
 472  2
             } else {
 473  
                 //must be an array property:
 474  1
                 int index = Integer.valueOf(keyString);
 475  1
                 referencedValue = getIndexedProperty(object, mapPropertyPath, index);
 476  
             }
 477  
 
 478  3
             if (referencedValue == null) {
 479  0
                 throw new ConfigurationException("Referenced map/array value '" + mapPropertyPath + "[" +
 480  
                 keyString + "]' does not exist.");
 481  
             }
 482  
 
 483  3
             applyProperty(referencedValue, remaining, value);
 484  
         }
 485  80
     }
 486  
     
 487  
     private void setProperty(Object object, String propertyPath, Object value) {
 488  
         try {
 489  72
             if (log.isTraceEnabled()) {
 490  72
                 log.trace("Applying property [{}] value [{}] on object of type [{}]",
 491  
                         new Object[]{propertyPath, value, object.getClass().getName()});
 492  
             }
 493  72
             BeanUtils.setProperty(object, propertyPath, value);
 494  1
         } catch (Exception e) {
 495  1
             String msg = "Unable to set property '" + propertyPath + "' with value [" + value + "] on object " +
 496  
                     "of type " + (object != null ? object.getClass().getName() : null) + ".  If " +
 497  
                     "'" + value + "' is a reference to another (previously defined) object, prefix it with " +
 498  
                     "'" + OBJECT_REFERENCE_BEGIN_TOKEN + "' to indicate that the referenced " +
 499  
                     "object should be used as the actual value.  " +
 500  
                     "For example, " + OBJECT_REFERENCE_BEGIN_TOKEN + value;
 501  1
             throw new ConfigurationException(msg, e);
 502  71
         }
 503  71
     }
 504  
     
 505  
     private Object getProperty(Object object, String propertyPath) {
 506  
         try {
 507  6
             return PropertyUtils.getProperty(object, propertyPath);
 508  0
         } catch (Exception e) {
 509  0
             throw new ConfigurationException("Unable to access property '" + propertyPath + "'", e);
 510  
         }
 511  
     }
 512  
     
 513  
     private void setIndexedProperty(Object object, String propertyPath, int index, Object value) {
 514  
         try {
 515  2
             PropertyUtils.setIndexedProperty(object, propertyPath, index, value);
 516  0
         } catch (Exception e) {
 517  0
             throw new ConfigurationException("Unable to set array property '" + propertyPath + "'", e);
 518  2
         }
 519  2
     }
 520  
     
 521  
     private Object getIndexedProperty(Object object, String propertyPath, int index) {
 522  
         try {
 523  1
             return PropertyUtils.getIndexedProperty(object, propertyPath, index);
 524  0
         } catch (Exception e) {
 525  0
             throw new ConfigurationException("Unable to acquire array property '" + propertyPath + "'", e);
 526  
         }
 527  
     }
 528  
     
 529  
     protected boolean isIndexedPropertyAssignment(String propertyPath) {
 530  70
         return propertyPath.endsWith("" + MAP_PROPERTY_END_TOKEN);
 531  
     }
 532  
 
 533  
     protected void applyProperty(Object object, String propertyName, String stringValue) {
 534  
 
 535  
         Object value;
 536  
 
 537  72
         if (NULL_VALUE_TOKEN.equals(stringValue)) {
 538  1
             value = null;
 539  71
         } else if (EMPTY_STRING_VALUE_TOKEN.equals(stringValue)) {
 540  1
             value = StringUtils.EMPTY_STRING;
 541  70
         } else if (isIndexedPropertyAssignment(propertyName)) {
 542  3
             String checked = checkForNullOrEmptyLiteral(stringValue);
 543  3
             value = resolveValue(checked);
 544  3
         } else if (isTypedProperty(object, propertyName, Set.class)) {
 545  2
             value = toSet(stringValue);
 546  65
         } else if (isTypedProperty(object, propertyName, Map.class)) {
 547  2
             value = toMap(stringValue);
 548  63
         } else if (isTypedProperty(object, propertyName, List.class)) {
 549  3
             value = toList(stringValue);
 550  60
         } else if (isTypedProperty(object, propertyName, Collection.class)) {
 551  3
             value = toCollection(stringValue);
 552  57
         } else if (isTypedProperty(object, propertyName, byte[].class)) {
 553  2
             value = toBytes(stringValue);
 554  55
         } else if (isTypedProperty(object, propertyName, ByteSource.class)) {
 555  0
             byte[] bytes = toBytes(stringValue);
 556  0
             value = ByteSource.Util.bytes(bytes);
 557  0
         } else {
 558  55
             String checked = checkForNullOrEmptyLiteral(stringValue);
 559  55
             value = resolveValue(checked);
 560  
         }
 561  
 
 562  71
         applyProperty(object, propertyName, value);
 563  70
     }
 564  
 
 565  
 }