package javakurs.musterloesung.uebung02.aufgabe1;

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

import org.xml.sax.Attributes;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.DefaultHandler;

/**
 * {@link TfsHandler} is a handler for parsing XML files with typed feature
 * structures (TFS). It extends {@link org.xml.sax.helpers.DefaultHandler} and
 * overrides the methods {@code startDocument}, {@code startElement},
 * {@code endElement} and {code error}.
 *
 * @author Joerg Steffen, DFKI
 * @version $Id: TfsHandler.java,v 1.1 2010/11/11 16:51:25 steffen Exp $
 */
public class TfsHandler extends DefaultHandler {

  /**
   * This contains the name of the top-level XML element.
   */
  public static final String FSLIST_ID = "FSLIST";

  /**
   * This contains the name of typed feature structure XML elements.
   */
  public static final String FS_ID = "FS";

  /**
   * This contains the name of feature XML elements.
   */
  public static final String F_ID = "F";

  /**
   * This is the default feature structure type, used when there is no explicit
   * type.
   */
  public static final String TOP_TYPE = "*top*";


  /**
   * This contains the result list of typed feature structures parsed from the
   * input XML document.
   */
  private List<TypedFeatStruct> resultTfsList;

  /**
   * This contains a stack for managing the nested TFS during the construction.
   */
  private Stack<TypedFeatStruct> tfsStack;

  /**
   * This contains a stack for managing the features during the construction
   * of nested typed feature structures.
   */
  private Stack<String> featureStack;


  /**
   * This creates a new instance of {@link TfsHandler}.
   */
  public TfsHandler() {

    // nothing to do here, stacks and results are initialized when document
    // start is parsed
  }


  /**
   * This returns a list of parsed typed feature structures.
   *
   * @return a list of typed feature structures
   */
  public List<TypedFeatStruct> getResultTfsList() {

    return this.resultTfsList;
  }


  /**
   * {@inheritDoc}
   */
  @Override
  public void startDocument() {

    // init stacks and result
    this.resultTfsList = new ArrayList<TypedFeatStruct>();
    this.tfsStack = new Stack<TypedFeatStruct>();
    this.featureStack = new Stack<String>();
  }


  /**
   * {@inheritDoc}
   */
  @Override
  public void startElement(
      String uri, String localName, String qualName, Attributes atts) {

    if (qualName.equals(FS_ID)) {
      // new typed feature structure found
      String fsType = atts.getValue("type");
      if (null == fsType) {
        // use default type
        fsType = TOP_TYPE;
      }
      // create new type feature structure and put it on the TFS stack
      this.tfsStack.push(new TypedFeatStruct(fsType));
    }
    else if (qualName.equals(F_ID)) {
      // new feature-value-pair found;
      // just put the feature name on the stack feature stack;
      // we have to wait until we find the corresponding value for it
      this.featureStack.push(atts.getValue("name"));
    }
    // FSLIST XML elements can be ignored; results are directly put into
    // resultTfsList
  }


  /**
   * {@inheritDoc}
   */
  @Override
  public void endElement(String uri, String localName, String qualName) {

    if (qualName.equals(FS_ID)) {
      // end of a typed feature structure found; this is either the value
      // of a feature-value-pair or the top-level feature structure
      if (this.featureStack.empty()) {
        // top-level feature structure
        this.getResultTfsList().add(this.tfsStack.pop());
      }
      // else:
      // value of a feature-value-pair; it will be used up when the
      // corresponding feature-value-pair is closed, so we have to do nothing
      // here
    }
    else if (qualName.equals(F_ID)) {
      // end of a feature-value-pair found;
      // the feature is the top element of the feature stack;
      // the value is the top element of the feature structure stack
      FeatValPair fvp =
        new FeatValPair(this.featureStack.pop(), this.tfsStack.pop());
      // the feature-value-pair belongs to the (new) top element of the feature
      // structure stack
      this.tfsStack.peek().addFeatValPair(fvp);
    }
  }


  /**
   * {@inheritDoc}
   */
  @Override
  public void error(SAXParseException e) {

    e.printStackTrace();
  }
}
