/*
 * Copyright (c) 2003, Morpho Project.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *	  this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *	  this list of conditions and the following disclaimer in the documentation
 *	  and/or other materials provided with the distribution.
 * 3. Neither the name of the Morpho Project nor the names of its contributors
 *	  may be used to endorse or promote products derived from this software
 *	  without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
package jp.morpho.config;

import java.io.InputStream;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

/**
 * <P>
 * このクラスは、配備記述子を使用してオブジェクトを形成します。
 * オブジェクトを形成するには、XML で記述された配備記述子の入力ストリームと形成したいオブジェクトを渡して、configure メソッドを呼び出します。
 * </P>
 * <P>
 * このクラスは、配備記述子に記述されたとおりにオブジェクトのメソッドを呼び出したり、新しいオブジェクトを作成することにより、configure メソッドに渡されたオブジェクトを形成します。
 * </P>
 * @author Kumiko Hiroi
 */
public class Configuration
	extends DefaultHandler
{

	/** トップレベルのノード、通常は configure タグのノード */
	private Node topNode = null;

	/** 読み込み中のノード */
	private Node currentNode = null;

	/** SAX パーサ */
	private SAXParser parser;

	/**
	 * <P>
	 * Configuration オブジェクトを作成します。
	 * </P>
	 * @param validating パーサが XML ドキュメントを検証するように設定されている場合は true、そうでない場合は false
	 */
	public Configuration(boolean validating)
	{
		try
		{
			SAXParserFactory factory = SAXParserFactory.newInstance();
			factory.setValidating(validating);
			parser = factory.newSAXParser();
		}
		catch (Exception e)
		{
			throw new Error(e.toString());
		}
	}

	/**
	 * <P>
	 * Configuration オブジェクトを作成します。
	 * </P>
	 */
	public Configuration()
	{
		this(true);
	}

	/**
	 * <P>
	 * XML 配備記述子を使用してオブジェクトを形成します。
	 * </P>
	 * @param obj 形成するオブジェクト
	 * @param in XML 配備記述子の入力ストリーム
	 * @exception ConfigureException オブジェクトが形成できなかった場合
	 */
	public void configure(Object obj, InputStream in) throws ConfigureException
	{
		try
		{
			parser.parse(in, this);
//			configure(obj, topNode, 0);
			topNode.configure(obj, 0);
		}
		catch (Exception e)
		{
			e.printStackTrace();
			throw new ConfigureException(e);
		}
	}

	/**
	 * <P>
	 * 要素の開始通知を受け取ります。
	 * </P>
	 * <P>
	 * タグの種類により、各種ノードを作成します。
	 * </P>
	 * @param uri 名前空間 URI
	 * @param localName 前置修飾子を含まないローカル名。名前空間処理が行われない場合は空文字列
	 * @param qName 前置修飾子を持つ修飾名。修飾名を使用できない場合は空文字列
	 * @param attributes 指定された属性またはデフォルトの属性
	 * @exception SAXException SAX 例外。ほかの例外をラップしている可能性がある
	 */
	public void startElement(String uri, String localName, String qName, Attributes attributes)
		throws SAXException
	{
		try
		{
			String name = (uri == null || uri.equals("")) ? qName : localName;
			int tag = NodeTypes.getTag(name);
			Node node = null;
			switch (tag)
			{
				case NodeTypes.SET:
					node = new SetNode(currentNode, attributes);
					break;
				case NodeTypes.GET:
					node = new GetNode(currentNode, attributes);
					break;
				case NodeTypes.CALL:
					node = new CallNode(currentNode, attributes);
					break;
				case NodeTypes.PUT:
					node = new PutNode(currentNode, attributes);
					break;
				case NodeTypes.NEW:
					node = new NewNode(currentNode, attributes);
					break;
				case NodeTypes.ARG:
					node = new ArgNode(currentNode, attributes);
					break;
				case NodeTypes.SYSTEMPROPERTY:
					node = new SystemPropertyNode(currentNode, attributes);
					break;
				case NodeTypes.CONFIGURE:
					node = new ConfigureNode(currentNode, attributes);
					break;
				case NodeTypes.ADD:
					node = new AddNode(currentNode, attributes);
					break;
			}
			if (topNode == null)
			{
				topNode = node;
			}
			else
			{
				currentNode.add(node);
			}
			currentNode = node;
		}
		catch (Exception e)
		{
			throw new SAXException(e);
		}
	}

	/**
	 * <P>
	 * 要素の終了通知を受け取ります。
	 * </P>
	 * @param uri 名前空間 URI
	 * @param localName 前置修飾子を含まないローカル名。名前空間処理が行われない場合は空文字列
	 * @param qName 前置修飾子を持つ XML 1.0 修飾名。修飾名を使用できない場合は空文字列
	 * @exception SAXException SAX 例外。ほかの例外をラップしている可能性がある
	 */
	public void endElement(String uri, String localName, String qName) throws SAXException
	{
		currentNode = currentNode.getParent();
	}

	/**
	 * <P>
	 * 要素内の文字データの通知を受け取ります。
	 * </P>
	 * <P>
	 * 要素内の文字データを受け取り、ノードに格納します。
	 * </P>
	 * @param ch 文字
	 * @param start 文字配列内の開始位置
	 * @param length 文字配列から使用される文字数
	 * @exception SAXException SAX 例外。ほかの例外をラップしている可能性がある
	 */
	public void characters(char[] ch, int start, int length) throws SAXException
	{
		currentNode.add(new String(ch, start, length));
	}
}
