
import java.io.*;
import org.xml.sax.SAXParseException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.*;

import java.util.*; // der Stack

public class parsen3 {
static Document document;
static int      level;
static String   str   = "";
static Stack    stack = new Stack();

 static public void main( String[] args) {
 File file = new File( "math1.xml");
 boolean load = file.canRead();

  if   (load)
       {
        try   {
              DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
              DocumentBuilder        builder = factory.newDocumentBuilder();

               document = builder.parse( file);
              }
        catch (SAXParseException err)
              {
               System.out.println( "\nParsing-Fehler in");
               System.out.println( "Datei: "+err.getSystemId());
               System.out.println( "Zeile: "+err.getLineNumber());
               System.out.println( err.getMessage());
               System.exit( 1);
              }
        catch (IOException ioe)
              {
               ioe.printStackTrace();
               System.exit( 1);
              }
        catch (Throwable ta)
              {
               ta.printStackTrace();
               System.exit( 1);
              }
       }
  else {
        System.out.println( "Datei nicht gefunden");
        System.exit( 1);
       }

  if (document.getElementsByTagName( "math") == null)
     {
      System.out.println( "Keine MathML-Sequenz gefunden");
      System.exit( 1);
     }

  level = 0;
  preOrder( document.getElementsByTagName( "math").item( 0));
  System.out.println( "Vom Stack:\n");
  while (!stack.isEmpty())
        {
         System.out.println( (String)stack.pop());
        }
 }

 /**
 Wert eines Kindknotens übergeben. Ist kein Kindknoten oder kein
 Wert vorhanden, wird null übergeben.
 @param node Knoten dessen Kindknotenwert zu ermitteln ist.
 @return Kindknotenwert als String.
 */
 static public String getChildValue( Node node) {
 Node child = node.getFirstChild();
  if (child == null)                return null;
  if (child.getNodeValue() == null) return null;
  return child.getNodeValue().trim();
 }

 //**********************************************/
 /** Wert des Sub-Nodes als Identifier annehmen */
 //**********************************************/

 static public void mi( Node node) {
 String identifier = getChildValue( node);

  stack.push( identifier);
 }

 //******************************************/
 /** Wert des Sub-Nodes als Number annehmen */
 //******************************************/

 static public void mn( Node node) {
 String number = getChildValue( node);

  stack.push( number);
 }

 //********************************************/
 /** Wert des Sub-Nodes als Operator annehmen */
 //********************************************/

 static public void mo( Node node) {
 String operator = getChildValue( node);

  stack.push( operator);
 }

 //******************************/
 /** Aktuellen Knoten behandeln */
 //******************************/

 static public void handle( Node node) {
 String name = node.getNodeName();

  if (name.equals( "#text")) return;
  if (name.equals( "mi")) { mi( node); return; }
  if (name.equals( "mn")) { mn( node); return; }
  if (name.equals( "mo")) { mo( node); return; }

  if (str.length() > 0)
     {
      stack.push( str);
      str = "";
     }
  stack.push( name);
 }

 //**********************************************/
 /** Nachbearbeitung des/der letzten KindKnoten */
 //**********************************************/

 static public void finish( Node node) {
 String name = node.getNodeName();

  if (name.equals( "mi") ||
      name.equals( "mn") ||
      name.equals( "mo") ||
      name.equals( "#text")) return;

  /*-------------------------------------------------------*/
  /* Gruppierung zu einem einzelnen Element zusammenfassen */
  /*-------------------------------------------------------*/

  if (name.equals( "mrow"))
     {
      while (!((String)stack.peek()).equals( name))
             str = ((String)stack.pop()).concat( str);
      stack.pop();      // mrow vom Stack entfernen
      stack.push( str); // aufgebauten String ablegen
      str = "";         // String leeren
      return;
     }

  /*------------------------------------------------------------------*/
  /* Aufbau einer Bruchsequenz aus den beiden obersten Stackelementen */
  /*------------------------------------------------------------------*/

  if (name.equals( "mfrac"))
     {
     String nom = (String)stack.pop(); // Zähler
     String den = (String)stack.pop(); // Nenner

      /*------------------------------------------*/
      /* Test, ob mfrac-Tag korrekt abgeschlossen */
      /*------------------------------------------*/

      str = (String)stack.pop();
      if (!str.equals( "mfrac"))
         {
          System.out.println( "Fehlerhafter Aufbau eines Bruchterms");
          System.exit( 1);
         }
      str = "";

      /*---------------------------------*/
      /* Eventuelle Summenterme klammern */
      /*---------------------------------*/

      if (sumTerm( nom)) nom = "(".concat( nom).concat( ")");
      if (sumTerm( den)) den = "(".concat( den).concat( ")");

      stack.push( den+"/"+nom);
      return;
     }

  /*------------------------------------*/
  /* Aufbau eines indizierten Ausdrucks */
  /*------------------------------------*/

  if (name.equals( "msub"))
     {
     String index = (String)stack.pop();
     String base  = (String)stack.pop();

      /*------------------------------------------*/
      /* Test, ob mfrac-Tag korrekt abgeschlossen */
      /*------------------------------------------*/

      str = (String)stack.pop();
      if (!str.equals( "msub"))
         {
          System.out.println( "Fehlerhafter Aufbau eines indizierten Terms");
          System.exit( 1);
         }
      str = "";

      stack.push( base+"["+index+"]");
      return;
     }

  stack.push( "/"+name);
 }

 /**
 Aufbereitung des Node-Trees als postfix-ordered. Die Ausführung
 erfolgt rekursiv.
 @param node Der aktuelle Node im Document-Tree
 */
 static public void postOrder( Node node) {
  if (node == null) return;

  finish( node);
  level++;
  postOrder( node.getFirstChild()); // rekuriv Stufe hinunter steigen
  level--;
  handle( node);                             // Inhalt des Nodes ermitteln
  postOrder( node.getNextSibling());         // rekuriv Stufe herauf steigen
 }

 /**
 Aufbereitung des Node-Trees als prefix-ordered. Die Ausführung
 erfolgt rekursiv.
 @param node Der aktuelle Node im Document-Tree
 */
 static public void preOrder( Node node) {
  if (node == null) return;

  handle( node);                    // Inhalt des Nodes bearbeiten
  level++;
  preOrder( node.getFirstChild());  // rekuriv Stufe hinunter steigen
  level--;
  finish( node);                    // Nachberarbeitung
  preOrder( node.getNextSibling()); // rekuriv folgenden Node
 }

 /**
 Feststellung, ob ein Summenterm vorliegt. Ein Klammerterm ist,
 unabhängig von seinem Inhalt, kein Summenterm.
 @param term Der zu untersuchende Term.
 @return true, wenn ein Summenterm vorliegt, sonst false.
 */
 static public boolean sumTerm( String term) {
 int para = 0;

  for (int i=0; i<term.length(); i++)
      {
       if (term.charAt(i) == '(') para++;
       if (term.charAt(i) == ')') para--;
       if (para == 0)
          {
           if (term.charAt( i) == '-' || term.charAt( i) == '+')
              {
               if (i > 0)
                  {
                   if (term.charAt( i-1) != '*' && term.charAt( i-1) != '/')
                      {
                       return true;
                      }
                  }
              }
          }
      }

  return false;
 }
}

