com.opengamma.component.ComponentConfigIniLoader.java Source code

Java tutorial

Introduction

Here is the source code for com.opengamma.component.ComponentConfigIniLoader.java

Source

/**
 * 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.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang.StringUtils;
import org.springframework.core.io.Resource;

import com.opengamma.OpenGammaRuntimeException;
import com.opengamma.util.ArgumentChecker;
import com.opengamma.util.ResourceUtils;

/**
 * Loads configuration from the INI 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}</code> is replaced by an earlier replacement declaration<br>
 *  <code>[group]</code> defines the start of a named group of configs<br>
 *  <code>key = value</code> defines a single config element within a group<br>
 *  <code>MANAGER.INCLUDE = resource</code> declares a resource to be included immediately<br>
 *  the "global" group is used to add keys to the set of properties used for replacement<br>
 *  Everything is trimmed as necessary.
 *  <p>
 *  The specified properties are used in two ways.
 *  Firstly, they are used to substitute sections that have defined the '<code>${key}</code>' notation.
 *  Secondly, they can directly override properties in an INI group, if the property has a
 *  key of the format '{@code [group].key}'.
 */
public class ComponentConfigIniLoader extends AbstractComponentConfigLoader {

    /**
     * The pattern to match [group].key
     */
    private static final Pattern GROUP_OVERRIDE = Pattern.compile("\\[" + "([^\\]]+)" + "\\]" + "[.]" + "(.+)");

    /**
     * Creates an instance.
     * 
     * @param logger  the logger, not null
     * @param properties  the properties in use, not null
     */
    public ComponentConfigIniLoader(ComponentLogger logger, ConcurrentMap<String, String> properties) {
        super(logger, properties);
    }

    //-------------------------------------------------------------------------
    /**
     * Loads the INI file.
     * <p>
     * Loads the configuration defining components from the specified resource.
     * 
     * @param resource  the config resource to load, not null
     * @param depth  the depth of the properties file, used for logging
     * @param config  the config being loaded, not null
     */
    public void load(final Resource resource, final int depth, final ComponentConfig config) {
        ArgumentChecker.notNull(resource, "resource");
        ArgumentChecker.notNull(config, "config");
        try {
            doLoad(resource, depth, config);
            overrideProperties(config);

        } catch (RuntimeException ex) {
            throw new OpenGammaRuntimeException("Unable to load INI file: " + resource, ex);
        }
    }

    /**
     * Loads the INI file.
     * <p>
     * Loads the configuration defining components from the specified resource.
     * 
     * @param resource  the config resource to load, not null
     * @param depth  the depth of the properties file, used for logging
     * @param config  the config being loaded, not null
     * @return the config, not null
     */
    private void doLoad(final Resource resource, final int depth, final ComponentConfig config) {
        List<String> lines = readLines(resource);
        String group = null;
        int lineNum = 0;
        for (String line : lines) {
            lineNum++;
            line = line.trim();
            if (line.length() == 0 || line.startsWith("#") || line.startsWith(";")) {
                continue;
            }
            if (line.startsWith("[") && line.endsWith("]")) {
                group = line.substring(1, line.length() - 1);

            } else if (group == null) {
                throw new OpenGammaRuntimeException(
                        "Invalid format, properties must be specified within a [group], line " + lineNum);

            } else {
                int equalsPosition = line.indexOf('=');
                if (equalsPosition < 0) {
                    throw new OpenGammaRuntimeException("Invalid format, line " + lineNum);
                }
                String key = line.substring(0, equalsPosition).trim();
                String value = line.substring(equalsPosition + 1).trim();
                if (key.length() == 0) {
                    throw new IllegalArgumentException("Invalid empty key, line " + lineNum);
                }
                if (config.contains(group, key)) {
                    throw new IllegalArgumentException(
                            "Invalid file, key '" + key + "' specified twice, line " + lineNum);
                }

                // resolve ${} references
                value = resolveProperty(value, lineNum);

                // handle includes
                if (key.equals(ComponentManager.MANAGER_INCLUDE)) {
                    handleInclude(resource, value, depth, config);
                } else {
                    // store property
                    config.put(group, key, value);
                    if (group.equals("global")) {
                        getProperties().put(key, value);
                    }
                }
            }
        }
    }

    /**
     * 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
     * @param config  the config being loaded, not null
     */
    private void handleInclude(final Resource baseResource, String includeFile, final int depth,
            final ComponentConfig config) {
        // find resource
        Resource include;
        try {
            include = ResourceUtils.createResource(includeFile);
        } catch (Exception ex) {
            try {
                include = baseResource.createRelative(includeFile);
            } catch (Exception ex2) {
                throw new OpenGammaRuntimeException(ex2.getMessage(), ex2);
            }
        }

        // load and merge
        getLogger().logInfo(
                StringUtils.repeat(" ", depth) + "   Including item: " + ResourceUtils.getLocation(include));
        try {
            doLoad(include, depth + 1, config);
        } catch (RuntimeException ex) {
            throw new OpenGammaRuntimeException("Unable to load INI file: " + include, ex);
        }
    }

    //-------------------------------------------------------------------------
    /**
     * Override config using the properties.
     * <p>
     * Any property that has a key of the form '[group].key' will replace the
     * specified key within the group.
     * 
     * @param config  the config to update, not null
     */
    private void overrideProperties(final ComponentConfig config) {
        List<String[]> iniProperties = extractIniOverrideProperties();
        for (String[] array : iniProperties) {
            config.getGroup(array[0]); // validate group (but returns a copy of the inner map)
            config.put(array[0], array[1], array[2]);
            getLogger().logDebug("  Replacing group property: [" + array[0] + "]." + array[1] + "=" + array[2]);
        }
    }

    /**
     * Extracts any properties that match the group name style "[group].key".
     * <p>
     * These directly override any INI file settings.
     * 
     * @return the extracted set of INI properties, not null
     */
    private List<String[]> extractIniOverrideProperties() {
        List<String[]> extracted = new ArrayList<String[]>();
        for (String key : getProperties().keySet()) {
            Matcher matcher = GROUP_OVERRIDE.matcher(key);
            if (matcher.matches()) {
                String group = matcher.group(1);
                String propertyKey = matcher.group(2);
                String[] array = { group, propertyKey, getProperties().get(key) };
                extracted.add(array);
            }
        }
        return extracted;
    }

}