001/* 002 * Copyright 2005,2009 Ivan SZKIBA 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ini4j; 017 018import org.ini4j.spi.BeanAccess; 019import org.ini4j.spi.BeanTool; 020import org.ini4j.spi.Warnings; 021 022import java.lang.reflect.Array; 023 024import java.util.regex.Matcher; 025import java.util.regex.Pattern; 026 027public class BasicOptionMap extends CommonMultiMap<String, String> implements OptionMap 028{ 029 private static final char SUBST_CHAR = '$'; 030 private static final String SYSTEM_PROPERTY_PREFIX = "@prop/"; 031 private static final String ENVIRONMENT_PREFIX = "@env/"; 032 private static final int SYSTEM_PROPERTY_PREFIX_LEN = SYSTEM_PROPERTY_PREFIX.length(); 033 private static final int ENVIRONMENT_PREFIX_LEN = ENVIRONMENT_PREFIX.length(); 034 private static final Pattern EXPRESSION = Pattern.compile("(?<!\\\\)\\$\\{(([^\\[]+)(\\[([0-9]+)\\])?)\\}"); 035 private static final int G_OPTION = 2; 036 private static final int G_INDEX = 4; 037 private static final long serialVersionUID = 325469712293707584L; 038 private BeanAccess _defaultBeanAccess; 039 private final boolean _propertyFirstUpper; 040 041 public BasicOptionMap() 042 { 043 this(false); 044 } 045 046 public BasicOptionMap(boolean propertyFirstUpper) 047 { 048 _propertyFirstUpper = propertyFirstUpper; 049 } 050 051 @Override @SuppressWarnings(Warnings.UNCHECKED) 052 public <T> T getAll(Object key, Class<T> clazz) 053 { 054 requireArray(clazz); 055 T value; 056 057 value = (T) Array.newInstance(clazz.getComponentType(), length(key)); 058 for (int i = 0; i < length(key); i++) 059 { 060 Array.set(value, i, BeanTool.getInstance().parse(get(key, i), clazz.getComponentType())); 061 } 062 063 return value; 064 } 065 066 @Override public void add(String key, Object value) 067 { 068 super.add(key, ((value == null) || (value instanceof String)) ? (String) value : String.valueOf(value)); 069 } 070 071 @Override public void add(String key, Object value, int index) 072 { 073 super.add(key, ((value == null) || (value instanceof String)) ? (String) value : String.valueOf(value), index); 074 } 075 076 @Override public <T> T as(Class<T> clazz) 077 { 078 return BeanTool.getInstance().proxy(clazz, getDefaultBeanAccess()); 079 } 080 081 @Override public <T> T as(Class<T> clazz, String keyPrefix) 082 { 083 return BeanTool.getInstance().proxy(clazz, newBeanAccess(keyPrefix)); 084 } 085 086 @Override public String fetch(Object key) 087 { 088 int len = length(key); 089 090 return (len == 0) ? null : fetch(key, len - 1); 091 } 092 093 @Override public String fetch(Object key, int index) 094 { 095 String value = get(key, index); 096 097 if ((value != null) && (value.indexOf(SUBST_CHAR) >= 0)) 098 { 099 StringBuilder buffer = new StringBuilder(value); 100 101 resolve(buffer); 102 value = buffer.toString(); 103 } 104 105 return value; 106 } 107 108 @Override public <T> T fetch(Object key, Class<T> clazz) 109 { 110 return BeanTool.getInstance().parse(fetch(key), clazz); 111 } 112 113 @Override public <T> T fetch(Object key, int index, Class<T> clazz) 114 { 115 return BeanTool.getInstance().parse(fetch(key, index), clazz); 116 } 117 118 @Override @SuppressWarnings(Warnings.UNCHECKED) 119 public <T> T fetchAll(Object key, Class<T> clazz) 120 { 121 requireArray(clazz); 122 T value; 123 124 value = (T) Array.newInstance(clazz.getComponentType(), length(key)); 125 for (int i = 0; i < length(key); i++) 126 { 127 Array.set(value, i, BeanTool.getInstance().parse(fetch(key, i), clazz.getComponentType())); 128 } 129 130 return value; 131 } 132 133 @Override public void from(Object bean) 134 { 135 BeanTool.getInstance().inject(getDefaultBeanAccess(), bean); 136 } 137 138 @Override public void from(Object bean, String keyPrefix) 139 { 140 BeanTool.getInstance().inject(newBeanAccess(keyPrefix), bean); 141 } 142 143 @Override public <T> T get(Object key, Class<T> clazz) 144 { 145 return BeanTool.getInstance().parse(get(key), clazz); 146 } 147 148 @Override public <T> T get(Object key, int index, Class<T> clazz) 149 { 150 return BeanTool.getInstance().parse(get(key, index), clazz); 151 } 152 153 @Override public String put(String key, Object value) 154 { 155 return super.put(key, ((value == null) || (value instanceof String)) ? (String) value : String.valueOf(value)); 156 } 157 158 @Override public String put(String key, Object value, int index) 159 { 160 return super.put(key, ((value == null) || (value instanceof String)) ? (String) value : String.valueOf(value), index); 161 } 162 163 @Override public void putAll(String key, Object value) 164 { 165 if (value != null) 166 { 167 requireArray(value.getClass()); 168 } 169 170 remove(key); 171 if (value != null) 172 { 173 int n = Array.getLength(value); 174 175 for (int i = 0; i < n; i++) 176 { 177 add(key, Array.get(value, i)); 178 } 179 } 180 } 181 182 @Override public void to(Object bean) 183 { 184 BeanTool.getInstance().inject(bean, getDefaultBeanAccess()); 185 } 186 187 @Override public void to(Object bean, String keyPrefix) 188 { 189 BeanTool.getInstance().inject(bean, newBeanAccess(keyPrefix)); 190 } 191 192 synchronized BeanAccess getDefaultBeanAccess() 193 { 194 if (_defaultBeanAccess == null) 195 { 196 _defaultBeanAccess = newBeanAccess(); 197 } 198 199 return _defaultBeanAccess; 200 } 201 202 boolean isPropertyFirstUpper() 203 { 204 return _propertyFirstUpper; 205 } 206 207 BeanAccess newBeanAccess() 208 { 209 return new Access(); 210 } 211 212 BeanAccess newBeanAccess(String propertyNamePrefix) 213 { 214 return new Access(propertyNamePrefix); 215 } 216 217 void resolve(StringBuilder buffer) 218 { 219 Matcher m = EXPRESSION.matcher(buffer); 220 221 while (m.find()) 222 { 223 String name = m.group(G_OPTION); 224 int index = (m.group(G_INDEX) == null) ? -1 : Integer.parseInt(m.group(G_INDEX)); 225 String value; 226 227 if (name.startsWith(ENVIRONMENT_PREFIX)) 228 { 229 value = Config.getEnvironment(name.substring(ENVIRONMENT_PREFIX_LEN)); 230 } 231 else if (name.startsWith(SYSTEM_PROPERTY_PREFIX)) 232 { 233 value = Config.getSystemProperty(name.substring(SYSTEM_PROPERTY_PREFIX_LEN)); 234 } 235 else 236 { 237 value = (index == -1) ? fetch(name) : fetch(name, index); 238 } 239 240 if (value != null) 241 { 242 buffer.replace(m.start(), m.end(), value); 243 m.reset(buffer); 244 } 245 } 246 } 247 248 private void requireArray(Class clazz) 249 { 250 if (!clazz.isArray()) 251 { 252 throw new IllegalArgumentException("Array required"); 253 } 254 } 255 256 class Access implements BeanAccess 257 { 258 private final String _prefix; 259 260 Access() 261 { 262 this(null); 263 } 264 265 Access(String prefix) 266 { 267 _prefix = prefix; 268 } 269 270 @Override public void propAdd(String propertyName, String value) 271 { 272 add(transform(propertyName), value); 273 } 274 275 @Override public String propDel(String propertyName) 276 { 277 return remove(transform(propertyName)); 278 } 279 280 @Override public String propGet(String propertyName) 281 { 282 return fetch(transform(propertyName)); 283 } 284 285 @Override public String propGet(String propertyName, int index) 286 { 287 return fetch(transform(propertyName), index); 288 } 289 290 @Override public int propLength(String propertyName) 291 { 292 return length(transform(propertyName)); 293 } 294 295 @Override public String propSet(String propertyName, String value) 296 { 297 return put(transform(propertyName), value); 298 } 299 300 @Override public String propSet(String propertyName, String value, int index) 301 { 302 return put(transform(propertyName), value, index); 303 } 304 305 private String transform(String orig) 306 { 307 String ret = orig; 308 309 if (((_prefix != null) || isPropertyFirstUpper()) && (orig != null)) 310 { 311 StringBuilder buff = new StringBuilder(); 312 313 if (_prefix != null) 314 { 315 buff.append(_prefix); 316 } 317 318 if (isPropertyFirstUpper()) 319 { 320 buff.append(Character.toUpperCase(orig.charAt(0))); 321 buff.append(orig.substring(1)); 322 } 323 else 324 { 325 buff.append(orig); 326 } 327 328 ret = buff.toString(); 329 } 330 331 return ret; 332 } 333 } 334}