HTMLSourceTag.java

/**
 *  Copyright (C) 2006 - OQube / Arnaud Bailly
 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
 License as published by the Free Software Foundation; either
 version 2.1 of the License, or (at your option) any later version.
 This library is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 Lesser General Public License for more details.
 You should have received a copy of the GNU Lesser General Public
 License along with this library; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 Created 19 sept. 2006
 */
package oqube.muse.html;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.uwyn.jhighlight.renderer.JavaXhtmlRenderer;
import com.uwyn.jhighlight.renderer.Renderer;
import com.uwyn.jhighlight.renderer.XhtmlRenderer;
import com.uwyn.jhighlight.renderer.XhtmlRendererFactory;
import fr.lifl.utils.SubstitutableString;
import oqube.muse.AbstractTagHandler;
import oqube.muse.MuseSink;
import oqube.muse.MuseTagHandler;
/**
 * A class that generates an XHTML fragment from <source> tags encountered in a
 * muse literate file. HTML markup is done using <em>Jhighlight</em> package
 * from Geert Bevin
 * 
 * @author nono
 * @see <a href="https://jhighlight.dev.java.net/">Jhighlight home page</a>
 */
public class HTMLSourceTag extends AbstractTagHandler {
  {
    try {
      Class.forName("oqube.jhighlight.haskell.HaskellXhtmlRenderer");
      Class.forName("oqube.jhighlight.python.PythonXhtmlRenderer");
    } catch (Exception e) {
      e.printStackTrace();
      System.err.println("Error instantiating renderer :"+e);
    }
  }
  /*
   * map from fragment names to references count.
   */
  private Map ids = new HashMap();
  /*
   * (non-Javadoc)
   * 
   * @see oqube.muse.MuseTagHandler#block(oqube.muse.MuseSink, java.lang.String,
   *      java.lang.String[][], java.lang.String)
   */
  public boolean block(MuseSink sink, String tag, String[][] at, String content) {
    // Check tag and parameters
    if (!"source".equals(tag) && !"src".equals(tag))
      if (getNext() != null)
        return getNext().block(sink, tag, at, content);
      else
        return false;
    String fname = null;
    String lang = null;
    for (int i = 0; i < at.length; i++) {
      if ("name".equals(at[i][0]))
        fname = at[i][1];
      else if ("language".equals(at[i][0]) || "lang".equals(at[i][0]))
        lang = at[i][1];
      else if ("hidden".equals(at[i][0]) && "true".equalsIgnoreCase(at[i][1]))
        return true;
    }
    // highlight
    String hlt;
    try {
      hlt = highlight(content, lang);
    } catch (IOException e) {
      // TODO: log error
      hlt = "error";
    }
    // id map - fragment id defaults to 'id'
    String baseid = fname == null ? "id" : fname;
    String id;
    String previd = (String) ids.get(baseid);
    if (previd != null) {
      if (previd.equals(baseid))
        id = baseid + '1';
      else
        id = baseid + (Integer.parseInt(previd.substring(baseid.length())) + 1);
    } else {
      id = baseid;
    }
    ids.put(baseid, id);
    StringBuffer sb = new StringBuffer();
    Pattern pat = Pattern
        .compile("<span class=\"java_operator\">&lt;&lt;</span><span class=\"java_plain\">([^<]+)</span><span class=\"java_operator\">&gt;&gt;</span>");
    Matcher m = pat.matcher(hlt);
    while (m.find()) {
      m.appendReplacement(sb, "<a href=\"#$1\">&lt;&lt;$1&gt;&gt;</a>");
    }
    m.appendTail(sb);
    sink.rawText("<pre class=\"code\" id=\"" + id + "\">");
    sink.anchor(id);
    if (previd != null)
      sink.link("#" + previd, "previous");
    sink.rawText(sb.toString());
    sink.rawText("</pre>");
    return true;
  }
  /*
   * highlight given content as HTML in given language and returns a
   * StringBuffer containing the result.
   */
  private String highlight(String content, String lang) throws IOException {
    if (lang == null)
      lang = XhtmlRendererFactory.JAVA;
    Renderer rend = XhtmlRendererFactory.getRenderer(lang);
    if (rend == null)
      // TODO: log ignored fragment
      return content;
    return rend.highlight(null, content, "UTF-8", true);
  }
  /*
   * (non-Javadoc)
   * 
   * @see oqube.muse.MuseTagHandler#flow(oqube.muse.MuseSink, java.lang.String,
   *      java.lang.String[][], java.lang.String)
   */
  public boolean flow(MuseSink sink, String tag, String[][] at, String content) {
    return block(sink, tag, at, content);
  }
}