home *** CD-ROM | disk | FTP | other *** search
/ PC Plus SuperCD (UK) 1999 October / pcp156b.iso / alphawrk / TEXML / TEXML.ZIP / com / ibm / texml / TeXML.java < prev    next >
Encoding:
Java Source  |  1999-05-18  |  13.4 KB  |  492 lines

  1. /*
  2.  * (C) Copyright IBM Corp. 1998  All rights reserved.
  3.  *
  4.  * US Government Users Restricted Rights Use, duplication or
  5.  * disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
  6.  *
  7.  * The program is provided "as is" without any warranty express or
  8.  * implied, including the warranty of non-infringement and the implied
  9.  * warranties of merchantibility and fitness for a particular purpose.
  10.  * IBM will not be liable for any damages suffered by you as a result
  11.  * of using the Program. In no event will IBM be liable for any
  12.  * special, indirect or consequential damages or lost profits even if
  13.  * IBM has been advised of the possibility of their occurrence. IBM
  14.  * will not be liable for any third party claims against you.
  15.  */
  16.  
  17. package com.ibm.texml;
  18.  
  19. import java.io.*;
  20. import java.util.Hashtable;
  21. import org.w3c.dom.*;
  22.  
  23. /**
  24. This class provides methods to convert a DOM
  25. conforming to TeXML.dtd into actual TeX source.
  26. */
  27. public class TeXML
  28.   {
  29.   public static final String ATTR_Begin = "begin";
  30.   public static final String ATTR_Catcode = "cat";
  31.   public static final String ATTR_Char = "ch";
  32.   public static final String ATTR_Close = "close";
  33.   public static final String ATTR_End = "end";
  34.   public static final String ATTR_Linebreaks = "linebreaks";
  35.   public static final String ATTR_Name = "name";
  36.   public static final String ATTR_Open = "open";
  37.   public static final String EL_Command = "cmd";
  38.   public static final String EL_Control = "ctrl";
  39.   public static final String EL_Environment = "env";
  40.   public static final String EL_Group = "group";
  41.   public static final String EL_Opt = "opt";
  42.   public static final String EL_Parm = "parm";
  43.   public static final String EL_Special = "spec";
  44.   public static final String EL_TeXML = "texml";
  45.   public static final String EL_Verb = "verb";
  46.   public static final String ENTITY_AMP = "amp";
  47.   public static final String ENTITY_LT = "lt";
  48.   public static final String ENTITY_GT = "gt";
  49.   public static final String V_ActiveSpace = "tilde";
  50.   public static final String V_Alignment = "align";
  51.   public static final String V_BGroup = "bg";
  52.   public static final String V_Comment = "comment";
  53.   public static final String V_EGroup = "eg";
  54.   public static final String V_Escape = "esc";
  55.   public static final String V_MathShift = "mshift";
  56.   public static final String V_None = "none";
  57.   public static final String V_Parameter = "parm";
  58.   public static final String V_Subscript = "sub";
  59.   public static final String V_Superscript = "sup";
  60.  
  61.   Hashtable handlers;
  62.   Hashtable escapes;
  63.   Hashtable specials;
  64.  
  65.   boolean inVerbatim = false;
  66.   boolean verbatimBreaks = true;
  67.  
  68.   /**
  69.   Create the translator.
  70.   */
  71.   public TeXML()
  72.     {
  73.     handlers = new Hashtable();
  74.     handlers.put(EL_Command, ehCommand);
  75.     handlers.put(EL_Control, ehControl);
  76.     handlers.put(EL_Environment, ehEnvironment);
  77.     handlers.put(EL_Group, ehParm);
  78.     handlers.put(EL_Opt, ehOpt);
  79.     handlers.put(EL_Parm, ehParm);
  80.     handlers.put(EL_Special, ehSpecial);
  81.     handlers.put(EL_Verb, ehVerb);
  82.  
  83.     escapes = new Hashtable();
  84.     escapes.put(new Character('%'), new String("\\%{}"));
  85.     escapes.put(new Character('&'), new String("\\&{}"));
  86.     escapes.put(new Character('{'), new String("\\{"));
  87.     escapes.put(new Character('}'), new String("\\}"));
  88.     escapes.put(new Character('\\'), new String("$\\backslash$"));
  89.     escapes.put(new Character('$'), new String("\\${}"));
  90.     escapes.put(new Character('#'), new String("\\#{}"));
  91.     escapes.put(new Character('_'), new String("\\_{}"));
  92.     escapes.put(new Character('^'), new String("\\char`\\^{}"));
  93.     escapes.put(new Character('~'), new String("\\char`\\~{}"));
  94.     escapes.put(new Character('<'), new String("$<$"));
  95.     escapes.put(new Character('>'), new String("$>$"));
  96.     escapes.put(new Character('|'), new String("$|$"));
  97.  
  98.     specials = new Hashtable();
  99.     specials.put(V_ActiveSpace, "~");
  100.     specials.put(V_Alignment, "&");
  101.     specials.put(V_BGroup, "{");
  102.     specials.put(V_Comment, "%");
  103.     specials.put(V_EGroup, "}");
  104.     specials.put(V_Escape, "\\");
  105.     specials.put(V_MathShift, "$");
  106.     specials.put(V_Parameter, "#");
  107.     specials.put(V_Subscript, "_");
  108.     specials.put(V_Superscript, "^");
  109.     }
  110.  
  111.   /**
  112.   Need to track line separators to make sure we output
  113.   only one in succession.
  114.   */
  115.   private class OutputAutomata
  116.     {
  117.     boolean sawLineBreak;
  118.     PrintWriter pout;
  119.  
  120.     OutputAutomata(OutputStream ostream)
  121.       {
  122.       sawLineBreak = false;
  123.       pout = new PrintWriter(ostream);
  124.       }
  125.  
  126.     protected void finalize()
  127.       {
  128.       pout.flush();
  129.       }
  130.  
  131.     void output(char ch)
  132.       {
  133.       if (ch == '\n')
  134.         {
  135.         if (!sawLineBreak)
  136.           {
  137.           pout.print(ch);
  138.           sawLineBreak = true;
  139.           }
  140.         }
  141.       else
  142.         {
  143.         pout.print(ch);
  144.         sawLineBreak = false;
  145.         }
  146.       }
  147.  
  148.     /**
  149.     Caller certifes that string contains no line breaks.
  150.     */
  151.     void outputClean(String s)
  152.       {
  153.       pout.print(s);
  154.       sawLineBreak = false;
  155.       }
  156.  
  157.     /**
  158.     Output string one character at a time, filtering for line breaks.
  159.     */
  160.     void output(String s)
  161.       {
  162.       for (int i = 0; i < s.length(); ++i)
  163.         {
  164.         output(s.charAt(i));
  165.         }
  166.       }
  167.  
  168.     void flush()
  169.       {
  170.       pout.flush();
  171.       }
  172.     }
  173.  
  174.   private interface ElementHandler
  175.     {
  176.     public void processElement(Element n, OutputAutomata pout);
  177.     }
  178.  
  179.   private void delimitedElement(Element n, OutputAutomata pout,
  180.                                 String open, String close)
  181.     {
  182.     String b = n.getAttribute(ATTR_Open);
  183.     if (b.length() == 0)
  184.       {
  185.       b = open;
  186.       }
  187.     String e = n.getAttribute(ATTR_Close);
  188.     if (e.length() == 0)
  189.       {
  190.       e = close;
  191.       }
  192.     pout.output(b);
  193.     processChildren(n, pout);
  194.     pout.output(e);
  195.     };
  196.  
  197.   private ElementHandler ehOpt = new ElementHandler()
  198.     {
  199.     public void processElement(Element n, OutputAutomata pout)
  200.       {
  201.       delimitedElement(n, pout, "[", "]");
  202.       }
  203.     };
  204.  
  205.   private ElementHandler ehParm = new ElementHandler()
  206.     {
  207.     public void processElement(Element n, OutputAutomata pout)
  208.       {
  209.       delimitedElement(n, pout, "{", "}");
  210.       }
  211.     };
  212.  
  213.   /**
  214.   Return true if there are option children.
  215.   */
  216.   private boolean hasOptions(NodeList nl)
  217.     {
  218.     int i = 0;
  219.     boolean hasOpt = false;
  220.     while(i < nl.getLength() && !hasOpt)
  221.       {
  222.       Node child = nl.item(i);
  223.       hasOpt = child.getNodeType() == Node.ELEMENT_NODE &&
  224.                 ((Element)child).getTagName().equalsIgnoreCase(EL_Opt);
  225.       ++i;
  226.       }
  227.     return hasOpt;
  228.     }
  229.  
  230.   /**
  231.   Return true if there are parameter children.
  232.   */
  233.   private boolean hasParameters(NodeList nl)
  234.     {
  235.     int i = 0;
  236.     boolean hasParm = false;
  237.     while(i < nl.getLength() && !hasParm)
  238.       {
  239.       Node child = nl.item(i);
  240.       hasParm = child.getNodeType() == Node.ELEMENT_NODE &&
  241.                 ((Element)child).getTagName().equalsIgnoreCase(EL_Parm);
  242.       ++i;
  243.       }
  244.     return hasParm;
  245.     }
  246.  
  247.   /**
  248.   Process command elements.
  249.   */
  250.   private ElementHandler ehCommand = new ElementHandler()
  251.     {
  252.     public void processElement(Element n, OutputAutomata pout)
  253.       {
  254.       String s = n.getAttribute(ATTR_Name);
  255.       pout.outputClean("\\"+s);
  256.       NodeList nl = n.getChildNodes();
  257.       if (hasParameters(nl) || hasOptions(nl))
  258.         {
  259.         processChildren(n, pout);
  260.         }
  261.       else
  262.         {
  263.         //ensure the command is terminated
  264.         pout.output(' ');
  265.         }
  266.       }
  267.     };
  268.  
  269.   /**
  270.   Process control elements.
  271.   */
  272.   private ElementHandler ehControl = new ElementHandler()
  273.     {
  274.     public void processElement(Element n, OutputAutomata pout)
  275.       {
  276.       String s = n.getAttribute(ATTR_Char);
  277.       pout.outputClean("\\"+s);
  278.       }
  279.     };
  280.  
  281.   /**
  282.   Process environment elements.
  283.   */
  284.   private ElementHandler ehEnvironment = new ElementHandler()
  285.     {
  286.     public void processElement(Element n, OutputAutomata pout)
  287.       {
  288.       String name = n.getAttribute(ATTR_Name);
  289.       String begin = n.getAttribute(ATTR_Begin);
  290.       if (begin.length() == 0)
  291.         {
  292.         begin = "begin";
  293.         }
  294.       String end = n.getAttribute(ATTR_End);
  295.       if (end.length() == 0)
  296.         {
  297.         end = "end";
  298.         }
  299.       pout.outputClean("\\"+begin+"{"+name+"}");
  300.       processChildren(n, pout);
  301.       pout.outputClean("\\"+end+"{"+name+"}");
  302.       }
  303.     };
  304.  
  305.   /**
  306.   No-op for option and parameter elements found out of the context of a command.
  307.   */
  308.   private ElementHandler ehNop = new ElementHandler()
  309.     {
  310.     public void processElement(Element n, OutputAutomata pout)
  311.       {
  312.       System.err.println("Element "+n.getTagName()+" occurred out of context.");
  313.       }
  314.     };
  315.  
  316.   /**
  317.   Process special elements.
  318.   */
  319.   private ElementHandler ehSpecial = new ElementHandler()
  320.     {
  321.     public void processElement(Element n, OutputAutomata pout)
  322.       {
  323.       String s = n.getAttribute(ATTR_Catcode);
  324.       String spec = (String)specials.get(s);
  325.       if (spec == null)
  326.         {
  327.         System.err.println("Unknown special attribute, "+s);
  328.         }
  329.       else
  330.         {
  331.         pout.outputClean(spec);
  332.         }
  333.       }
  334.     };
  335.  
  336.   /**
  337.   Process the verbatim element
  338.   */
  339.   private ElementHandler ehVerb = new ElementHandler()
  340.     {
  341.     public void processElement(Element n, OutputAutomata pout)
  342.       {
  343.       inVerbatim = true;
  344.       if (n.getAttribute(ATTR_Linebreaks).equals(V_None))
  345.         {
  346.         verbatimBreaks = false;
  347.         }
  348.       processChildren(n, pout);
  349.       inVerbatim = false;
  350.       verbatimBreaks = true;
  351.       }
  352.     };
  353.  
  354.   private void processTeXMLElement(Element el, OutputAutomata pout)
  355.     {
  356.     ElementHandler eh = (ElementHandler)handlers.get(el.getTagName());
  357.     if (eh != null)
  358.       {
  359.       eh.processElement(el, pout);
  360.       }
  361.     else
  362.       {
  363.       System.out.println("Unrecognized DOM element element name, "+
  364.                          el.getTagName());
  365.       }
  366.     }
  367.  
  368.   private void outputCharacter(char ch, OutputAutomata pout)
  369.     {
  370.     Character tch = new Character(ch);
  371.     String escape = (String)escapes.get(tch);
  372.     if (escape != null)
  373.       {
  374.       // write escaped character
  375.       pout.outputClean(escape);
  376.       }
  377.     else
  378.       {
  379.       // write normal character
  380.       pout.output(ch);
  381.       }
  382.     }
  383.  
  384.   /**
  385.   Copy text to pout.
  386.   Escape specials.
  387.   Eliminate multiple newlines.
  388.   */
  389.   private void processTeXMLText(CharacterData t, OutputAutomata pout)
  390.     {
  391.     String text = t.getData();
  392.     if (inVerbatim)
  393.       {
  394.       if (!verbatimBreaks)
  395.         {
  396.         text = text.replace('\n',' ');
  397.         }
  398.       pout.outputClean(text);
  399.       }
  400.     else
  401.       {
  402.       for(int ofs = 0; ofs < text.length(); ++ofs)
  403.         {
  404.         outputCharacter(text.charAt(ofs), pout);
  405.         }
  406.       }
  407.     }
  408.  
  409.   private void processChildren(Node n, OutputAutomata pout)
  410.     {
  411.     NodeList nl = n.getChildNodes();
  412.     for (int i = 0; i < nl.getLength(); ++i)
  413.       {
  414.       Node child = nl.item(i);
  415.       int type = child.getNodeType();
  416.       if (type == Node.ELEMENT_NODE)
  417.         {
  418.         processTeXMLElement((Element)child, pout);
  419.         }
  420.       else if (type == Node.TEXT_NODE || type == Node.CDATA_SECTION_NODE)
  421.         {
  422.         processTeXMLText((Text)child, pout);
  423.         }
  424.       else if (type == Node.ENTITY_REFERENCE_NODE)
  425.         {
  426.         String name = child.getNodeName();
  427.         if (name.equalsIgnoreCase(ENTITY_AMP))
  428.           {
  429.           outputCharacter('&', pout);
  430.           }
  431.         else if (name.equalsIgnoreCase(ENTITY_LT))
  432.           {
  433.           outputCharacter('<', pout);
  434.           }
  435.         else if (name.equalsIgnoreCase(ENTITY_GT))
  436.           {
  437.           outputCharacter('>', pout);
  438.           }
  439.         }
  440.       else
  441.         {
  442.         System.out.print("Failed to handle node type, "+type);
  443.         System.out.print(", name "+child.getNodeName());
  444.         System.out.println(", value "+child.getNodeValue());
  445.         }
  446.       }
  447.     }
  448.  
  449.   /**
  450.   Translate "TeXML" node and children to TeX on the output stream.
  451.   This method may be called multiple times, with multiple DOM's,
  452.   on a single instance.
  453.  
  454.   @param texML the root "TeXML" node of the TeXML DOM.
  455.   @param ostream the destination of the TeX output.
  456.   */
  457.   public void processTeXML(Element texML, OutputStream ostream)
  458.     {
  459.     OutputAutomata pout = new OutputAutomata(ostream);
  460.     if (texML.getTagName().equalsIgnoreCase(EL_TeXML))
  461.       {
  462.       processChildren(texML, pout);
  463.       }
  464.     else
  465.       {
  466.       System.out.println("Unrecognized DOM root element name, "+
  467.                          texML.getTagName());
  468.       System.out.println(EL_TeXML+" expected.");
  469.       }
  470.     pout.flush();
  471.     }
  472.  
  473.   /**
  474.   Translate TeXML document to tex.
  475.   This method may be called multiple times, with multiple DOM's,
  476.   on a single instance.
  477.  
  478.   @param texML the document node of the TeXML DOM.
  479.   @param tex the destination file handle.
  480.   */
  481.   public void processTeXML(Document texML, OutputStream ostream)
  482.     {
  483.     DocumentType dtd = texML.getDoctype();
  484.     Element texRoot = texML.getDocumentElement();
  485.     if (dtd != null)
  486.       {
  487.       System.out.println("DTD is "+dtd.getName());
  488.       }
  489.     processTeXML(texRoot, ostream);
  490.     }
  491.   }
  492.