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 018package org.apache.commons.configuration2.beanutils; 019 020import java.lang.reflect.Array; 021import java.util.Collection; 022import java.util.List; 023 024import org.apache.commons.beanutils.DynaBean; 025import org.apache.commons.beanutils.DynaClass; 026import org.apache.commons.configuration2.Configuration; 027import org.apache.commons.configuration2.ConfigurationMap; 028import org.apache.commons.configuration2.SubsetConfiguration; 029import org.apache.commons.logging.Log; 030import org.apache.commons.logging.LogFactory; 031 032/** 033 * The {@code ConfigurationDynaBean} dynamically reads and writes 034 * configurations properties from a wrapped configuration-collection 035 * {@link org.apache.commons.configuration2.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.configuration2.Configuration#subset} 042 * method. Similarly, indexed properties reference lists of configuration 043 * properties using the 044 * {@link org.apache.commons.configuration2.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 * @since 1.0-rc1 053 */ 054public class ConfigurationDynaBean extends ConfigurationMap implements DynaBean 055{ 056 /** Constant for the property delimiter.*/ 057 private static final String PROPERTY_DELIMITER = "."; 058 059 /** The logger.*/ 060 private static final Log LOG = LogFactory.getLog(ConfigurationDynaBean.class); 061 062 /** 063 * Creates a new instance of {@code ConfigurationDynaBean} and sets 064 * the configuration this bean is associated with. 065 * 066 * @param configuration the configuration 067 */ 068 public ConfigurationDynaBean(final Configuration configuration) 069 { 070 super(configuration); 071 if (LOG.isTraceEnabled()) 072 { 073 LOG.trace("ConfigurationDynaBean(" + configuration + ")"); 074 } 075 } 076 077 @Override 078 public void set(final String name, final Object value) 079 { 080 if (LOG.isTraceEnabled()) 081 { 082 LOG.trace("set(" + name + "," + value + ")"); 083 } 084 085 if (value == null) 086 { 087 throw new NullPointerException("Error trying to set property to null."); 088 } 089 090 if (value instanceof Collection) 091 { 092 final Collection<?> collection = (Collection<?>) value; 093 for (final Object v : collection) 094 { 095 getConfiguration().addProperty(name, v); 096 } 097 } 098 else if (value.getClass().isArray()) 099 { 100 final int length = Array.getLength(value); 101 for (int i = 0; i < length; i++) 102 { 103 getConfiguration().addProperty(name, Array.get(value, i)); 104 } 105 } 106 else 107 { 108 getConfiguration().setProperty(name, value); 109 } 110 } 111 112 @Override 113 public Object get(final 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 final 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 @Override 145 public boolean contains(final String name, final String key) 146 { 147 final Configuration subset = getConfiguration().subset(name); 148 if (subset == null) 149 { 150 throw new IllegalArgumentException("Mapped property '" + name + "' does not exist."); 151 } 152 153 return subset.containsKey(key); 154 } 155 156 @Override 157 public Object get(final String name, final int index) 158 { 159 if (!checkIndexedProperty(name)) 160 { 161 throw new IllegalArgumentException("Property '" + name 162 + "' is not indexed."); 163 } 164 165 final List<Object> list = getConfiguration().getList(name); 166 return list.get(index); 167 } 168 169 @Override 170 public Object get(final String name, final String key) 171 { 172 final Configuration subset = getConfiguration().subset(name); 173 if (subset == null) 174 { 175 throw new IllegalArgumentException("Mapped property '" + name + "' does not exist."); 176 } 177 178 return subset.getProperty(key); 179 } 180 181 @Override 182 public DynaClass getDynaClass() 183 { 184 return new ConfigurationDynaClass(getConfiguration()); 185 } 186 187 @Override 188 public void remove(final String name, final String key) 189 { 190 final Configuration subset = new SubsetConfiguration(getConfiguration(), name, PROPERTY_DELIMITER); 191 subset.setProperty(key, null); 192 } 193 194 @Override 195 public void set(final String name, final int index, final Object value) 196 { 197 if (!checkIndexedProperty(name) && index > 0) 198 { 199 throw new IllegalArgumentException("Property '" + name 200 + "' is not indexed."); 201 } 202 203 final Object property = getConfiguration().getProperty(name); 204 205 if (property instanceof List) 206 { 207 // This is safe because multiple values of a configuration property 208 // are always stored as lists of type Object. 209 @SuppressWarnings("unchecked") 210 final 211 List<Object> list = (List<Object>) property; 212 list.set(index, value); 213 getConfiguration().setProperty(name, list); 214 } 215 else if (property.getClass().isArray()) 216 { 217 Array.set(property, index, value); 218 } 219 else if (index == 0) 220 { 221 getConfiguration().setProperty(name, value); 222 } 223 } 224 225 @Override 226 public void set(final String name, final String key, final Object value) 227 { 228 getConfiguration().setProperty(name + "." + key, value); 229 } 230 231 /** 232 * Tests whether the given name references an indexed property. This 233 * implementation tests for properties of type list or array. If the 234 * property does not exist, an exception is thrown. 235 * 236 * @param name the name of the property to check 237 * @return a flag whether this is an indexed property 238 * @throws IllegalArgumentException if the property does not exist 239 */ 240 private boolean checkIndexedProperty(final String name) 241 { 242 final Object property = getConfiguration().getProperty(name); 243 244 if (property == null) 245 { 246 throw new IllegalArgumentException("Property '" + name 247 + "' does not exist."); 248 } 249 250 return property instanceof List || property.getClass().isArray(); 251 } 252}