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 */
016 package org.ini4j;
017
018 import org.ini4j.spi.BeanAccess;
019 import org.ini4j.spi.BeanTool;
020 import org.ini4j.spi.Warnings;
021
022 import java.lang.reflect.Array;
023
024 import java.util.regex.Matcher;
025 import java.util.regex.Pattern;
026
027 public 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 }