/*   Copyright 2004 BEA Systems, Inc.
 *
 *   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 com.bea.xml.stream;

import com.bea.xml.stream.util.CircularQueue;
import com.bea.xml.stream.util.ElementTypeNames;

import javax.xml.stream.events.XMLEvent;
import javax.xml.stream.events.Characters;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.util.XMLEventAllocator;
import javax.xml.stream.util.XMLEventConsumer;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLInputFactory;

/**
 * <p>The base reader class.</p>
 */

public class XMLEventReaderBase 
  implements XMLEventReader, XMLEventConsumer
{
  private CircularQueue elementQ = new CircularQueue();
  private boolean open = true;
  protected XMLStreamReader reader;
  protected XMLEventAllocator allocator;
  private boolean reachedEOF=false;
  private ConfigurationContextBase configurationContext;

  public XMLEventReaderBase(XMLStreamReader reader)

    throws XMLStreamException
  {
    this(reader, new XMLEventAllocatorBase());
  }

  public XMLEventReaderBase(XMLStreamReader reader,
                            XMLEventAllocator alloc) 
    throws XMLStreamException
  {
    if (reader==null) 
      throw new IllegalArgumentException("XMLStreamReader may not be null");
    if (alloc==null) 
      throw new IllegalArgumentException("XMLEventAllocator may not be null");

    this.reader = reader;
    open = true;
    // create the allocator
    this.allocator = alloc;
    //    System.out.println("Allocator->"+allocator);

    // This check fills the information from the XMLDeclaration
    // into the startdocument event
    if (reader.getEventType()==XMLEvent.START_DOCUMENT) {
      XMLEvent e = allocator.allocate(reader);
      reader.next();
      add(e);
    }
  }


  public void setAllocator(XMLEventAllocator allocator) {
    if (allocator == null)
      throw new IllegalArgumentException("XMLEvent Allocator may not be null");

    this.allocator = allocator;
  }

  public String getElementText() throws XMLStreamException {
    StringBuffer buf = new StringBuffer();
    XMLEvent e = nextEvent();
    if (!e.isStartElement())
      throw new XMLStreamException("Precondition for readText is nextEvent().getTypeEventType() == START_ELEMENT (got "+e.getEventType()+")");
    while(hasNext()) {
      e = peek();
      if(e.isStartElement())
        throw new XMLStreamException("Unexpected Element start");
      if(e.isCharacters())
        buf.append(((Characters) e).getData());
      if(e.isEndElement())
        return buf.toString();
      nextEvent();
    } 
    throw new XMLStreamException("Unexpected end of Document");

  }

  public XMLEvent nextTag() throws XMLStreamException {
    while(hasNext()) {
      XMLEvent e = nextEvent();
      if (e.isCharacters() && !((Characters) e).isWhiteSpace())
        throw new XMLStreamException("Unexpected text");
      if (e.isStartElement() || e.isEndElement())
        return e;
    }
    throw new XMLStreamException("Unexpected end of Document");
  }

  public Object next() {
    try {
      return nextEvent();
    } catch (XMLStreamException e) {
      return null;
    }
  }

  public XMLEvent nextEvent()
    throws XMLStreamException
  {
    // FIXME cfry throw error if parseSome fails
    if (needsMore()) {
        if (!parseSome())
            throw new java.util.NoSuchElementException("Attempt to call nextEvent() on a stream with no more elements");
    }
    return get();
  }

  public boolean hasNext() 
  {
    if (!open) return false;
    if (!elementQ.isEmpty()) return true;
    try {
      if (reader.hasNext()) return true;
    } catch (XMLStreamException e) {
      return false;
    }
    open = false;
    return false;
  }

  public XMLEvent peek() 
    throws XMLStreamException
  {
    if (!elementQ.isEmpty()) 
      return (XMLEvent) elementQ.peek();
    if (parseSome()) 
      return (XMLEvent) elementQ.peek();

    // Stax specs indicate null should be returned, if no more stuff:
    return null;
  }

  public void add(XMLEvent event) 
    throws XMLStreamException
  {
    elementQ.add(event);
  }

  protected boolean needsMore() {
    return elementQ.isEmpty();
  }

  protected XMLEvent get() 
    throws XMLStreamException
  {
    return (XMLEvent) elementQ.remove();
  }

  protected boolean isOpen() {
    return !reachedEOF;
  }

  protected void internal_close() {
    reachedEOF = true;
  }

  public void close() 
    throws XMLStreamException
  {
    internal_close();
  }
   
  protected boolean parseSome() 
    throws XMLStreamException
  {
    /* 26-Sep-2005, TSa: Should check if we have hit EOF, and if so,
     *   fail to get any more stuff...
     */
    if (reachedEOF) {
        return false;
    }

    //    System.out.println("Allocator->"+allocator);
    allocator.allocate(reader,this);
    if (reader.hasNext())
      reader.next();
    if (reader.getEventType() == XMLEvent.END_DOCUMENT) {
      allocator.allocate(reader,this);
      reachedEOF = true;
    }
    return !needsMore();
  }

  public void setConfigurationContext(ConfigurationContextBase base) {
    configurationContext = base;
  }
  
  public Object getProperty(String name) {
    return configurationContext.getProperty(name);
  }
  public void remove() {
    throw new java.lang.UnsupportedOperationException();
  }
  public static void main(String args[]) throws Exception {

    System.setProperty("javax.xml.stream.XMLInputFactory", 
                       "com.bea.xml.stream.MXParserFactory");
    System.setProperty("javax.xml.stream.XMLEventFactory", 
                       "com.bea.xml.stream.EventFactory");

    XMLInputFactory factory = XMLInputFactory.newInstance();
    XMLEventReader xmlr = factory.createXMLEventReader(new java.io.FileReader(args[0]));
    
    while(xmlr.hasNext()) {
      XMLEvent e = xmlr.nextEvent();
      System.out.println("["+
                         ElementTypeNames.getEventTypeString(e.getEventType())
                         +"]["+
                         e+"]");
    }
  }
  //  public ConfigurationContext getConfigurationContext() {
  //  return new ConfigurationContextBase();
  // }

}
