001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019 020package org.apache.isis.core.commons.config; 021 022import java.awt.Color; 023import java.awt.Font; 024import java.util.Enumeration; 025import java.util.Iterator; 026import java.util.Map; 027import java.util.Properties; 028import java.util.StringTokenizer; 029 030import com.google.common.collect.Maps; 031 032import org.slf4j.Logger; 033import org.slf4j.LoggerFactory; 034 035import org.apache.isis.core.commons.debug.DebugBuilder; 036import org.apache.isis.core.commons.exceptions.IsisException; 037import org.apache.isis.core.commons.resource.ResourceStreamSource; 038 039public class IsisConfigurationDefault implements IsisConfiguration { 040 041 private static final Logger LOG = LoggerFactory.getLogger(IsisConfigurationDefault.class); 042 private final Properties properties = new Properties(); 043 private final ResourceStreamSource resourceStreamSource; 044 045 // //////////////////////////////////////////////// 046 // Constructor 047 // //////////////////////////////////////////////// 048 049 public IsisConfigurationDefault() { 050 this(null); 051 } 052 053 public IsisConfigurationDefault(final ResourceStreamSource resourceStreamSource) { 054 this.resourceStreamSource = resourceStreamSource; 055 LOG.debug("configuration initialised with stream: " + nameOf(resourceStreamSource)); 056 } 057 058 private String nameOf(final ResourceStreamSource resourceStreamSource) { 059 return resourceStreamSource != null ? resourceStreamSource.getName() : null; 060 } 061 062 // //////////////////////////////////////////////// 063 // ResourceStreamSource 064 // //////////////////////////////////////////////// 065 066 @Override 067 public ResourceStreamSource getResourceStreamSource() { 068 return resourceStreamSource; 069 } 070 071 // //////////////////////////////////////////////// 072 // add 073 // //////////////////////////////////////////////// 074 075 /** 076 * How to handle the case when the configuration already contains the key being added. 077 */ 078 public enum ContainsPolicy { 079 /** 080 * If the configuration already contains the key, then ignore the new value. 081 */ 082 IGNORE, 083 /** 084 * If the configuration already contains the key, then overwrite with the new. 085 */ 086 OVERWRITE, 087 /** 088 * If the configuration already contains the key, then throw an exception. 089 */ 090 EXCEPTION 091 } 092 093 /** 094 * Add the properties from an existing Properties object; if the key exists in the configuration then will be ignored. 095 * 096 * @see #add(Properties, ContainsPolicy) 097 * @see #put(Properties) 098 */ 099 public void add(final Properties properties) { 100 add(properties, ContainsPolicy.IGNORE); 101 } 102 103 /** 104 * Add the properties from an existing Properties object; if the key exists in the configuration then will be overwritten. 105 * 106 * @see #add(Properties) 107 * @see #add(Properties, ContainsPolicy) 108 */ 109 public void put(final Properties properties) { 110 add(properties, ContainsPolicy.OVERWRITE); 111 } 112 113 /** 114 * Add the properties from an existing Properties object; if the key exists in the configuration then the 115 * {@link ContainsPolicy} will be applied. 116 * 117 * @see #add(Properties) 118 * @see #put(Properties) 119 */ 120 public void add(final Properties properties, final ContainsPolicy policy) { 121 for(Object key: properties.keySet()) { 122 Object value = properties.get(key); 123 add((String)key, (String)value); 124 } 125 } 126 127 /** 128 * Adds a key-value pair to this set of properties; if the key exists in the configuration then will be ignored. 129 * 130 * <p> 131 * @see #add(String, String, ContainsPolicy) 132 * @see #put(String, String) 133 */ 134 public void add(final String key, final String value) { 135 add(key, value, ContainsPolicy.IGNORE); 136 } 137 138 /** 139 * Adds a key-value pair to this set of properties; if the key exists in the configuration then will be replaced. 140 * 141 * <p> 142 * @see #add(String, String) 143 * @see #add(String, String, ContainsPolicy) 144 */ 145 public void put(final String key, final String value) { 146 add(key, value, ContainsPolicy.OVERWRITE); 147 } 148 149 /** 150 * Adds a key-value pair to this set of properties; if the key exists in the configuration then the 151 * {@link ContainsPolicy} will be applied. 152 * 153 * @see #add(String, String) 154 * @see #put(String, String) 155 */ 156 public void add(final String key, final String value, final ContainsPolicy policy) { 157 if (value == null) { 158 LOG.debug("ignoring " + key + " as value is null"); 159 return; 160 } 161 if (key == null) { 162 return; 163 } 164 if (properties.containsKey(key)) { 165 switch (policy) { 166 case IGNORE: 167 LOG.info("ignoring " + key + "=" + value + " as value already set (with " + properties.get(key) + ")" ); 168 break; 169 case OVERWRITE: 170 LOG.info("overwriting " + key + "=" + value + " (previous value was " + properties.get(key) + ")" ); 171 properties.put(key, value); 172 break; 173 case EXCEPTION: 174 throw new IllegalStateException("Configuration already has a key " + key + ", value of " + properties.get(key) ); 175 } 176 } else { 177 LOG.info("adding " + key + "=" + value); 178 properties.put(key, value); 179 } 180 } 181 182 @Override 183 public IsisConfiguration createSubset(final String prefix) { 184 final IsisConfigurationDefault subset = new IsisConfigurationDefault(resourceStreamSource); 185 186 String startsWith = prefix; 187 if (!startsWith.endsWith(".")) { 188 startsWith = startsWith + '.'; 189 } 190 final int prefixLength = startsWith.length(); 191 192 for(Object keyObj: properties.keySet()) { 193 final String key = (String)keyObj; 194 if (key.startsWith(startsWith)) { 195 final String modifiedKey = key.substring(prefixLength); 196 subset.properties.put(modifiedKey, properties.get(key)); 197 } 198 } 199 return subset; 200 } 201 202 // //////////////////////////////////////////////// 203 // getXxx 204 // //////////////////////////////////////////////// 205 206 /** 207 * Gets the boolean value for the specified name where no value or 'on' will 208 * result in true being returned; anything gives false. If no boolean 209 * property is specified with this name then false is returned. 210 * 211 * @param name 212 * the property name 213 */ 214 @Override 215 public boolean getBoolean(final String name) { 216 return getBoolean(name, false); 217 } 218 219 /** 220 * Gets the boolean value for the specified name. If no property is 221 * specified with this name then the specified default boolean value is 222 * returned. 223 * 224 * @param name 225 * the property name 226 * @param defaultValue 227 * the value to use as a default 228 */ 229 @Override 230 public boolean getBoolean(final String name, final boolean defaultValue) { 231 String value = getProperty(name); 232 if (value == null) { 233 return defaultValue; 234 } 235 value = value.toLowerCase(); 236 if (value.equals("on") || value.equals("yes") || value.equals("true") || value.equals("")) { 237 return true; 238 } 239 if (value.equals("off") || value.equals("no") || value.equals("false")) { 240 return false; 241 } 242 243 throw new IsisConfigurationException("Illegal flag for " + name + "; must be one of on, off, yes, no, true or false"); 244 } 245 246 /** 247 * Gets the color for the specified name. If no color property is specified 248 * with this name then null is returned. 249 * 250 * @param name 251 * the property name 252 */ 253 @Override 254 public Color getColor(final String name) { 255 return getColor(name, null); 256 } 257 258 /** 259 * Gets the color for the specified name. If no color property is specified 260 * with this name then the specified default color is returned. 261 * 262 * @param name 263 * the property name 264 * @param defaultValue 265 * the value to use as a default 266 */ 267 @Override 268 public Color getColor(final String name, final Color defaultValue) { 269 final String color = getProperty(name); 270 271 if (color == null) { 272 return defaultValue; 273 } 274 275 return Color.decode(color); 276 } 277 278 @Override 279 public void debugData(final DebugBuilder str) { 280 str.appendln("Resource Stream Source", resourceStreamSource); 281 str.appendln(); 282 final Enumeration<?> names = properties.propertyNames(); 283 while (names.hasMoreElements()) { 284 final String name = (String) names.nextElement(); 285 str.appendln(name, properties.getProperty(name)); 286 } 287 } 288 289 @Override 290 public String debugTitle() { 291 return "Properties Configuration"; 292 } 293 294 /** 295 * Gets the font for the specified name. If no font property is specified 296 * with this name then null is returned. 297 * 298 * @param name 299 * the property name 300 */ 301 @Override 302 public Font getFont(final String name) { 303 return getFont(name, null); 304 } 305 306 /** 307 * Gets the font for the specified name. If no font property is specified 308 * with this name then the specified default font is returned. 309 * 310 * @param name 311 * the property name 312 * @param defaultValue 313 * the color to use as a default 314 */ 315 @Override 316 public Font getFont(final String name, final Font defaultValue) { 317 final String font = getProperty(name); 318 319 if (font == null) { 320 return defaultValue; 321 } 322 323 return Font.decode(font); 324 } 325 326 /** 327 * Gets the number value for the specified name. If no property is specified 328 * with this name then 0 is returned. 329 * 330 * @param name 331 * the property name 332 */ 333 @Override 334 public int getInteger(final String name) { 335 return getInteger(name, 0); 336 } 337 338 /** 339 * Gets the number value for the specified name. If no property is specified 340 * with this name then the specified default number value is returned. 341 * 342 * @param name 343 * the property name 344 * @param defaultValue 345 * the value to use as a default 346 */ 347 @Override 348 public int getInteger(final String name, final int defaultValue) { 349 final String value = getProperty(name); 350 351 if (value == null) { 352 return defaultValue; 353 } 354 355 return Integer.valueOf(value).intValue(); 356 } 357 358 @Override 359 public String[] getList(final String name) { 360 final String list = getString(name); 361 if (list == null) { 362 return new String[0]; 363 } else { 364 final StringTokenizer tokens = new StringTokenizer(list, ConfigurationConstants.LIST_SEPARATOR); 365 final String array[] = new String[tokens.countTokens()]; 366 int i = 0; 367 while (tokens.hasMoreTokens()) { 368 array[i++] = tokens.nextToken().trim(); 369 } 370 return array; 371 } 372 } 373 374 @Override 375 public IsisConfiguration getProperties(final String withPrefix) { 376 final int prefixLength = "".length(); 377 378 final Properties pp = new Properties(); 379 final Enumeration<?> e = properties.keys(); 380 while (e.hasMoreElements()) { 381 final String key = (String) e.nextElement(); 382 if (key.startsWith(withPrefix)) { 383 final String modifiedKey = key.substring(prefixLength); 384 pp.put(modifiedKey, properties.get(key)); 385 } 386 } 387 final IsisConfigurationDefault isisConfigurationDefault = new IsisConfigurationDefault(resourceStreamSource); 388 isisConfigurationDefault.add(pp); 389 return isisConfigurationDefault; 390 } 391 392 private String getProperty(final String name) { 393 return getProperty(name, null); 394 } 395 396 private String getProperty(final String name, final String defaultValue) { 397 final String key = referedToAs(name); 398 if (key.indexOf("..") >= 0) { 399 throw new IsisException("property names should not have '..' within them: " + name); 400 } 401 String property = properties.getProperty(key, defaultValue); 402 property = property != null ? property.trim() : null; 403 LOG.debug("get property: '" + key + "' = '" + property + "'"); 404 return property; 405 } 406 407 /** 408 * Returns the configuration property with the specified name. If there is 409 * no matching property then null is returned. 410 */ 411 @Override 412 public String getString(final String name) { 413 return getProperty(name); 414 } 415 416 @Override 417 public String getString(final String name, final String defaultValue) { 418 return getProperty(name, defaultValue); 419 } 420 421 @Override 422 public boolean hasProperty(final String name) { 423 final String key = referedToAs(name); 424 return properties.containsKey(key); 425 } 426 427 @Override 428 public boolean isEmpty() { 429 return properties.isEmpty(); 430 } 431 432 @Override 433 public Iterator<String> iterator() { 434 return properties.stringPropertyNames().iterator(); 435 } 436 437 /** 438 * Returns as a String that the named property is refered to as. For example 439 * in a simple properties file the property z might be specified in the file 440 * as x.y.z. 441 */ 442 private String referedToAs(final String name) { 443 return name; 444 } 445 446 @Override 447 public int size() { 448 return properties.size(); 449 } 450 451 @Override 452 public String toString() { 453 return "ConfigurationParameters [properties=" + properties + "]"; 454 } 455 456 // //////////////////////////////////////////////////////////////////// 457 // injectInto 458 // //////////////////////////////////////////////////////////////////// 459 460 @Override 461 public void injectInto(final Object candidate) { 462 if (IsisConfigurationAware.class.isAssignableFrom(candidate.getClass())) { 463 final IsisConfigurationAware cast = IsisConfigurationAware.class.cast(candidate); 464 cast.setConfiguration(this); 465 } 466 } 467 468 @Override 469 public Map<String,String> asMap() { 470 final Map<String, String> map = Maps.newHashMap(); 471 for(String propertyName: this) { 472 final String propertyValue = this.getProperty(propertyName); 473 map.put(propertyName, propertyValue); 474 } 475 return map; 476 } 477 478}