Index: core/xwiki-core/src/main/java/com/xpn/xwiki/plugin/graphviz/GraphVizMacro.java =================================================================== --- core/xwiki-core/src/main/java/com/xpn/xwiki/plugin/graphviz/GraphVizMacro.java (revision 12421) +++ core/xwiki-core/src/main/java/com/xpn/xwiki/plugin/graphviz/GraphVizMacro.java (working copy) @@ -23,6 +23,9 @@ import java.io.IOException; import java.io.Writer; +import java.util.Arrays; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.radeox.api.engine.RenderEngine; import org.radeox.api.engine.context.RenderContext; @@ -33,6 +36,44 @@ import com.xpn.xwiki.XWikiContext; import com.xpn.xwiki.render.XWikiRadeoxRenderEngine; +/** + *

+ * Macro used to invoke GraphViz and render its output on a XWiki page. + *

+ *

+ * For it to work properly, you must have the GraphViz plugin enabled and configured. + *

+ *

+ * The content of the macro is the source code for the graph, using GraphViz DOT language. + *

+ * The parameters are as follows: + *

    + *
  1. type: [dot] or neato. Specifies which engine will be used to produce the graph.
  2. + *
  3. title: Title attribute for the image (floating text box when you hover it). Do not specify it if you + * want to use GraphViz tooltip attribute. With the tooltip attributes you can generate different text values for + * different regions of the image.
  4. + *
  5. height: Image height in pixels.
  6. + *
  7. width: Image width in pixels.
  8. + *
  9. alt: Alternative text (alt attribute) for the image, in case it cannot be rendered by the browser.
  10. + *
  11. format: This attribute specifies what will be GraphViz output and also how it will be rendered in the + * HTML.
    The HTML output of the macro will be one of the following, depending on this attribute: + * + *
  12. + *
  13. clickable: If present and not false indicates that the IMG tag must reference the link map produced by + * GraphViz ("cmapx" format). Anonymous graphs will provoke errors if this attribute is true, so don't forget to name + * them. This parameter will have no effect if format specifies anything different from an IMG tag to be + * rendered - what doesn't mean you can't have clickable SVG images, you just don't need an extra map tag for that.
  14. + *
+ *

+ */ public class GraphVizMacro extends BaseLocaleMacro { public String getLocaleKey() @@ -58,24 +99,115 @@ } boolean dot = !("neato").equals(params.get("type")); - StringBuffer str = new StringBuffer(); - String img = params.get("text", 0); - String height = params.get("height", 1); - String width = params.get("width", 2); + String title = nullifyBadParameter(params.get("title", 0)); + String height = nullifyBadParameter(params.get("height", 1)); + String width = nullifyBadParameter(params.get("width", 2)); + String alt = nullifyBadParameter(params.get("alt", 3)); + String format = nullifyBadParameter(params.get("format", 4)); + if (format == null) + format = "png"; + String clickableStr = nullifyBadParameter(params.get("clickable", 5)); + boolean clickable = clickableStr != null && !"false".equals(clickableStr); + String dottext = params.getContent(); - String dottext = params.getContent(); - str.append("= 0) { + /* producing plain text output */ + byte[] graphvizOutput = plugin.getDotImage(dottext, format, dot); + writer.write(new String(graphvizOutput)); + } else if (Arrays.binarySearch(embedTagFormats, format) >= 0) { + /* producing object/embed tag output */ + final String resultURL = plugin.getDotResultURL(dottext, dot, format, xcontext); + writer.write(""); + if (alt != null) /* object tag content text */ + writer.write(alt); + writer.write(""); + } else { + /* producing img tag output */ + if (clickable) { + /* creates the map */ + byte[] graphvizOutput = plugin.getDotImage(dottext, "cmapx", dot); + writer.write(new String(graphvizOutput)); + } + final String resultURL = plugin.getDotResultURL(dottext, dot, format, xcontext); + writer.write(""); + throw new IllegalArgumentException("Macro content is not a DOT graph definition or is an " + + "anonymous graph (not supported). Content: " + dottext); + } + final String graphName = matcher.group(2); + writer.write(" usemap=\"#"); + writer.write(graphName); + writer.write('"'); + } + writer.write("/>"); + } + } + + private void writeImgOrEmbedCommonContent(Writer writer, String resultURL, String title, String height, + String width, String alt) throws IOException + { + writer.write("src=\""); + writer.write(resultURL); + writer.write("\" "); + writeDimensionsAntTitle(writer, title, height, width); + if (alt != null) { + writer.write("alt=\""); + writer.write(alt); + writer.write('"'); + } + } + + private void writeDimensionsAntTitle(Writer writer, String title, String height, String width) throws IOException + { if ((!"none".equals(height)) && (height != null)) { - str.append("height=\"" + height + "\" "); + writer.write("height=\""); + writer.write(height); + writer.write("\" "); } if ((!"none".equals(width)) && (width != null)) { - str.append("width=\"" + width + "\" "); + writer.write("width=\""); + writer.write(width); + writer.write("\" "); } - str.append("alt=\""); - str.append(img); - str.append("\" />"); - writer.write(str.toString()); + if (title != null) { + writer.write("title=\""); + writer.write(title); + writer.write("\" "); + } } + + /** + * Checks if a parameter came with a '=' sign and return null in the case. Else, returns the parameter itself. + * + * @param radeoxParam + * @return String + */ + private String nullifyBadParameter(String radeoxParam) + { + return radeoxParam != null && radeoxParam.indexOf('=') >= 0 ? null : radeoxParam; + } } Index: core/xwiki-core/src/main/java/com/xpn/xwiki/plugin/graphviz/GraphVizPlugin.java =================================================================== --- core/xwiki-core/src/main/java/com/xpn/xwiki/plugin/graphviz/GraphVizPlugin.java (revision 12421) +++ core/xwiki-core/src/main/java/com/xpn/xwiki/plugin/graphviz/GraphVizPlugin.java (working copy) @@ -180,9 +180,13 @@ } } FileInputStream fis = new FileInputStream(ofile); - byte[] result = new byte[(int) ofile.length()]; - fis.read(result); - return result; + try { + byte[] result = new byte[(int) ofile.length()]; + fis.read(result); + return result; + } finally { + fis.close(); + } } public byte[] readDotImage(File ofile) throws FileNotFoundException, IOException @@ -250,7 +254,23 @@ public String getDotImageURL(String content, boolean dot, XWikiContext context) throws IOException { - String filename = writeDotImage(content, "png", dot); + return getDotResultURL(content, dot, "png", context); + } + + /** + *

+ * Executes graphviz and returns the url for the produced file. + *

+ * + * @param content GraphViz source code. View http://www.graphviz.org/doc/info/lang.html for the language + * specification. + * @param dot Whether the dot engine should be used instead of the neato engine. Other engines are not supported. + * @param format Any GraphViz output format. View http://www.graphviz.org/doc/info/output.html for more information. + * @param context XWikiContext + */ + public String getDotResultURL(String content, boolean dot, String format, XWikiContext context) throws IOException + { + String filename = writeDotImage(content, format, dot); return context.getDoc().getAttachmentURL(filename, "dot", context); } }