ConfigurationDynaBean.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *     http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */

  17. package org.apache.commons.configuration2.beanutils;

  18. import java.lang.reflect.Array;
  19. import java.util.Collection;
  20. import java.util.List;
  21. import java.util.Objects;

  22. import org.apache.commons.beanutils.DynaBean;
  23. import org.apache.commons.beanutils.DynaClass;
  24. import org.apache.commons.configuration2.Configuration;
  25. import org.apache.commons.configuration2.ConfigurationMap;
  26. import org.apache.commons.configuration2.SubsetConfiguration;
  27. import org.apache.commons.logging.Log;
  28. import org.apache.commons.logging.LogFactory;

  29. /**
  30.  * The {@code ConfigurationDynaBean} dynamically reads and writes configurations properties from a wrapped
  31.  * configuration-collection {@link org.apache.commons.configuration2.Configuration} instance. It also implements a
  32.  * {@link java.util.Map} interface so that it can be used in JSP 2.0 Expression Language expressions.
  33.  *
  34.  * <p>
  35.  * The {@code ConfigurationDynaBean} maps nested and mapped properties to the appropriate {@code Configuration} subset
  36.  * using the {@link org.apache.commons.configuration2.Configuration#subset} method. Similarly, indexed properties
  37.  * reference lists of configuration properties using the
  38.  * {@link org.apache.commons.configuration2.Configuration#getList(String)} method. Setting an indexed property is
  39.  * supported, too.
  40.  * </p>
  41.  *
  42.  * <p>
  43.  * Note: Some of the methods expect that a dot (&quot;.&quot;) is used as property delimiter for the wrapped
  44.  * configuration. This is true for most of the default configurations. Hierarchical configurations, for which a specific
  45.  * expression engine is set, may cause problems.
  46.  * </p>
  47.  *
  48.  * @since 1.0-rc1
  49.  */
  50. public class ConfigurationDynaBean extends ConfigurationMap implements DynaBean {

  51.     /** Constant for the property delimiter. */
  52.     private static final String PROPERTY_DELIMITER = ".";

  53.     /** The logger. */
  54.     private static final Log LOG = LogFactory.getLog(ConfigurationDynaBean.class);

  55.     /**
  56.      * Constructs a new instance of {@code ConfigurationDynaBean} and sets the configuration this bean is associated with.
  57.      *
  58.      * @param configuration the configuration
  59.      */
  60.     public ConfigurationDynaBean(final Configuration configuration) {
  61.         super(configuration);
  62.         if (LOG.isTraceEnabled()) {
  63.             LOG.trace("ConfigurationDynaBean(" + configuration + ")");
  64.         }
  65.     }

  66.     /**
  67.      * Checks whether the given name references an indexed property. This implementation tests for properties of type list or
  68.      * array. If the property does not exist, an exception is thrown.
  69.      *
  70.      * @param name the name of the property to check
  71.      * @return a flag whether this is an indexed property
  72.      * @throws IllegalArgumentException if the property does not exist
  73.      */
  74.     private boolean checkIndexedProperty(final String name) {
  75.         final Object property = getConfiguration().getProperty(name);

  76.         if (property == null) {
  77.             throw new IllegalArgumentException("Property '" + name + "' does not exist.");
  78.         }

  79.         return property instanceof List || property.getClass().isArray();
  80.     }

  81.     @Override
  82.     public boolean contains(final String name, final String key) {
  83.         final Configuration subset = getConfiguration().subset(name);
  84.         if (subset == null) {
  85.             throw new IllegalArgumentException("Mapped property '" + name + "' does not exist.");
  86.         }

  87.         return subset.containsKey(key);
  88.     }

  89.     @Override
  90.     public Object get(final String name) {
  91.         if (LOG.isTraceEnabled()) {
  92.             LOG.trace("get(" + name + ")");
  93.         }

  94.         // get configuration property
  95.         Object result = getConfiguration().getProperty(name);
  96.         if (result == null) {
  97.             // otherwise attempt to create bean from configuration subset
  98.             final Configuration subset = new SubsetConfiguration(getConfiguration(), name, PROPERTY_DELIMITER);
  99.             if (!subset.isEmpty()) {
  100.                 result = new ConfigurationDynaBean(subset);
  101.             }
  102.         }

  103.         if (LOG.isDebugEnabled()) {
  104.             LOG.debug(name + "=[" + result + "]");
  105.         }

  106.         if (result == null) {
  107.             throw new IllegalArgumentException("Property '" + name + "' does not exist.");
  108.         }
  109.         return result;
  110.     }

  111.     @Override
  112.     public Object get(final String name, final int index) {
  113.         if (!checkIndexedProperty(name)) {
  114.             throw new IllegalArgumentException("Property '" + name + "' is not indexed.");
  115.         }

  116.         final List<Object> list = getConfiguration().getList(name);
  117.         return list.get(index);
  118.     }

  119.     @Override
  120.     public Object get(final String name, final String key) {
  121.         final Configuration subset = getConfiguration().subset(name);
  122.         if (subset == null) {
  123.             throw new IllegalArgumentException("Mapped property '" + name + "' does not exist.");
  124.         }

  125.         return subset.getProperty(key);
  126.     }

  127.     @Override
  128.     public DynaClass getDynaClass() {
  129.         return new ConfigurationDynaClass(getConfiguration());
  130.     }

  131.     @Override
  132.     public void remove(final String name, final String key) {
  133.         final Configuration subset = new SubsetConfiguration(getConfiguration(), name, PROPERTY_DELIMITER);
  134.         subset.setProperty(key, null);
  135.     }

  136.     @Override
  137.     public void set(final String name, final int index, final Object value) {
  138.         if (!checkIndexedProperty(name) && index > 0) {
  139.             throw new IllegalArgumentException("Property '" + name + "' is not indexed.");
  140.         }

  141.         final Object property = getConfiguration().getProperty(name);

  142.         if (property instanceof List) {
  143.             // This is safe because multiple values of a configuration property
  144.             // are always stored as lists of type Object.
  145.             @SuppressWarnings("unchecked")
  146.             final List<Object> list = (List<Object>) property;
  147.             list.set(index, value);
  148.             getConfiguration().setProperty(name, list);
  149.         } else if (property.getClass().isArray()) {
  150.             Array.set(property, index, value);
  151.         } else if (index == 0) {
  152.             getConfiguration().setProperty(name, value);
  153.         }
  154.     }

  155.     @Override
  156.     public void set(final String name, final Object value) {
  157.         if (LOG.isTraceEnabled()) {
  158.             LOG.trace("set(" + name + "," + value + ")");
  159.         }
  160.         Objects.requireNonNull(value, "value");

  161.         if (value instanceof Collection) {
  162.             final Collection<?> collection = (Collection<?>) value;
  163.             collection.forEach(v -> getConfiguration().addProperty(name, v));
  164.         } else if (value.getClass().isArray()) {
  165.             final int length = Array.getLength(value);
  166.             for (int i = 0; i < length; i++) {
  167.                 getConfiguration().addProperty(name, Array.get(value, i));
  168.             }
  169.         } else {
  170.             getConfiguration().setProperty(name, value);
  171.         }
  172.     }

  173.     @Override
  174.     public void set(final String name, final String key, final Object value) {
  175.         getConfiguration().setProperty(name + "." + key, value);
  176.     }
  177. }