/*
 * Copyright 2006 Takahiro Nakamura.
 *
 * 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 woolpack.dom;

import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

import org.w3c.dom.Node;

import woolpack.utils.AppendableWriter;
import woolpack.utils.MapIterableMap;

/**
 * Interpreter デザインパターンの Context 役。
 * 本クラスはスレッドセーフではない。本クラスはリクエストごとに生成することを想定している。
 * @author nakamura
 * 
 */
public class DomContext{
	private String id;
	private Map<String,Object> config;
	private Object container;
	private Map input;
	private Map<String,Object> request;
	private Map<String,Object> session;
	private Map<String,Object> application;
	private Map<String,Object> beforeLocal;
	private Map<String,Object> local;
	private Node node;
	
	/**
	 * コンストラクタ。
	 *
	 */
	public DomContext(){
	}
	
	/**
	 * 浅いコピーコンストラクタ。
	 * @param base コピー元。
	 */
	public DomContext(final DomContext base){
		this.id = base.id;
		this.config = base.config;
		this.container = base.container;
		this.input = base.input;
		this.request = base.request;
		this.session = base.session;
		this.application = base.application;
		this.beforeLocal = (base.local != null)?base.local:base.beforeLocal;
		this.node = base.node;
	}

	/**
	 * 浅いコピー。{@link java.lang.Clonerable}は実装していない。
	 * @return コピーされた{@link DomContext}。
	 */
	public DomContext copy() {
		return new DomContext(this);
	}

	/**
	 * 変更がコピー元{@link DomContext#getLocal()}には反映されない{@link Map}を返す。
	 * @return テンポラリの{@link Map}。
	 */
	public Map<String,Object> getLocal() {
		if(local == null){
			if(beforeLocal == null){
				local = new HashMap<String,Object>();
			}else{
				local = new MapIterableMap<String,Object>(Arrays.asList(new HashMap<String,Object>(), beforeLocal));
			}
		} 
		return local;
	}
	
	/**
	 * このインスタンスの内容を出力する。
	 * テスト・デバッグ用。
	 * @param sb 出力先。
	 * @throws IOException {@link Appendable}が例外を投げた場合。
	 * @throws NullPointerException 引数が null の場合。
	 */
	public void appendTo(final Appendable sb) throws IOException{
		sb.append("DomContext dump information:");
		appendTo(sb, "id", id);
		appendTo(sb, "config", config);
		appendTo(sb, "container", container);
		appendTo(sb, "input", input);
		appendTo(sb, "request", request);
		appendTo(sb, "session", session);
		appendTo(sb, "application", application);
		appendTo(sb, "local", local);
		sb.append("\n,node:");
		if(node != null){
			DomConstants.write(node, new AppendableWriter(sb));
		}
	}
	
	private static void appendTo(final Appendable sb, final String label, final Object o) throws IOException{
		sb.append('\n');
		sb.append(',');
		sb.append(label);
		sb.append(':');
		if(o != null){
			sb.append(o.toString());
		}
	}

	public Node getNode() {
		return node;
	}

	public void setNode(final Node node) {
		this.node = node;
	}

	public Map<String,Object> getConfig() {
		return config;
	}

	public void setConfig(final Map<String,Object> config) {
		this.config = config;
	}

	public Object getContainer() {
		return container;
	}

	public void setContainer(final Object container) {
		this.container = container;
	}

	public Map<String,Object> getRequest() {
		return request;
	}

	public void setRequest(final Map<String,Object> request) {
		this.request = request;
	}

	public Map<String,Object> getSession() {
		return session;
	}

	public void setSession(final Map<String,Object> session) {
		this.session = session;
	}

	/**
	 * idを返す。
	 * コピー先で設定した値はコピー元には反映されない。
	 * @return id。
	 */
	public String getId() {
		return id;
	}

	/**
	 * id を設定する。
	 * コピー先で設定した値はコピー元には反映されない。
	 * @param id id。
	 */
	public void setId(final String id) {
		this.id = id;
	}

	public Map getInput() {
		return input;
	}

	public void setInput(final Map input) {
		this.input = input;
	}

	public Map<String, Object> getApplication() {
		return application;
	}

	public void setApplication(Map<String, Object> application) {
		this.application = application;
	}
}
