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 (".") 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 }