package javacodebook.xml.processing.dom.parse;

import org.w3c.dom.*;

import org.xml.sax.*;

/**
 * Die ValidatingDOMParseUtil-Klasse parst ein XML-Dokument und kann
 * dabei sowohl gegen DTDs als auch gegen XML-Schemata validieren.
 * Um Fehler, die beim parsen von nicht validem XML aufgetreten sind
 * zu analysieren, bedient sie sich einem Objekt der Klasse
 * ErrorCollector, dass das SAX-ErrorHandler-Interface implementiert.
 *
 * @author Christoph Leinemann
 */

public class ValidatingDOMParseUtil {

  private static final String USAGE =
      "\nBenutzerhinweis: javacodebook.xml.processing.dom.parse.ValidatingDOMParseUtil " +
      "<uri>\n\nwobei\n\n<uri>\n" +
      "die URI ist, unter der ein XML-Dokument zu finden ist das geparst und validiert werden soll.\n";

  /**
   * Im wesentlichen passieren 3 Schritte in der main-Methode.  Zunächst wird ein
   * Dokument geparst und validiert.  Dann werden die Fehler die beim Parsen auf-
   * getreten sind analysiert und zum Schluss wird, falls keine fatalen Fehler
   * aufgetreten sind, das Dokument verarbeitet.
   *
   * @param args
   */
  public static void main(String[] args) {

    if (args.length != 1) {
      System.out.println(getUsage());
      System.exit(1);
    }

    String documentLocation = args[0];

    ValidatingDOMParseUtil validatingDOMParseUtil = new ValidatingDOMParseUtil();

    // Es wird ein Objekt unserer ErrorHandler-Implementierung
    // instanziiert.
    ErrorCollector errorCollector = new ErrorCollector();

    // das Dokument wird geparst
    Document document = validatingDOMParseUtil.parseDocument(documentLocation,
        errorCollector);

    // Eventuell aufgetretene Fehler werden verarbeitet
    boolean isValid = validatingDOMParseUtil.processErrors(errorCollector);

    // Falls das Dokument valide ist, wird es verarbeitet
    if(isValid)
    {
      validatingDOMParseUtil.processDocument(document);
    }
    else
    {
      System.out.println("Dokument wurde nicht verarbeitet, da es nicht valide ist");
    }

  }

  public Document parseDocument(String documentLocation,
                                ErrorCollector errorCollector) {

    // Ein DOMParser-Objekt wird instanziiert.
    org.apache.xerces.parsers.DOMParser parser =
        new org.apache.xerces.parsers.DOMParser();
    try {
      // über die die setFeature-Methode des Parser-Objektes kann
      // die Validierung eingeschaltet werden.
      parser.setFeature("http://xml.org/sax/features/validation", true);

      // Das ErrorHandler-Objekt wird beim Parser als
      // ErrorHandler registriert.
      parser.setErrorHandler(errorCollector);

      // Das Dokument, dass als Parameter übergeben
      // wurde, wird geparst.
      parser.parse(documentLocation);
    }
    catch (Exception e) {
      System.out.println("Dokument konnte nicht verarbeitet werden: " + e +
                         "\n" +
                         errorCollector.getFatalErrorMessagesAsText());
    }

    // das geparste Dokument kann von dem Parser geholt werden.
    return parser.getDocument();
  }

  /**
   * Die Methode verarbeitet Fehlermeldungen, die vom ErrorCollector-Objekt
   * gesammelt wurden. Sie liefert einen boolschen Wert zurück, der besagt,
   * ob das Dokument valide war oder nicht.
   *
   * @param errorCollector
   * @return boolean
   */
  public boolean processErrors(ErrorCollector errorCollector) {

    // Falls Probleme aufgetreten sind werden die entsprechenden
    // Meldungen in die Standardausgabe geschrieben.
    if (errorCollector.anyProblems()) {
      System.out.println(errorCollector.getWarningsMessagesAsText());
      System.out.println(errorCollector.getErrorMessagesAsText());
      System.out.println(errorCollector.getFatalErrorMessagesAsText());
      return false;
    }
    else {
      System.out.println("Das Dokument entspricht Schema bzw. DTD");
      return true;
    }
  }

  /**
   * Die Methode liefert die Pseudoverarbeitung eines Dokuments.
   * Es werden alle Namen der Elemente innerhalb der Root-Elements
   * in die Standardausgabe geschrieben.
   *
   * @param document
   */
  public void processDocument(Document document) {

    Element root = document.getDocumentElement();
    NodeList nl = root.getChildNodes();
    for (int i = 0; i < nl.getLength(); i++) {
      Node node = nl.item(i);
      if (node.getNodeType() == Node.ELEMENT_NODE) {
        System.out.println(node.getNodeName());
      }
    }

  }

  public static String getUsage() {
    return USAGE;
  }

}