/**
* Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.component;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.apache.commons.lang.StringUtils;
import org.springframework.core.io.Resource;
import com.opengamma.util.ResourceUtils;
/**
* Loads configuration from the properties format file.
* <p>
* The format is line-based as follows:<br>
* <code>#</code> or <code>;</code> for comment lines (at the start of the line)<br>
* <code>key = value</code> declares a property<br>
* <code>MANAGER.INCLUDE = resource</code> declares a resource to be included immediately<br>
* <code>${key}</code> is replaced by an earlier replacement declaration<br>
* Everything is trimmed as necessary.
* <p>
* The {@link Properties} class is not used for parsing as it does not preserver order.
*/
public class ComponentConfigPropertiesLoader extends AbstractComponentConfigLoader {
/**
* Creates an instance.
*
* @param logger the logger, not null
* @param properties the properties being built up, not null
*/
public ComponentConfigPropertiesLoader(ComponentLogger logger, ConfigProperties properties) {
super(logger, properties);
}
//-------------------------------------------------------------------------
/**
* Loads the properties file.
*
* @param resource the config resource to load, not null
* @param depth the depth of the properties file, used for logging
* @return the next configuration file to load, null if not specified
* @throws ComponentConfigException if the resource cannot be loaded
*/
public String load(final Resource resource, final int depth) {
try {
return doLoad(resource, depth);
} catch (RuntimeException ex) {
throw new ComponentConfigException("Unable to load properties file: " + resource, ex);
}
}
/**
* Loads the properties file.
*
* @param resource the config resource to load, not null
* @param depth the depth of the properties file, used for logging
* @return the next configuration file to load, null if not specified
* @throws ComponentConfigException if the resource cannot be loaded
*/
private String doLoad(final Resource resource, final int depth) {
final Map<String, String> fileProperties = new HashMap<String, String>();
final List<String> lines = readLines(resource);
int lineNum = 0;
for (String line : lines) {
lineNum++;
line = line.trim();
if (line.length() == 0 || line.startsWith("#") || line.startsWith(";")) {
continue;
}
int equalsPosition = line.indexOf('=');
if (equalsPosition < 0) {
throw new ComponentConfigException("Invalid format, line " + lineNum);
}
String key = line.substring(0, equalsPosition).trim();
String value = line.substring(equalsPosition + 1).trim();
if (key.length() == 0) {
throw new ComponentConfigException("Invalid empty key, line " + lineNum);
}
if (fileProperties.containsKey(key)) {
throw new ComponentConfigException("Invalid file, key '" + key + "' specified twice, line " + lineNum);
}
// resolve ${} references
ConfigProperty resolved = getProperties().resolveProperty(key, value, lineNum);
if (resolved != null) {
// handle includes
if (key.equals(ComponentManager.MANAGER_INCLUDE)) {
handleInclude(resource, resolved.getValue(), depth);
} else {
// store property
fileProperties.put(key, resolved.getValue());
if (key.equals(ComponentManager.MANAGER_NEXT_FILE) == false) {
getProperties().addIfAbsent(resolved); // first definition wins
}
}
}
}
return fileProperties.get(ComponentManager.MANAGER_NEXT_FILE);
}
/**
* Handle the inclusion of another file.
*
* @param baseResource the base resource, not null
* @param includeFile the resource to include, not null
* @param depth the depth of the properties file, used for logging
* @throws ComponentConfigException if the included resource cannot be loaded
*/
private void handleInclude(final Resource baseResource, String includeFile, final int depth) {
// find resource
Resource include;
try {
include = ResourceUtils.createResource(includeFile);
} catch (Exception ex) {
try {
include = baseResource.createRelative(includeFile);
} catch (Exception ex2) {
throw new ComponentConfigException(ex2.getMessage(), ex2);
}
}
// load and merge
getLogger().logInfo(StringUtils.repeat(" ", depth) + " Including item: " + ResourceUtils.getLocation(include));
load(include, depth + 1);
}
}