package javacodebook.xml.processing.sax.parse;

import java.util.*;

import org.xml.sax.*;

/**
 * Der StatefullContentHandler implementiert das ContentHandler-Interface
 * und hat 2 wesentliche generische Funktionen.
 * 1.  Er kann bestimmte Elemente zählen
 * 2.  Er kann alle Werte eines gewünschten Elements in Form eines
 *     Vectors zur Verfügung stellen.
 *
 * @author Christoph Leinemann
 */

public class StatefullContentHandler
    implements ContentHandler {

  private Vector contextVector = new Vector();

  private Hashtable elementCounter = new Hashtable();
  private Hashtable elementDataContainer = new Hashtable();

  // Es folgen Methoden die vom ContentHandler-Interface gefordert werden.
  //
  // Für jedes Ereignis, dass beim Parsen des XML-Dokuments auftreten kann,
  // gibt es hier die entsprechenende Methode, die zur Verarbeitung des
  // Ereignisses implementiert werden kann.  Die meisten der folgenden
  // Implementierungen sind Dummy-Implementierungen.  Im Normalfall würde man
  // in so einem Fall von der org.xml.sax.helpers.DefaultHandler-Klasse erben
  // und nur die Methoden überschreiben, die man wirklich braucht.  Die
  // DefaultHandler-Klasse liefert leere Implementierungen für alle vom
  // ContentHandler-Interface geforderten Methoden.
  public void setDocumentLocator(Locator locator) {
    System.out.println("setDocumentLocator" + locator);
  }

  public void startDocument() throws SAXException {
    // Das Dokument wird als oberster Kontext angemeldet.
    this.startContext("DocumentElement");
    System.out.println("startDocument");
  }

  public void endDocument() throws SAXException {
    // Abmeldung des obersten Kontextes
    this.endContext();
    System.out.println("endDocument");
  }

  public void startPrefixMapping(String prefix, String uri) throws SAXException {
    System.out.println("startPrefixMapping " + prefix + " " + uri);
  }

  public void endPrefixMapping(String prefix) throws SAXException {
    System.out.println("startPrefixMapping " + prefix);
  }

  public void startElement(String namespaceURI, String localName, String qName,
                           Attributes atts) throws SAXException {
    System.out.println("startElement " + localName);

    // für jedes neue Element wird eine neuer Kontext angemeldet.
    this.startContext(localName);

    // falls das Element für eine Zählung registriert ist, zähle
    // den entsprechenden Zähler in der Hashtable eins hoch.
    if (this.elementCounter.containsKey(localName)) {
      ((Counter)this.elementCounter.get(localName)).increase();
    }
  }

  public void endElement(String namespaceURI, String localName, String qName) throws
      SAXException {
    System.out.println("endElement " + localName);

    // Abmeldung des Kontextes
    this.endContext();
  }

  public void characters(char[] ch, int start, int length) throws SAXException {
    System.out.println("characters " + new String(ch,start,length));

    // Falls der Kontext über die extractValuesOfElements-Methode registriert wurde,
    // wird der Inhalt des Elements in einem Vector abgelegt, der in einer
    // Hashtable diesem Elemententyp zugeordnet ist.
    if (this.elementDataContainer.containsKey(this.getContext())) {
      ((Vector)this.elementDataContainer.get(this.getContext())).add(new String(ch,start,length));
    }
  }

  public void ignorableWhitespace(char[] ch, int start, int length) throws
      SAXException {
    System.out.println("ignorableWhitespace " + ch);
  }

  public void processingInstruction(String target, String data) throws
      SAXException {
    System.out.println("processingInstruction " + target + " " + data);
  }

  public void skippedEntity(String name) throws SAXException {
    System.out.println("skippedEntity " + name);
  }

  // Ende der Methoden die vom ContentHandler-Interface gefordert werden.

  /**
   * Diese Methode dient dazu, ein Element für eine Zählung zu registrieren.
   * Sofern ein Element hier registriert wurde, kann über die getCountOfElement-
   * Methode erfragt werden, wie oft es in dem Dokument erschienen ist.
   *
   * @param elementName
   */
  public void countElements(String elementName) {
    this.elementCounter.put(elementName, new Counter());
  }

  /**
   * Falls ein Elementtyp über diese Methode registriert wird, ist nach
   * dem Parsing-Prozess der Inhalt aller Elemente die im Dokument
   * aufgetreten sind über die getValuesOfElement-Methode verfügbar.
   *
   * @param elementName
   */
  public void extractValuesOfElements(String elementName) {
    this.elementDataContainer.put(elementName, new Vector());
  }
  /**
   * Falls ein Elementtyp zuvor über die extractValuesOfElements-
   * Methode registriert wurde, liefert die Methode einen Vector mit
   * Strings von Inhalten aller zugehörigen Elemente des gesamten
   * Dokuments.
   *
   * @param name
   * @return java.util.Vector
   */
  public Vector getValuesOfElement(String name) {
    return (Vector)this.elementDataContainer.get(name);
  }
  /**
   * Liefert die im Dokument aufgetretene Anzahl von Elementen
   * des als Parameter übergebenen Typs oder -1,
   * falls der Elementtyp nicht zur Zählung registriert war.
   *
   * @param type
   * @return int
   */
  public int getCountOfElement(String type) {
    if (this.elementCounter.containsKey(type)) {
      return ((Counter)this.elementCounter.get(type)).getInt();
    }
    else {
    // -1 um zu zeigen, dass der Wert keine Aussage macht, da
    // das Element unter Umständen gar nicht registriert war.
      return -1;
    }
  }

  /**
   * Die Methode dient zur Anmeldung einen neuen Kontextes
   *
   * @param name
   */
  private void startContext(String name) {
    contextVector.add(name);
  }

  /**
   * Die Methode dient zur Abmeldung eines Kontextes
   */
  private void endContext() {
    contextVector.removeElement(contextVector.lastElement());
  }

  /**
   * Die Methode liefert den aktuellen Kontext zurück.
   * Jeweils das zuletzt eingefügte Element im contextVector ist der
   * aktuelle Kontext.
   *
   * @return java.lang.String
   */
  private String getContext() {
    return (String) contextVector.lastElement();
  }
}
// Hilfsklasse zum Zählen
class Counter
{
  private int counter=0;
  protected void increase()
  {
    counter++;
  }
  protected int getInt()
  {
    return counter;
  }
}