/*
 * $Header: /home/cvspublic/jakarta-slide/proposals/tamino/src/util/org/apache/slide/util/Properties.java,v 1.1 2004/03/25 16:18:11 juergen Exp $
 * $Revision: 1.1 $
 * $Date: 2004/03/25 16:18:11 $
 *
 * ====================================================================
 *
 * Copyright 1999-2004 The Apache Software Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.slide.util;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.slide.util.os.Platform;
import org.apache.slide.util.cli.Abort;

/** TODO: merge with config properties. TODO: use for configuration template **/
public class Properties {
    // not a Map -- ordering has to be preserved
    private final List keys;
    private final List values;
    
    public Properties() {
        keys = new ArrayList();
        values = new ArrayList();
    }

    private static final String START = "${";
    private static final String END = "}";
    
    public void add(String key, String value) {
        if (key == null) {
            throw new IllegalArgumentException();
        }
        if (value == null) {
            throw new IllegalArgumentException();
        }
        if (keys.contains(key)) {
            // first definition wins
            return;
        }
        keys.add(key);
        values.add(value);
    }
    
    public void addAll(Properties props) {
        int i;
        int max;
        
        max = props.keys.size();
        for (i = 0; i < max; i++) {
            add((String) props.keys.get(i), (String) props.values.get(i));
        }
    }
    
    public void addAll(Map map) {
        Iterator iter;
        Map.Entry entry;
        
        iter = map.entrySet().iterator();
        while (iter.hasNext()) {
            entry = (Map.Entry) iter.next();
            add(entry.getKey().toString(), entry.getValue().toString());
        }
    }
    
    public void toMap(Map map) {
        int i;
        int max;
        
        max = keys.size();
        for (i = 0; i < max; i++) {
            map.put(keys.get(i), values.get(i));
        }
    }
    
    public void load(File file) throws IOException {
        parse(new IO().readString(file));
    }
    
    public String replace(String template) {
        int start;
        int end;
        StringBuffer result;
        String subst;
        String name;
        int last;
        
        result = new StringBuffer();
        last = 0;
        while (true) {
            start = template.indexOf(START, last);
            if (start == -1) {
                break;
            }
            end = template.indexOf(END, start);
            if (end == -1) {
                throw new IllegalArgumentException("no end after ofs: " + start);
            }
            name = template.substring(start + START.length(), end);
            subst = getOptional(name);
            if (subst == null) {
                throw new IllegalArgumentException("no such property: " + name);
            }
            result.append(template.substring(last, start));
            result.append(subst);
            last = end + END.length();
        }
        result.append(template.substring(last));
        return result.toString();
    }
    
    public void parse(String properties) {
        String[] lines;
        int i;
        String line;
        int idx;
        String left;
        String right;
        
        lines = Strings.split(properties, Platform.CURRENT.lineSeparator);
        for (i = 0; i < lines.length; i++) {
            line = lines[i].trim();
            if (isUsed(line)) {
                idx = line.indexOf('=');
                if (idx == -1) {
                    throw new IllegalArgumentException("missing '=' in line " + line);
                }
                left = line.substring(0, idx).trim();
                right = line.substring(idx + 1).trim();
                
                add(left, replace(right));
            }
        }
    }
    
    private static final char DELIMITER = '.';
    
    public String[] getAll() {
        return Strings.toArray(keys);
    }

    public String[] getChildren(String key) {
        List lst;
        int i;
        int max;
        String prefix;
        int idx;
        
        lst = new ArrayList();  // don't use a set to keep ordering
        max = keys.size();
        prefix = key + DELIMITER;
        for (i = 0; i < max; i++) {
            key = (String) keys.get(i);
            if (key.startsWith(prefix)) {
                idx = key.indexOf(DELIMITER, prefix.length());
                if (idx != -1) {
                    key = key.substring(0, idx);
                }
                if (!lst.contains(key)) {
                    lst.add(key);
                }
            }
        }
        return Strings.toArray(lst);
    }

    public String remove(String key) {
        int idx;
        
        idx = indexOf(key);
        if (idx == -1) {
            return null;
        } else {
            keys.remove(idx);
            return (String) values.remove(idx);
        }
    }
    
    public String getOptional(String key) {
        int idx;
        
        idx = indexOf(key);
        if (idx == -1) {
            return null;
        } else {
            return (String) values.get(idx);
        }
    }
    
    public String get(String name) {
        String value;
        
        value = getOptional(name);
        if (value == null) {
            throw new IllegalArgumentException("mandatory system property not found: " + name);
        }
        return value;
    }
    
    private int indexOf(String key) {
        return keys.indexOf(key);
    }
 
    public int getInt(String key) {
        String str;
        
        str = get(key);
        try {
            return Integer.parseInt(str);
        } catch (NumberFormatException e) {
            throw new IllegalArgumentException(key + ": not a number: " + str);
        }
    }
    
    public File getDirectory(String key) throws Abort {
        String path;
        File file;
        
        path = get(key);
        path = path.replace('/', File.separatorChar);
        file = new File(path);
        file = file.getAbsoluteFile();
        if (!file.isDirectory()) {
            throw new Abort(key + ": no such directory: " + path);
        }
        try {
            file = file.getCanonicalFile();
        } catch (IOException e) {
            throw new XAssertionFailed("unexpected IO exception for existing file", e);
        }
        return file;
    }
    
    public String[] toProcessArgument() {
        int i;
        String[] env;
        
        env = new String[keys.size()];
        for (i = 0; i < env.length; i++) {
            env[i] = ((String) keys.get(i)) + "=" + ((String) values.get(i));
        }
        return env;
    }
    
    public String toString() {
        return Strings.join(toProcessArgument(), "\n");
    }

    private static boolean isUsed(String line) {
        return line.trim().length() > 0 && !line.startsWith("#");
    }
}
