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.AbstractBeanInvocationHandler;
019 import org.ini4j.spi.BeanTool;
020 import org.ini4j.spi.IniHandler;
021
022 import java.lang.reflect.Array;
023 import java.lang.reflect.Proxy;
024
025 import java.util.regex.Matcher;
026 import java.util.regex.Pattern;
027
028 public class BasicProfile extends CommonMultiMap<String, Profile.Section> implements Profile
029 {
030 private static final String SECTION_SYSTEM_PROPERTIES = "@prop";
031 private static final String SECTION_ENVIRONMENT = "@env";
032 private static final Pattern EXPRESSION = Pattern.compile("(?<!\\\\)\\$\\{(([^\\[]+)(\\[([0-9]+)\\])?/)?([^\\[^/]+)(\\[(([0-9]+))\\])?\\}");
033 private static final int G_SECTION = 2;
034 private static final int G_SECTION_IDX = 4;
035 private static final int G_OPTION = 5;
036 private static final int G_OPTION_IDX = 7;
037 private static final long serialVersionUID = -1817521505004015256L;
038 private String _comment;
039 private final boolean _propertyFirstUpper;
040 private final boolean _treeMode;
041
042 public BasicProfile()
043 {
044 this(false, false);
045 }
046
047 public BasicProfile(boolean treeMode, boolean propertyFirstUpper)
048 {
049 _treeMode = treeMode;
050 _propertyFirstUpper = propertyFirstUpper;
051 }
052
053 @Override public String getComment()
054 {
055 return _comment;
056 }
057
058 @Override public void setComment(String value)
059 {
060 _comment = value;
061 }
062
063 @Override public Section add(String name)
064 {
065 if (isTreeMode())
066 {
067 int idx = name.lastIndexOf(getPathSeparator());
068
069 if (idx > 0)
070 {
071 String parent = name.substring(0, idx);
072
073 if (!containsKey(parent))
074 {
075 add(parent);
076 }
077 }
078 }
079
080 Section section = newSection(name);
081
082 add(name, section);
083
084 return section;
085 }
086
087 @Override public void add(String section, String option, Object value)
088 {
089 getOrAdd(section).add(option, value);
090 }
091
092 @Override public <T> T as(Class<T> clazz)
093 {
094 return as(clazz, null);
095 }
096
097 @Override public <T> T as(Class<T> clazz, String prefix)
098 {
099 return clazz.cast(Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[] { clazz },
100 new BeanInvocationHandler(prefix)));
101 }
102
103 @Override public String fetch(Object sectionName, Object optionName)
104 {
105 Section sec = get(sectionName);
106
107 return (sec == null) ? null : sec.fetch(optionName);
108 }
109
110 @Override public <T> T fetch(Object sectionName, Object optionName, Class<T> clazz)
111 {
112 Section sec = get(sectionName);
113
114 return (sec == null) ? BeanTool.getInstance().zero(clazz) : sec.fetch(optionName, clazz);
115 }
116
117 @Override public String get(Object sectionName, Object optionName)
118 {
119 Section sec = get(sectionName);
120
121 return (sec == null) ? null : sec.get(optionName);
122 }
123
124 @Override public <T> T get(Object sectionName, Object optionName, Class<T> clazz)
125 {
126 Section sec = get(sectionName);
127
128 return (sec == null) ? BeanTool.getInstance().zero(clazz) : sec.get(optionName, clazz);
129 }
130
131 @Override public String put(String sectionName, String optionName, Object value)
132 {
133 return getOrAdd(sectionName).put(optionName, value);
134 }
135
136 @Override public Section remove(Section section)
137 {
138 return remove((Object) section.getName());
139 }
140
141 @Override public String remove(Object sectionName, Object optionName)
142 {
143 Section sec = get(sectionName);
144
145 return (sec == null) ? null : sec.remove(optionName);
146 }
147
148 boolean isTreeMode()
149 {
150 return _treeMode;
151 }
152
153 char getPathSeparator()
154 {
155 return PATH_SEPARATOR;
156 }
157
158 boolean isPropertyFirstUpper()
159 {
160 return _propertyFirstUpper;
161 }
162
163 Section newSection(String name)
164 {
165 return new BasicProfileSection(this, name);
166 }
167
168 void resolve(StringBuilder buffer, Section owner)
169 {
170 Matcher m = EXPRESSION.matcher(buffer);
171
172 while (m.find())
173 {
174 String sectionName = m.group(G_SECTION);
175 String optionName = m.group(G_OPTION);
176 int optionIndex = parseOptionIndex(m);
177 Section section = parseSection(m, owner);
178 String value = null;
179
180 if (SECTION_ENVIRONMENT.equals(sectionName))
181 {
182 value = Config.getEnvironment(optionName);
183 }
184 else if (SECTION_SYSTEM_PROPERTIES.equals(sectionName))
185 {
186 value = Config.getSystemProperty(optionName);
187 }
188 else if (section != null)
189 {
190 value = (optionIndex == -1) ? section.fetch(optionName) : section.fetch(optionName, optionIndex);
191 }
192
193 if (value != null)
194 {
195 buffer.replace(m.start(), m.end(), value);
196 m.reset(buffer);
197 }
198 }
199 }
200
201 void store(IniHandler formatter)
202 {
203 formatter.startIni();
204 store(formatter, getComment());
205 for (Ini.Section s : values())
206 {
207 store(formatter, s);
208 }
209
210 formatter.endIni();
211 }
212
213 void store(IniHandler formatter, Section s)
214 {
215 store(formatter, getComment(s.getName()));
216 formatter.startSection(s.getName());
217 for (String name : s.keySet())
218 {
219 store(formatter, s, name);
220 }
221
222 formatter.endSection();
223 }
224
225 void store(IniHandler formatter, String comment)
226 {
227 if ((comment != null) && (comment.length() != 0))
228 {
229 formatter.handleComment(comment);
230 }
231 }
232
233 void store(IniHandler formatter, Section section, String option)
234 {
235 store(formatter, section.getComment(option));
236 int n = section.length(option);
237
238 for (int i = 0; i < n; i++)
239 {
240 store(formatter, section, option, i);
241 }
242 }
243
244 void store(IniHandler formatter, Section section, String option, int index)
245 {
246 formatter.handleOption(option, section.get(option, index));
247 }
248
249 private Section getOrAdd(String sectionName)
250 {
251 Section section = get(sectionName);
252
253 return ((section == null)) ? add(sectionName) : section;
254 }
255
256 private int parseOptionIndex(Matcher m)
257 {
258 return (m.group(G_OPTION_IDX) == null) ? -1 : Integer.parseInt(m.group(G_OPTION_IDX));
259 }
260
261 private Section parseSection(Matcher m, Section owner)
262 {
263 String sectionName = m.group(G_SECTION);
264 int sectionIndex = parseSectionIndex(m);
265
266 return (sectionName == null) ? owner : ((sectionIndex == -1) ? get(sectionName) : get(sectionName, sectionIndex));
267 }
268
269 private int parseSectionIndex(Matcher m)
270 {
271 return (m.group(G_SECTION_IDX) == null) ? -1 : Integer.parseInt(m.group(G_SECTION_IDX));
272 }
273
274 private final class BeanInvocationHandler extends AbstractBeanInvocationHandler
275 {
276 private final String _prefix;
277
278 private BeanInvocationHandler(String prefix)
279 {
280 _prefix = prefix;
281 }
282
283 @Override protected Object getPropertySpi(String property, Class<?> clazz)
284 {
285 String key = transform(property);
286 Object o = null;
287
288 if (containsKey(key))
289 {
290 if (clazz.isArray())
291 {
292 o = Array.newInstance(clazz.getComponentType(), length(key));
293 for (int i = 0; i < length(key); i++)
294 {
295 Array.set(o, i, get(key, i).as(clazz.getComponentType()));
296 }
297 }
298 else
299 {
300 o = get(key).as(clazz);
301 }
302 }
303
304 return o;
305 }
306
307 @Override protected void setPropertySpi(String property, Object value, Class<?> clazz)
308 {
309 String key = transform(property);
310
311 remove(key);
312 if (value != null)
313 {
314 if (clazz.isArray())
315 {
316 for (int i = 0; i < Array.getLength(value); i++)
317 {
318 Section sec = add(key);
319
320 sec.from(Array.get(value, i));
321 }
322 }
323 else
324 {
325 Section sec = add(key);
326
327 sec.from(value);
328 }
329 }
330 }
331
332 @Override protected boolean hasPropertySpi(String property)
333 {
334 return containsKey(transform(property));
335 }
336
337 String transform(String property)
338 {
339 String ret = (_prefix == null) ? property : (_prefix + property);
340
341 if (isPropertyFirstUpper())
342 {
343 StringBuilder buff = new StringBuilder();
344
345 buff.append(Character.toUpperCase(property.charAt(0)));
346 buff.append(property.substring(1));
347 ret = buff.toString();
348 }
349
350 return ret;
351 }
352 }
353 }