/*
 ************************************************************************
 *
 * Copyright (c) 2003-2004, C&C Research Laboratories, NEC Europe Ltd.
 *
 * Copyright in this software belongs to C&C Research Laboratories,
 * Rathausallee 10, 53757 Sankt Augustin, Germany.
 *
 * This software may not be used, sold, licensed, transferred, copied
 * or reproduced in whole or in part in any manner or form or in or
 * on any media by any person other than in accordance with the terms
 * of the Licence Agreement supplied with the software, or otherwise
 * without the prior written consent of the copyright owners.
 *
 * This software is distributed WITHOUT ANY WARRANTY, without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 * PURPOSE, except where stated in the Licence Agreement supplied with
 * the software.
 *
 *  Created By :           G.A. Kohring
 *  Created for Project :  GEMSS (IST-2001-37153)
 *
 ************************************************************************
 *
 */


package de.nece.ccrle.util;

import java.io.InputStream;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Properties;


/**
 * An extenstion of the <code>Properties</code> class to allow 
 * system properties as variables in property values.
 * <p>
 * This class allows system properties such as <em>user.home</em> to be used 
 * as part of the value string in property files. The syntax for identifying
 * system properties is <em>${name}</em>
 * For example, if a property file exists with, the line:
 * <pre>
 * myapp.config = ${user.home}/myapp.cnf
 * </pre>
 * then after loading this property file, 
 * calling the method <code>getProperty( "myapp.config" )</code> will return
 * a string in which "${user.home}" has been replaced by the value of this
 * system variable. Any valid system variable, including user defined
 * variables can be used anywhere in the property value. E.g.,
 * <pre>
 * myapp.config.os = ${user.home}/myapp/versions/${os.name}/myapp.cnf
 * </pre>
 * The substition occurs when the properties are requested, i.e., when 
 * either the method {@link #getProperty( String key )} or 
 * {@link #getProperty( String key, String defaultValue )} is called.
 * Calling any of the the {@link java.util.Hashtable} methods such as
 * {@link java.util.Hashtable#get(Object key )} returns the
 * original, uninterpreted string.
 * <p>
 * For convenience, the variable ${/} expands to ${file.separator} and
 * ${:} expands to ${path.separator} and
 *
 * @author Greg Kohring
 */
public class VariableProperties extends Properties {

    /**
     * String marking the start of a system variable string. This is the
     * string: <code>${</code>
     */
    public static final String OPEN = "${";

    /**
     * String marking the end of a system variable. This is the
     * string: <code>}</code>
     */
    public static final String CLOSE = "}";

    /**
     * Constructs an empty property list with no default values.
     */
    public VariableProperties() {
        super();
    }

    /**
     * Constructs an empty property list with the specified defaults.
     */
    public VariableProperties( Properties defaults ) {
        super( defaults );
    }


    /**
     * Searches for the property with the specified key in this property list.
     * If the key is not found in this property list, the default property
     * list,     
     * and its defaults, recursively, are then checked. The method
     * returns
     * <code>null</code> if the property is not found.
     * <p>
     * If the property, or its default, contains any system variables, these 
     * will be
     * replaced by their corresponding values before the property is returned.
     *
     * @param   key   the property key.
     * @return  the value in this property list with the specified key value 
     *      and its system variables, if any, replaced by their values.
     */
    public String getProperty( String key ){
        String value = super.getProperty( key );
        if ( value == null ){
            return value;
        } else {
            return expand( value );
        }
    }

    /**
     * Searches for the property with the specified key in this property list.
     * If the key is not found in this property list, the default property
     * list,     
     * and its defaults, recursively, are then checked. The method
     * returns the
     * default value argument if the property is not found.
     * <p>
     * If the property, or its default, contains any system variables, these 
     * will be
     * replaced by their corresponding values before the property is returned.
     *
     * @param   key            the hashtable key.
     * @param   defaultValue   a default value.
     *
     * @return  the value in this property list with the specified key value 
     *      and its system variables, if any, replaced by their values.
     */
    public String getProperty( String key, String defaultValue ){
        String value = super.getProperty( key, defaultValue );
        if ( value == null ){
            return value;
        } else {
            return expand( value );
        }
    }


    /**
     * This method performs the actual variable substitution.
     *
     * @param   value a string value contianing system variables.
     *
     * @return  a string in which all system variables have been replaced
     *      by their corresponding values.
     */
    private String expand( String value ){

        if ( value == null ) return null;

        String val = value;

        int start = -1;
        while ( (start = val.indexOf( OPEN, start ) ) > -1 ){

            int end = val.indexOf( CLOSE, start );
            String sysVar = val.substring( start + OPEN.length(), end );

            if ( sysVar.equals( "/" ) ){
                sysVar = "file.separator";
            } else if ( sysVar.equals( ":" ) ){
                sysVar = "path.separator";
            }

            String sysVal = System.getProperty( sysVar );

            if ( sysVal != null ){
                String before = val.substring( 0, start );
                String after = val.substring( end + CLOSE.length() );
                val = before + sysVal + after;
                start = -1;
            } else {
                start = end;
            }
        }
        
        return val;
    }

}
