package javacodebook.xml.processing.dom.manipulate;

import java.io.*;
import org.w3c.dom.*;

// das sind Xerces-spezifischen Klassen die benötig werden, da
// deren Funktionsumfang vom W3C noch nicht spezifiziert ist.
import org.apache.xml.serialize.OutputFormat;
import org.apache.xml.serialize.XMLSerializer;

/**
 * Die Klasse DOMParserUtil ist ausführbar und nimmt 2 Parameter entgegen.
 * <uri> und <zielVerzeichnis>
 * wobei <uri> den Ort des Dokumentes personal.xml beschreibt und <zielVerzeichnis>
     * ein Verzeichnis enthält, in das das manipulierte XML geschrieben werden soll.
 * Im Wesentlichen führt die Anwendung einige Dummy-Manipulation an dem XML-Dokument
 * durch.
 * Es sollen folgende Aktionen durchgeführt werden:
 * + Finde das erste Element namens 'person' mit einem Attribut namens id
 *   das den Wert 'three.worker' hat.
 * + Lese den Wert des Subelementes namens 'email' aus und schreibe ihn
 *   in die Standardausgabe
 * + Kopiere das person-Element 'three.worker' und setzte das Attribut 'id' auf
 *   clone.three.worker und füge es wieder in das Dokument ein.
 * + Schreibe das Dokument unter dem Namen 'manipulated.xml'
 *   in das Verzeichnis, das als 2. Argument übergeben wurde.
 *
 *
 * @author Christoph Leinemann
 */

public class DOMManipulator {

  private static final String USAGE =
      "\nBenutzerhinweis: javacodebook.xml.processing.dom.parse.DOMManipulator " +
      "<uri> <zielVerzeichnis>\n\nwobei\n\n<uri>\n" +
      "die URI unter der das XML-Dokument 'personal.xml' zu finden ist \n" +
      "(z.B. %XML_CHAPTER_HOME%\\processing\\dom\\manipulate\\personal.xml). Und\n\n<zielVerzeichnis>\n" +
      "das Verzeichnis ist, in das das manipulierte Dokument geschrieben werden \n" +
      "soll";

  /**
   *
   * In der main-Methode werden die beiden Parameter <uri> und <zielVerzeichnis>
   * ausgelesen, das entsprechende Dokument geparst und einige
   * Manipulation an dem Dokument durchgeführt, bevor es dann wieder
   * in das Zielverzeichnis geschrieben wird.
   *
   * @param args <uri> und <zielVerzeichnis>
   */
  public static void main(String[] args) {

    if (args.length != 2) {
      System.out.println(getUsage());
      System.exit(1);
    }

    Document doc = null;
    String uri = args[0];
    String target = args[1];

    DOMManipulator dOMManipulator = new DOMManipulator();

    // das Dokument wir von der URI geparst.
    try {
      doc = dOMManipulator.parse(uri);
    }
    catch (Exception e) {
      System.out.println("Probleme beim parsen der URI '" + uri + "' mit: " + e);
    }

    // Um das gewünschte Element zu finden müssen zunächst alle Element des Typs 'person'
    // gesucht werden.  Sie werden in einem Objekt vom Typ NodeList als eine
    // Art Liste referenziert.
    NodeList personNodeList = doc.getElementsByTagName("person");

    // Ein zusätzliches Element muss deklariert werden, über das das kopierte
    // Elemente später referenziert werden kann.
    Element personClone = null;

    // Für jedes der Elemente mit dem Namen person, über die über die personNodeList
    // iteriert werden kann, wird geprüft, ob ein Attribut namens id
    // mit dem Wert 'three.worker' existiert.
    for (int i = 0; i < personNodeList.getLength(); i++) {

      // Obwohl die item-Method nur ein Node-Objekt zurückliefert,
      // können wir sicher sein, dass es sich um ein Element handelt,
      // da die Methode getElementsByTagName nur Elemente zurückliefert.
      // Also kann hier auch bedenkenlos gecastet werden.
      Element personElement = (Element) personNodeList.item(i);

      // Der Wert des Attributs 'id' wird abgefragt.
      String id = personElement.getAttribute("id");
      if (id.equals("three.worker")) {

        // falls der Attributwert 'three.worker' entspricht,
        // wird das Element geklont.  Der boolsche parameter bestimmt,
        // ob das Element mit oder ohne Unterelementen geklont werden soll.
        // Wir wollen das Element mit samt allen Unterknoten klonen.
        personClone = (Element) personElement.cloneNode(true);

        // das Unterelement 'email' wird ausgelesen.  Hier muss man wieder
        // über eine NodeList gehen.
        NodeList emailNodeList = personElement.getElementsByTagName("email");
        Element emailElement = null;

        // wir wissen, dass es nur ein email-Element geben kann. Also holen wir uns das
        // erste Element der NodeList.
        if (emailNodeList.getLength() > 0) {
          emailElement = (Element) emailNodeList.item(0);
        }

        // Da man nicht direkt auf den Text innerhalb eines Elements zugreifen
        // kann, müssen zunächst die Text-Knoten unterhalb des email-Elements
        // geholt werden.  Man kann sich nicht grundsätzlich
        // darauf verlassen, dass Text innerhalb eines Elements
        // immer in einem einzigen Text-Knoten abgelegt ist.  Es kann durchaus
        // vorkommen, dass der Text über mehrere Text-Knoten verteilt ist.  Deswegen an
        // dieser Stelle eine Iteration über alle Text-Knoten unterhalb des
        // email Elements.
        NodeList subNodesFromEmail = emailElement.getChildNodes();
        for (int j = 0; j < subNodesFromEmail.getLength(); j++) {
          Node node = subNodesFromEmail.item(j);

          // Da sich noch Knoten anderer Art innerhalb des
          // email-Elements befinden könnten, prüfen wir,
          // ob es sich um einen Text-Knoten handelt.  Falls ja
          // wird der Inhalt in die Standardausgabe geschrieben.
          if (node.getNodeType() == Node.TEXT_NODE) {
            System.out.println(node.getNodeValue());
          }
        }
      }
    }

    // wir erstellen noch einen Kommentar
    Comment comment = doc.createComment(
        "Es folgt das geklonte und leicht manipuliert wieder eingefügte Element");

    // zunächst wird das Attribut an dem Klon verändert.
    // An dieser Stelle könnten beliebige andere strukturelle und inhaltliche
    // Änderungen durchgeführt werden.
    personClone.setAttribute("id", "clone.three.worker");

    // dann werden Kommentar und Klon an das Root-Element des Dokuments gehängt.
    doc.getDocumentElement().appendChild(comment);
    doc.getDocumentElement().appendChild(personClone);

    // Für die Serialisierung wird ein OutputFormat-Objekt benötigt.
    OutputFormat format = new OutputFormat(doc, "ISO-8859-1", true);

    // Es wird ein XMLSerializer auf Basis des FileWriter-Objektes
    // und des OutputFormat-Objekts instanziiert.
    try {
      XMLSerializer serial = new XMLSerializer(new FileWriter(target +
          "/manipulated.xml"), format);
      serial.serialize(doc);
    }
    catch (Exception e) {
      System.out.println("Fehler beim Schreiben des Dokumentes: " + e);
    }
  }

  /**
   *
   * Diese Methode parst auf apache-xerces-proprietäre Weise eine URI, und liefert eine
   * W3C-Document Implementierung zurück.
   *
   * @param uri
   * @return org.w3c.Document
   * @throws Exception
   */
  public Document parse(String uri) throws Exception {
    org.apache.xerces.parsers.DOMParser parser = new org.apache.xerces.parsers.
        DOMParser();
    parser.parse(uri);
    return parser.getDocument();
  }

  public static String getUsage() {
    return USAGE;
  }
}