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.AbstractBeanInvocationHandler; 019import org.ini4j.spi.BeanTool; 020import org.ini4j.spi.IniHandler; 021 022import java.lang.reflect.Array; 023import java.lang.reflect.Proxy; 024 025import java.util.regex.Matcher; 026import java.util.regex.Pattern; 027 028public 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}