001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    
018    package org.apache.commons.configuration.beanutils;
019    
020    import java.lang.reflect.Array;
021    import java.util.Collection;
022    import java.util.List;
023    
024    import org.apache.commons.beanutils.DynaBean;
025    import org.apache.commons.beanutils.DynaClass;
026    import org.apache.commons.configuration.Configuration;
027    import org.apache.commons.configuration.ConfigurationMap;
028    import org.apache.commons.configuration.SubsetConfiguration;
029    import org.apache.commons.logging.Log;
030    import org.apache.commons.logging.LogFactory;
031    
032    /**
033     * The <tt>ConfigurationDynaBean</tt> dynamically reads and writes
034     * configurations properties from a wrapped configuration-collection
035     * {@link org.apache.commons.configuration.Configuration} instance. It also
036     * implements a {@link java.util.Map} interface so that it can be used in
037     * JSP 2.0 Expression Language expressions.
038     *
039     * <p>The {@code ConfigurationDynaBean} maps nested and mapped properties
040     * to the appropriate {@code Configuration} subset using the
041     * {@link org.apache.commons.configuration.Configuration#subset}
042     * method. Similarly, indexed properties reference lists of configuration
043     * properties using the
044     * {@link org.apache.commons.configuration.Configuration#getList(String)}
045     * method. Setting an indexed property is supported, too.</p>
046     *
047     * <p>Note: Some of the methods expect that a dot (&quot;.&quot;) is used as
048     * property delimiter for the wrapped configuration. This is true for most of
049     * the default configurations. Hierarchical configurations, for which a specific
050     * expression engine is set, may cause problems.</p>
051     *
052     * @author <a href="mailto:ricardo.gladwell@btinternet.com">Ricardo Gladwell</a>
053     * @version $Id: ConfigurationDynaBean.java 1208765 2011-11-30 20:41:50Z oheger $
054     * @since 1.0-rc1
055     */
056    public class ConfigurationDynaBean extends ConfigurationMap implements DynaBean
057    {
058        /** Constant for the property delimiter.*/
059        private static final String PROPERTY_DELIMITER = ".";
060    
061        /** The logger.*/
062        private static Log log = LogFactory.getLog(ConfigurationDynaBean.class);
063    
064        /**
065         * Creates a new instance of {@code ConfigurationDynaBean} and sets
066         * the configuration this bean is associated with.
067         *
068         * @param configuration the configuration
069         */
070        public ConfigurationDynaBean(Configuration configuration)
071        {
072            super(configuration);
073            if (log.isTraceEnabled())
074            {
075                log.trace("ConfigurationDynaBean(" + configuration + ")");
076            }
077        }
078    
079        public void set(String name, Object value)
080        {
081            if (log.isTraceEnabled())
082            {
083                log.trace("set(" + name + "," + value + ")");
084            }
085    
086            if (value == null)
087            {
088                throw new NullPointerException("Error trying to set property to null.");
089            }
090    
091            if (value instanceof Collection)
092            {
093                Collection<?> collection = (Collection<?>) value;
094                for (Object v : collection)
095                {
096                    getConfiguration().addProperty(name, v);
097                }
098            }
099            else if (value.getClass().isArray())
100            {
101                int length = Array.getLength(value);
102                for (int i = 0; i < length; i++)
103                {
104                    getConfiguration().addProperty(name, Array.get(value, i));
105                }
106            }
107            else
108            {
109                getConfiguration().setProperty(name, value);
110            }
111        }
112    
113        public Object get(String name)
114        {
115            if (log.isTraceEnabled())
116            {
117                log.trace("get(" + name + ")");
118            }
119    
120            // get configuration property
121            Object result = getConfiguration().getProperty(name);
122            if (result == null)
123            {
124                // otherwise attempt to create bean from configuration subset
125                Configuration subset = new SubsetConfiguration(getConfiguration(), name, PROPERTY_DELIMITER);
126                if (!subset.isEmpty())
127                {
128                    result = new ConfigurationDynaBean(subset);
129                }
130            }
131    
132            if (log.isDebugEnabled())
133            {
134                log.debug(name + "=[" + result + "]");
135            }
136    
137            if (result == null)
138            {
139                throw new IllegalArgumentException("Property '" + name + "' does not exist.");
140            }
141            return result;
142        }
143    
144        public boolean contains(String name, String key)
145        {
146            Configuration subset = getConfiguration().subset(name);
147            if (subset == null)
148            {
149                throw new IllegalArgumentException("Mapped property '" + name + "' does not exist.");
150            }
151    
152            return subset.containsKey(key);
153        }
154    
155        public Object get(String name, int index)
156        {
157            if (!checkIndexedProperty(name))
158            {
159                throw new IllegalArgumentException("Property '" + name
160                        + "' is not indexed.");
161            }
162    
163            List<Object> list = getConfiguration().getList(name);
164            return list.get(index);
165        }
166    
167        public Object get(String name, String key)
168        {
169            Configuration subset = getConfiguration().subset(name);
170            if (subset == null)
171            {
172                throw new IllegalArgumentException("Mapped property '" + name + "' does not exist.");
173            }
174    
175            return subset.getProperty(key);
176        }
177    
178        public DynaClass getDynaClass()
179        {
180            return new ConfigurationDynaClass(getConfiguration());
181        }
182    
183        public void remove(String name, String key)
184        {
185            Configuration subset = new SubsetConfiguration(getConfiguration(), name, PROPERTY_DELIMITER);
186            subset.setProperty(key, null);
187        }
188    
189        public void set(String name, int index, Object value)
190        {
191            if (!checkIndexedProperty(name) && index > 0)
192            {
193                throw new IllegalArgumentException("Property '" + name
194                        + "' is not indexed.");
195            }
196    
197            Object property = getConfiguration().getProperty(name);
198    
199            if (property instanceof List)
200            {
201                // This is safe because multiple values of a configuration property
202                // are always stored as lists of type Object.
203                @SuppressWarnings("unchecked")
204                List<Object> list = (List<Object>) property;
205                list.set(index, value);
206                getConfiguration().setProperty(name, list);
207            }
208            else if (property.getClass().isArray())
209            {
210                Array.set(property, index, value);
211            }
212            else if (index == 0)
213            {
214                getConfiguration().setProperty(name, value);
215            }
216        }
217    
218        public void set(String name, String key, Object value)
219        {
220            getConfiguration().setProperty(name + "." + key, value);
221        }
222    
223        /**
224         * Tests whether the given name references an indexed property. This
225         * implementation tests for properties of type list or array. If the
226         * property does not exist, an exception is thrown.
227         *
228         * @param name the name of the property to check
229         * @return a flag whether this is an indexed property
230         * @throws IllegalArgumentException if the property does not exist
231         */
232        private boolean checkIndexedProperty(String name)
233        {
234            Object property = getConfiguration().getProperty(name);
235    
236            if (property == null)
237            {
238                throw new IllegalArgumentException("Property '" + name
239                        + "' does not exist.");
240            }
241    
242            return (property instanceof List) || property.getClass().isArray();
243        }
244    }