/*
 * Common Library for TBAS Softwares
 *
 * All Rights Reserved.
 * (c) Copyright 2002 by Tsukuba Bunko.
 *
 * $Id: ResourceLoader.java,v 1.2 2003/11/26 19:05:21 ppoi Exp $
 */
package tsukuba_bunko.resource;

import	java.io.IOException;

import	java.net.URL;

import	java.util.Iterator;
import	java.util.List;

import	javax.xml.parsers.ParserConfigurationException;
import	javax.xml.parsers.SAXParserFactory;

import	org.xml.sax.Attributes;
import	org.xml.sax.ContentHandler;
import	org.xml.sax.Locator;
import	org.xml.sax.SAXException;
import	org.xml.sax.XMLReader;


/**
 * リソースファイルを読み込む機能を提供します.
 * この実装では，マルチスレッドはサポートされません.
 * @author	$Author: ppoi $
 * @version	$Revision: 1.2 $
 */
public class ResourceLoader implements ContentHandler	{

	/**
	 * パースに使用する XMLReader
	 */
	private XMLReader	_parser = null;

	/**
	 * DeserializerMapping
	 */
	private DeserializerMapping	_mapping = DeserializerMapping.newInstance();

	/**
	 * 現在使用中の ResourceDeserializer
	 */
	private ResourceDeserializer	_currentDeserializer = null;

	/**
	 * レベル
	 */
	private int	_level = 0;

	/**
	 * パススタック
	 */
	private List	_stack = null;

	/**
	 * テキストバッファ
	 */
	private StringBuffer	_text = null;


	/**
	 * リソースを管理する Resources
	 */
	private Resources	_resources = null;


	/**
	 * <code>ResourceLoader</code> のインスタンスを作成します.
	 * @exception	ParserConfigurationException	JAXP が正しく構成されていない場合
	 * @exception	SAXException	JAXP が正しく構成されていない場合
	 */
	public ResourceLoader()
		throws ParserConfigurationException, SAXException
	{
		super();

		SAXParserFactory	factory = SAXParserFactory.newInstance();
		factory.setNamespaceAware( true );
		_parser = factory.newSAXParser().getXMLReader();
		_parser.setContentHandler( this );
	}


	/**
	 * リソースの解析に使用する <code>DeserializerMapping</code> を設定します.
	 * @param	mapping	リソースの解析に使用する <code>DeserializerMapping</code>
	 */
	public void setDeserializerMapping( DeserializerMapping mapping )
	{
		_mapping = mapping;
	}

	/**
	 * リソースの解析に使用する <code>DeserializerMapping</code> を取得します.
	 * @return	リソースの解析に使用する <code>DeserializerMapping</code>
	 */
	public DeserializerMapping getDeserializerMapping()
	{
		return _mapping;
	}

	/**
	 * リソースを読み込みます.
	 * @param	resoruceURL	リソースファイルの URL
	 * @param	リソースを登録する <code>ResourceManager</code>
	 */
	public void loadResource( URL resourceURL, Resources resources )
		throws IOException, IllegalResourceException
	{
		try	{
			_resources = resources;
			_parser.parse( resourceURL.toString() );
			_resources = null;
		}
		catch( SAXException se )	{
			throw new IllegalResourceException( se );
		}
	}

	/**
	 * ResourceDeserializer を取得します。
	 * @param	namespaceURI	XML namespace URI
	 * @param	localName	tag name (local part)
	 * @param	qName	tag name (quorified name)
	 * @param	attrs	attributes
	 * @throws	SAXException	不正なデータ型を検出した場合
	 */
	protected ResourceDeserializer getResourceDeserializer( String namespaceURI, String localName, String qName, Attributes attrs )
		throws SAXException
	{
		if( _mapping == null )	{
			return null;
		}

		String	dataType = attrs.getValue( "type" );
		if( dataType != null )	{
			ResourceDeserializer	deserializer = _mapping.getResourceDeserializer( dataType );
			if( deserializer == null )	{
				throw new SAXException( "no deserializer for \"" + dataType + "\"" );
			}
			return deserializer;
		}
		else	{
			return null;
		}
	}

	/**
	 * 現在のパスを計算します.
	 * @return	パス
	 */
	protected String getCurrentPath()
	{
		StringBuffer	buffer = new StringBuffer();
		Iterator	itr = _stack.iterator();
		if( itr.hasNext() )	{
			itr.next();
		}

		if( itr.hasNext() )	{
			buffer.append( itr.next() );
		}
		while( itr.hasNext() )	{
			buffer.append( "." );
			buffer.append( itr.next() );
		}
		return new String(buffer);
	}


//
//	ContentHandler の実装
//
	public void setDocumentLocator( Locator locator )
	{
	}

	public void startPrefixMapping( String namespaceURI, String prefix )
		throws SAXException
	{
		if( _currentDeserializer != null )	{
			_currentDeserializer.startPrefixMapping( namespaceURI, prefix );
		}
	}

	public void endPrefixMapping( String namespaceURI )
		throws SAXException
	{
		if( _currentDeserializer != null )	{
			_currentDeserializer.endPrefixMapping( namespaceURI );
		}
	}

	public void startDocument()
	{
		_stack = new java.util.ArrayList();
		if( _mapping == null )	{
			_mapping = DeserializerMapping.newInstance();
		}
	}

	public void endDocument()
	{
		_stack = null;
	}

	public void startElement( String namespaceURI, String localName, String qName, Attributes attrs )
		throws SAXException
	{
		if( _currentDeserializer != null )	{
			_level++;
			_currentDeserializer.startElement( namespaceURI, localName, qName, attrs );
		}
		else	{
			_stack.add( qName );

			ResourceDeserializer	deserializer = getResourceDeserializer( namespaceURI, localName, qName, attrs );
			if( deserializer != null )	{
				_currentDeserializer = deserializer;
				deserializer.setDeserializerMapping( _mapping );
				_level++;
				deserializer.startDocument();
				deserializer.startElement( namespaceURI, localName, qName, attrs );
			}
			else	{
				_text = new StringBuffer();
			}
		}
	}

	public void endElement( String namespaceURI, String localName, String qName )
		throws SAXException
	{
		if( _currentDeserializer != null )	{
			_level--;
			_currentDeserializer.endElement( namespaceURI, localName, qName );
			if( _level == 0 )	{
				_currentDeserializer.endDocument();
				_resources.setResource( getCurrentPath(), _currentDeserializer.getValue() );
				_stack.remove( _stack.size() - 1 );
				_currentDeserializer = null;
				_currentDeserializer = null;
			}
		}
		else if( _text != null )	{
			_resources.setResource( getCurrentPath(), new String(_text) );
			_stack.remove( _stack.size() - 1 );
		}
		else	{
			_stack.remove( _stack.size() - 1 );
		}
		_text = null;
	}

	public void processingInstruction( String target, String data )
		throws SAXException
	{
		if( _currentDeserializer != null )	{
			_currentDeserializer.processingInstruction( target, data );
		}
	}

	public void skippedEntity( String name )
		throws SAXException
	{
		if( _currentDeserializer != null )	{
			_currentDeserializer.skippedEntity( name );
		}
	}

	public void characters( char[] ch, int begin, int length )
		throws SAXException
	{
		if( _currentDeserializer != null )	{
			_currentDeserializer.characters( ch, begin, length );
		}
		else if( _text != null )	{
			_text.append( ch, begin, length );
		}
	}

	public void ignorableWhitespace( char[] ch, int begin, int length )
		throws SAXException
	{
		if( _currentDeserializer != null )	{
			_currentDeserializer.ignorableWhitespace( ch, begin, length );
		}
	}
}
