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 boolean remove(Object sectionName, Object optionName) 142 { 143 Section sec = get(sectionName); 144 if (sec == null) { 145 return false; 146 } 147 if (sec.containsKey(optionName)) { 148 sec.remove(optionName); 149 return true; 150 } 151 return false; 152 } 153 154 boolean isTreeMode() 155 { 156 return _treeMode; 157 } 158 159 char getPathSeparator() 160 { 161 return PATH_SEPARATOR; 162 } 163 164 boolean isPropertyFirstUpper() 165 { 166 return _propertyFirstUpper; 167 } 168 169 Section newSection(String name) 170 { 171 return new BasicProfileSection(this, name); 172 } 173 174 void resolve(StringBuilder buffer, Section owner) 175 { 176 Matcher m = EXPRESSION.matcher(buffer); 177 178 while (m.find()) 179 { 180 String sectionName = m.group(G_SECTION); 181 String optionName = m.group(G_OPTION); 182 int optionIndex = parseOptionIndex(m); 183 Section section = parseSection(m, owner); 184 String value = null; 185 186 if (SECTION_ENVIRONMENT.equals(sectionName)) 187 { 188 value = Config.getEnvironment(optionName); 189 } 190 else if (SECTION_SYSTEM_PROPERTIES.equals(sectionName)) 191 { 192 value = Config.getSystemProperty(optionName); 193 } 194 else if (section != null) 195 { 196 value = (optionIndex == -1) ? section.fetch(optionName) : section.fetch(optionName, optionIndex); 197 } 198 199 if (value != null) 200 { 201 buffer.replace(m.start(), m.end(), value); 202 m.reset(buffer); 203 } 204 } 205 } 206 207 void store(IniHandler formatter) 208 { 209 formatter.startIni(); 210 store(formatter, getComment()); 211 for (Ini.Section s : values()) 212 { 213 store(formatter, s); 214 } 215 216 formatter.endIni(); 217 } 218 219 void store(IniHandler formatter, Section s) 220 { 221 store(formatter, getComment(s.getName())); 222 formatter.startSection(s.getName()); 223 for (String name : s.keySet()) 224 { 225 store(formatter, s, name); 226 } 227 228 formatter.endSection(); 229 } 230 231 void store(IniHandler formatter, String comment) 232 { 233 if ((comment != null) && (comment.length() != 0)) 234 { 235 formatter.handleComment(comment); 236 } 237 } 238 239 void store(IniHandler formatter, Section section, String option) 240 { 241 store(formatter, section.getComment(option)); 242 int n = section.length(option); 243 244 for (int i = 0; i < n; i++) 245 { 246 store(formatter, section, option, i); 247 } 248 } 249 250 void store(IniHandler formatter, Section section, String option, int index) 251 { 252 formatter.handleOption(option, section.get(option, index)); 253 } 254 255 private Section getOrAdd(String sectionName) 256 { 257 Section section = get(sectionName); 258 259 return ((section == null)) ? add(sectionName) : section; 260 } 261 262 private int parseOptionIndex(Matcher m) 263 { 264 return (m.group(G_OPTION_IDX) == null) ? -1 : Integer.parseInt(m.group(G_OPTION_IDX)); 265 } 266 267 private Section parseSection(Matcher m, Section owner) 268 { 269 String sectionName = m.group(G_SECTION); 270 int sectionIndex = parseSectionIndex(m); 271 272 return (sectionName == null) ? owner : ((sectionIndex == -1) ? get(sectionName) : get(sectionName, sectionIndex)); 273 } 274 275 private int parseSectionIndex(Matcher m) 276 { 277 return (m.group(G_SECTION_IDX) == null) ? -1 : Integer.parseInt(m.group(G_SECTION_IDX)); 278 } 279 280 private final class BeanInvocationHandler extends AbstractBeanInvocationHandler 281 { 282 private final String _prefix; 283 284 private BeanInvocationHandler(String prefix) 285 { 286 _prefix = prefix; 287 } 288 289 @Override protected Object getPropertySpi(String property, Class<?> clazz) 290 { 291 String key = transform(property); 292 Object o = null; 293 294 if (containsKey(key)) 295 { 296 if (clazz.isArray()) 297 { 298 o = Array.newInstance(clazz.getComponentType(), length(key)); 299 for (int i = 0; i < length(key); i++) 300 { 301 Array.set(o, i, get(key, i).as(clazz.getComponentType())); 302 } 303 } 304 else 305 { 306 o = get(key).as(clazz); 307 } 308 } 309 310 return o; 311 } 312 313 @Override protected void setPropertySpi(String property, Object value, Class<?> clazz) 314 { 315 String key = transform(property); 316 317 remove(key); 318 if (value != null) 319 { 320 if (clazz.isArray()) 321 { 322 for (int i = 0; i < Array.getLength(value); i++) 323 { 324 Section sec = add(key); 325 326 sec.from(Array.get(value, i)); 327 } 328 } 329 else 330 { 331 Section sec = add(key); 332 333 sec.from(value); 334 } 335 } 336 } 337 338 @Override protected boolean hasPropertySpi(String property) 339 { 340 return containsKey(transform(property)); 341 } 342 343 String transform(String property) 344 { 345 String ret = (_prefix == null) ? property : (_prefix + property); 346 347 if (isPropertyFirstUpper()) 348 { 349 StringBuilder buff = new StringBuilder(); 350 351 buff.append(Character.toUpperCase(property.charAt(0))); 352 buff.append(property.substring(1)); 353 ret = buff.toString(); 354 } 355 356 return ret; 357 } 358 } 359}