/*
 * $Header: /home/cvspublic/jakarta-slide/src/share/org/apache/slide/util/conf/Populate.java,v 1.10 2005/06/10 12:34:11 luetzkendorf Exp $
 * $Revision: 1.10 $
 * $Date: 2005/06/10 12:34:11 $
 *
 * ====================================================================
 *
 * Copyright 1999-2002 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.conf;

import java.io.IOException;
import java.util.Stack;

import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;

/**
 * Helper class for reading an XML source and populating an 
 * {@link org.apache.slide.util.conf.Element}.
 */
public class Populate implements ContentHandler, ErrorHandler {
    
    private Element    _root;
    private Element    _current;
    private Locator    _locator;
    private Stack      _content = new Stack();
    
    
    public Element load(InputSource is, XMLReader parser)
        throws SAXException, IOException, ConfigurationException {
        parser.setContentHandler(this);
        parser.parse(is);
        return _root;
    }
    
    
    /**
     * Called to reset this object so it can be used a second time.
     */
    public void reset() {
        _root = null;
    }
    
    
    public void startDocument()
        throws SAXParseException {
        // Starting a new document with the same populate object. This object 
        // is not reusable with a reset.
        if  ( _root != null ) 
            throw new SAXParseException(
                "Cannot start processing a new document without a reset",
                _locator );
    }
    
    
    public void endDocument()
        throws SAXParseException {
        // Not all elements have been closed, but end of document has been 
        // reached. This should never happen.
        if ( _current != null )
            throw new SAXParseException(
                "Not all elements have been closed at end of document.",
                _locator);
    }
    
    
    public void startElement(String namespaceURI,String localName,
                             String qName, Attributes attr ) {
        int     i;
        Element parent;
        
        // If this is the root element, create the first element, else
        // create a child for the current element and process it.
        if ( _current == null ) {
            _current = new Element( qName, null );
            _root = _current;
        } else {
            parent = _current;
            _current = new Element( qName, parent );
            parent.addChild( _current );
        }
        // Set the element's name and the attributes one by one, so the
        // object tree will be a reflection of the XML document.
        _current.setName( qName );
        for ( i = 0 ; i < attr.getLength() ; ++i )
            _current.setAttribute( attr.getQName( i ), attr.getValue( i ) );
        
        _content.push(new StringBuffer());
    }
    
    
    /**
     * Closing the element. This is the place to check special conditions about
     * the element content. If not, just make the parent element the current 
     * element.
     */
    public void endElement(String namespaceURI,String localName,String qName)
    throws SAXParseException {
        // Attempt to close an element when the root element has already been
        // closed. Should never happen.
        if ( _current == null )
            throw new SAXParseException( "Attempt to close the element " + 
                qName + " when root element is already closed.", _locator );
        // Attempt to close one element when a different element is open and
        // waiting to be closed. Should never happen.
        if ( ! _current.getName().equals( qName ) )
            throw new SAXParseException( "Attempt to close the element " + 
                qName + " when the element " + _current.getName() + 
                " should be closed.", _locator );
        
        _current.setData(((StringBuffer)_content.pop()).toString());
        
        // All we have to do is go back to the parent.
        _current = _current.getParent();
    }
    
    
    /**
     * Called when there is character data (text) inside an element. Will 
     * accumulate all the character data and store it inside the object data.
     */
    public void characters( char[] ch, int start, int length )
        throws SAXParseException {

        if ( ch == null || length == 0 ) return;
        
        // Attempt to place character before or after the root element.
        // Should never happen.
        if ( _current == null )
            throw new SAXParseException( 
                "Attempt to place character before or after the root element.",
                _locator );

        ((StringBuffer)this._content.peek()).append(ch, start, length);
    }
     
    
    /**
     * Ignoreable whitespace is just space inside an element that only contains
     * other elements. We can safely ignore it.
     */
    public void ignorableWhitespace( char[] ch, int start, int length ) {
    }
    
    
    public void processingInstruction( String target, String pi ) {
    }
    
    
    public void startPrefixMapping(java.lang.String prefix, 
                                   java.lang.String uri)
        throws SAXException {
    }
    
    
    public void endPrefixMapping(java.lang.String prefix)
        throws SAXException {
    }
    
    
    public void skippedEntity(String name)
        throws SAXException {
    }
    
    
    /**
     * We can store the locate just to know where an error occurs.
     */
    public void setDocumentLocator( Locator locator ) {
        _locator = locator;
    }
    
    
    public void error( SAXParseException except ) {
        System.out.println( except.getMessage() );
    }
    
    
    public void fatalError( SAXParseException except ) {
        System.out.println( except.getMessage() );
    }
    
    
    public void warning( SAXParseException except ) {
        System.out.println( except.getMessage() );
    }
}

