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