/*******************************************************************************
 * Copyright (c) 2000, 2008 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package java.util;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintStream;

public class Properties extends Hashtable {

	protected Properties defaults;

public Properties() {
}

public Properties(Properties defaults) {
	this.defaults = defaults;
}

public String getProperty(String key) {
	return getProperty(key, null);
}

public String getProperty(String key, String defaultValue) {
	Properties properties = this;
	while (properties != null) {
		String result = (String)properties.get(key);
		if (result != null) return result;
		properties = properties.defaults;
	}
	return defaultValue;
}

public void list(PrintStream out) {
}

//public void list(PrintWriter out) {
//}

public void load(InputStream in) throws IOException {
	InputStreamReader reader = new InputStreamReader(in, "ISO-8859-1");
	String key = null;
	StringBuffer buffer = new StringBuffer();
	int state = 0, nextState = 0, read;
	boolean ignoreLineFeed = false;
	while ((read = reader.read()) != -1) {
		char c = (char)read;
		if (ignoreLineFeed && c == '\n') {
			ignoreLineFeed = false;
			continue;
		}
		switch (state) {
			case 0: // initial state
				switch (c) {
					case ' ':
					case '\t':
					case '\f':
					case '\r':
					case '\n':
						break;
					case '#':
					case '!':
						state = 1;
						break;
					case '\\':
						nextState  = 3;
						state = 2;
						break;
					default:
						buffer.append(c);
						state = 3;
				}
				break;
			case 1: // skip comment
				switch (c) {
					case '\r':
					case '\n':
						state = 0;
						break;
				}
				break;
			case 2: // escape \
				switch (c) {
					case '\\':
						buffer.append("\\");
						break;
					case 't':
						buffer.append("\t");
						break;
					case 'r':
						buffer.append("\r");
						break;
					case 'n':
						buffer.append("\n");
						break;
					case 'f':
						buffer.append("\f");
						break;
					case '\r':
						ignoreLineFeed = true;
						break;
					case '\n':
						break;
					case 'u':
						int r = uncodeEscape(reader.read());
						r = (r << 4) | uncodeEscape(reader.read());
						r = (r << 4) | uncodeEscape(reader.read());
						r = (r << 4) | uncodeEscape(reader.read());
						buffer.append((char)r);
						break;
					default:
						buffer.append(c);
						break;
				}
				state = nextState;
				break;
			case 3: // reading key
				switch (c) {
					case '\\':
						nextState = 3;
						state = 2;
						break;
					case '\r':
					case '\n':
						state = 0;
						put(buffer.toString(), "");
						buffer.setLength(0);
						break;
					case ' ':
					case '\t':
					case '\f':
						break;
					case ':':
					case '=':
						state = 4;
						key = buffer.toString();
						buffer.setLength(0);
						break;
					default:
						buffer.append(c);
						break;
				}
				break;
			case 4: // skipping leading spaces in value
				switch (c) {
					case ' ':
					case '\t':
					case '\f':
						break;
					case '\\':
						nextState = 5;
						state = 2;
						break;
					case '\r':
					case '\n':
						state = 0;
						put(key, "");
						key = null;
						buffer.setLength(0);
						break;
					default:
						state = 5;
						buffer.append(c);
						break;
				}
				break;
			case 5: // reading value
				switch (c) {
					case '\\':
						nextState = 5;
						state = 2;
						break;
					case '\r':
					case '\n':
						state = 0;
						put(key, buffer.toString());
						key = null;
						buffer.setLength(0);
						break;
					default:
						buffer.append(c);
						break;
				}
				break;
				
		}
	}
	if (key != null) {
		put(key, buffer.toString());
		key = null;
		buffer.setLength(0);
	} else {
		if (buffer.length() != 0) {
			put(buffer.toString(), "");
		}
	}
}

void out (String str, boolean key) {
	//TODO
}

public Enumeration propertyNames() {
	return null;
}

public void save(OutputStream out, String header) {
	try {
		store(out, header);
	} catch (IOException e) {}
}

public Object setProperty(String key, String value) {
	return put(key, value);
}

public void store(OutputStream out, String header) throws IOException {
	OutputStreamWriter writer = new OutputStreamWriter(out, "ISO-8859-1");
	String lineSeparator = System.getProperty("line.separator");
	if (header != null) {
		writer.write("#");
		writer.write(header);
		writer.write(lineSeparator);
	}
	writer.write("#");
	writer.write(new Date().toString());
	writer.write(lineSeparator);
	Enumeration enumeration = keys();
	while (enumeration.hasMoreElements()) {
		String key = (String)enumeration.nextElement();
		String value = (String)get(key);
		out (key, true);
		writer.write("=");
		out (value, false);
	}	
	writer.flush();
}

static int uncodeEscape(int c) {
	if (c != -1) {
		if ('A' <= c && c <= 'F') return (c - 'A') + 10;
		if ('a' <= c && c <= 'f') return (c - 'a') + 10;
		if ('0' <= c && c <= '9') return c - '0';
	}
	throw new IllegalArgumentException("Malformed unicode escape");
}

}
