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 }