/*   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.ElementTypeNames;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.xml.namespace.QName;

import javax.xml.stream.XMLEventFactory;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.util.XMLEventAllocator;
import javax.xml.stream.util.XMLEventConsumer;
import javax.xml.stream.XMLStreamException;

import javax.xml.stream.events.*;

import com.bea.xml.stream.util.EmptyIterator;
import com.bea.xml.stream.events.DTDEvent;
import com.bea.xml.stream.events.EntityDeclarationEvent;

/**
 * <p> An allocator that creates an event per method call. </p>
 */

public class XMLEventAllocatorBase 
  implements XMLEventAllocator 
{
  XMLEventFactory factory;

  public XMLEventAllocatorBase() {
    factory = XMLEventFactory.newInstance();
  }

  public XMLEventAllocator newInstance() {
    return new XMLEventAllocatorBase();
  }

  public static Iterator getAttributes(XMLStreamReader reader) {
    if (reader.getAttributeCount()==0) return EmptyIterator.emptyIterator;
    int attributeCount = reader.getAttributeCount();
    ArrayList atts = new ArrayList();
    for (int i = 0; i < attributeCount; i++){
      atts.add(new AttributeBase(reader.getAttributePrefix(i),
                                 reader.getAttributeNamespace(i),
                                 reader.getAttributeLocalName(i),
                                 reader.getAttributeValue(i),
                                 reader.getAttributeType(i)));
    }
    return atts.iterator();
  }
  
  public static Iterator getNamespaces(XMLStreamReader reader) {
    if (reader.getNamespaceCount()==0) return EmptyIterator.emptyIterator;
    ArrayList ns = new ArrayList();
    for (int i = 0; i < reader.getNamespaceCount(); i++){  
      String prefix = reader.getNamespacePrefix(i);
      if(prefix == null ||
         prefix.equals("")){
        ns.add(new NamespaceBase(reader.getNamespaceURI(i)));
      } else {
        ns.add(new NamespaceBase(prefix,
                                 reader.getNamespaceURI(i)));
      }
    }
    return ns.iterator();
  }

  public StartElement allocateStartElement(XMLStreamReader reader) 
    throws XMLStreamException 
  {
    String prefix = reader.getPrefix();
    String uri = reader.getNamespaceURI();
    if (prefix == null) prefix = "";
    if (uri == null) uri = "";
    return factory.createStartElement(prefix,
                                      uri,
                                      reader.getLocalName(),
                                      getAttributes(reader),
                                      getNamespaces(reader));
  }

  public EndElement allocateEndElement(XMLStreamReader reader) 
    throws XMLStreamException 
  {
    String prefix = reader.getPrefix();
    String uri = reader.getNamespaceURI();
    if (prefix == null) prefix = "";
    if (uri == null) uri = "";
    return factory.createEndElement(prefix,
                                    uri,
                                    reader.getLocalName(),
                                    getNamespaces(reader)
                                    );
  }

  public Characters allocateCharacters(XMLStreamReader reader) 
    throws XMLStreamException
  {
    int start = reader.getTextStart();
    int length = reader.getTextLength();
    String result = new String(reader.getTextCharacters(),
                               start,
                               length);
    if (reader.isWhiteSpace())
      return factory.createSpace(result);
    else
      return factory.createCharacters(result);
  }

  public Characters allocateCData(XMLStreamReader reader) 
    throws XMLStreamException
  {
    return factory.createCData(reader.getText());
  }

  public Characters allocateSpace(XMLStreamReader reader) 
    throws XMLStreamException
  {
    return factory.createSpace(reader.getText());
  }

  public EntityReference allocateEntityReference(XMLStreamReader reader) 
    throws XMLStreamException
  {
      // no factory method for entity declarations... weird.
      String name = reader.getLocalName();

      if (reader instanceof MXParser) {
          /* Should be able to get additional information (public/system id
           * for external entities, declaration)... but not yet implemented.
           */
          // !!! TBI
      }
      EntityDeclarationEvent ed = new EntityDeclarationEvent(name, reader.getText());
      return factory.createEntityReference(name, ed);
  }

  public ProcessingInstruction allocatePI(XMLStreamReader reader) 
    throws XMLStreamException
  {
    return factory.createProcessingInstruction(reader.getPITarget(),
                                               reader.getPIData());
  }

  public Comment allocateComment(XMLStreamReader reader) 
    throws XMLStreamException
  {
    return factory.createComment(reader.getText());
  }

  public StartDocument allocateStartDocument(XMLStreamReader reader)
    throws XMLStreamException
  {
    return allocateXMLDeclaration(reader);
  }

  public EndDocument allocateEndDocument(XMLStreamReader reader)
    throws XMLStreamException
  {
    return factory.createEndDocument();
  }

  public DTD allocateDTD(XMLStreamReader reader)
    throws XMLStreamException
  {
      /* 07-Mar-2006, TSa: Need to be able to specify notations and
       *    (external unparsed?) entities contained in the DTD, so can not
       *    use the constructor that just takes String..
       */
      if (reader instanceof MXParser) {
          MXParser mxp = (MXParser) reader;
          DTDEvent evt = new DTDEvent(reader.getText());
          evt.setNotations((List) mxp.getProperty(MXParser.FEATURE_STAX_NOTATIONS));
          evt.setEntities((List) mxp.getProperty(MXParser.FEATURE_STAX_ENTITIES));
          return evt;
      }

      // Blah. Using some other reader...
      return factory.createDTD(reader.getText());
  }

  public StartDocument allocateXMLDeclaration(XMLStreamReader reader)
    throws XMLStreamException
  {
    String encoding = reader.getCharacterEncodingScheme();
    String version = reader.getVersion();
    boolean standalone = reader.isStandalone();
    if (encoding != null && 
        version != null &&
        !standalone ) {
      return factory.createStartDocument(encoding,
                                         version,
                                         standalone);
    }
    if (version != null && 
        encoding != null)
      return factory.createStartDocument(encoding,
                                         version);

    if (encoding != null)
    return factory.createStartDocument(encoding);

    return factory.createStartDocument();
  }
  

  public XMLEvent allocate(XMLStreamReader reader) 
    throws XMLStreamException
  {
    switch (reader.getEventType()) {
    case XMLEvent.START_ELEMENT: return allocateStartElement(reader);
    case XMLEvent.END_ELEMENT: return allocateEndElement(reader);
    case XMLEvent.CHARACTERS: return allocateCharacters(reader);
    case XMLEvent.SPACE: return allocateCharacters(reader);
    case XMLEvent.CDATA: return allocateCData(reader);
    case XMLEvent.ENTITY_REFERENCE: return allocateEntityReference(reader);
    case XMLEvent.PROCESSING_INSTRUCTION: return allocatePI(reader);
    case XMLEvent.COMMENT: return allocateComment(reader);
      //    case XMLEvent.XML_DECLARATION: return allocateXMLDeclaration(reader);
    case XMLEvent.START_DOCUMENT: return allocateStartDocument(reader);
    case XMLEvent.END_DOCUMENT: return allocateEndDocument(reader);
    case XMLEvent.DTD: return allocateDTD(reader);
    default:
      throw new XMLStreamException("Unable to allocate event["+
                                   reader.getEventType()+" , "+
                                   ElementTypeNames.getEventTypeString(reader.getEventType())+"]");
    }
    //    return new com.bea.xml.stream.events.NullEvent();
  }

  public void allocate(XMLStreamReader reader,
                       XMLEventConsumer consumer) 
    throws XMLStreamException
  {
    consumer.add(allocate(reader));
  }

  public String toString() {
    return "NonStaticAllocator";
  }
}





