package gov.nasa.jpl.mbee.mdk.docgen;
import com.nomagic.magicdraw.core.Application;
import com.nomagic.magicdraw.core.GUILog;
import com.nomagic.magicdraw.export.image.ImageExporter;
import com.nomagic.magicdraw.properties.BooleanProperty;
import com.nomagic.magicdraw.properties.ElementProperty;
import com.nomagic.magicdraw.properties.NumberProperty;
import com.nomagic.magicdraw.properties.StringProperty;
import com.nomagic.magicdraw.uml.symbols.DiagramPresentationElement;
import com.nomagic.uml2.ext.magicdraw.classes.mdkernel.*;
import gov.nasa.jpl.mbee.mdk.docgen.docbook.DocumentElement;
import gov.nasa.jpl.mbee.mdk.docgen.view.ViewElement;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;
import java.io.*;
import java.util.*;
public class DocGenUtils {
/**
* this was trying to use regex only to convert html tags to docbook tags,
* should probably switch to just using jsoup
*/
@SuppressWarnings("serial")
public static final Map<String, String> html2docbookConvert = new HashMap<String, String>() {
{
put("<p>|<p [^>]*>", "<para>");
put("</p>", "</para>");
put("<ul>|<ul [^>]*>",
"<itemizedlist spacing=\"compact\">");
put("</ul>", "</itemizedlist>");
put("<ol>|<ol [^>]*>",
"<orderedlist spacing=\"compact\">");
put("</ol>", "</orderedlist>");
put("<li>|<li [^>]*>",
"<listitem><para>");
put("</li>", "</para></listitem>");
put("<b>|<b [^>]*>|<em>|<em [^>]*>|<strong>|<strong [^>]*>",
"<emphasis role=\"bold\">");
put("<h[1-6]>|<h[1-6] [^>]*>", "<para><emphasis role=\"bold\">");
put("<s>|<strike>|<s [^>]*>|<strike [^>]*>",
"<emphasis role=\"strikethrough\">");
put("<i>|<i [^>]*>", "<emphasis>");
put("<u>|<u [^>]*>",
"<emphasis role=\"underline\">");
put("<span>|<span [^>]*>|</span>|<br>|<br/>|</br>|<br />",
"");
put("</b>|</i>|</u>|</strong>|</em>|</s>|</strike>",
"</emphasis>");
put("</h[1-6]>", "</emphasis></para>");
put("<font [^>]*>|</font>", "");
put("<sup>|<sup [^>]*>",
"<superscript>");
put("<sub>|<sub [^>]*>",
"<subscript>");
put("</sup>", "</superscript>");
put("</sub>", "</subscript>");
put("<a href=\"(http[^\"]+)\">([^<]*)</a>",
"<link xlink:href=\"$1\">$2</link>");
put("<a href=\"(file[^\"]+)\">([^<]*)</a>",
"<link xlink:href=\"$1\">$2</link>");
put("<a href=\"(mailto[^\"]+)\">([^<]*)</a>",
"<link xlink:href=\"$1\">$2</link>");
put("<a href=\"mdel://([^\"&^\\?]+)(\\?[^\"]*)?\">([^<]*)</a>",
"<link linkend=\"$1\">$3</link>");
put("<img [^>]*src=\"([^>]+)\"[^>]*></img>", "<imageobject><imagedata fileref=\"$1\" scalefit=\"1\"/></imageobject>");
put("<img [^>]*src=\"([^>]+)\"[^>]*/>", "<imageobject><imagedata fileref=\"$1\" scalefit=\"1\"/></imageobject>");
put("<pre>|<pre [^>]*>", "<screen>");
put("</pre>", "</screen>");
put("<svg",
"<mediaobject><imageobject><imagedata><svg");
put("</svg>",
"</svg></imagedata></imageobject></mediaobject>");
put(" ", " ");
put("²",
"<superscript>2</superscript>");
put("³",
"<superscript>3</superscript>");
}
};
/**
* docbook ignores regular white space in table cells, this is to force
* indentation in docbook, 1 indent is 4 spaces
*
* @param name
* @param depth
* @return
*/
public static String getIndented(String name, int depth) {
String space = "";
for (int i = 1; i < depth; i++) {
space += " ";
}
return space + name;
}
/**
* given any object tries to return a string representation suitable for use
* in docbook<br/>
*
* @param s
* @return
*/
public static String fixString(Object s) {
return fixString(s, true);
}
public static String fixString(Object s, boolean convertHtml) {
String rv;
// may want to look at
// com.nomagic.magicdraw.uml.RepresentationTextCreator.getRepresentedText
if (s instanceof String) {
if (((String) s).contains("<html>")) {
if (convertHtml) {
return gov.nasa.jpl.mbee.mdk.util.HtmlManipulator.replaceHtmlEntities(html2docbook((String) s));
}
else {
return gov.nasa.jpl.mbee.mdk.util.Utils.stripHtmlWrapper((String) s);
}
}
else {
return gov.nasa.jpl.mbee.mdk.util.HtmlManipulator.replaceHtmlEntities(((String) s)
.replaceAll("&(?![A-Za-z#0-9]+;)", "&").replaceAll("<([>=\\s])", "<$1")
.replaceAll("<<", "<<").replaceAll("<(?![^>]+>)", "<"));
}
}
else if (s instanceof Integer) {
return Integer.toString((Integer) s);
}
else if (s instanceof InstanceValue) {
InstanceSpecification is = ((InstanceValue) s).getInstance();
if (is != null) {
return fixString(is.getName());
}
}
else if (s instanceof ElementValue) {
Element e = ((ElementValue) s).getElement();
return fixString(e);
}
else if (s instanceof LiteralBoolean) {
return Boolean.toString(((LiteralBoolean) s).isValue());
}
else if (s instanceof LiteralString) {
return fixString(((LiteralString) s).getValue());
}
else if (s instanceof LiteralInteger) {
return Integer.toString(((LiteralInteger) s).getValue());
}
else if (s instanceof LiteralUnlimitedNatural) {
return Integer.toString(((LiteralUnlimitedNatural) s).getValue());
}
else if (s instanceof LiteralReal) {
return Double.toString(((LiteralReal) s).getValue());
}
else if ((rv = getRestrictedValue(s)) != null) {
return rv;
}
else if (s instanceof NamedElement) {
return fixString(((NamedElement) s).getName());
}
else if (s instanceof Comment) {
return fixString(((Comment) s).getBody());
}
else if (s instanceof StringProperty) {
return fixString(((StringProperty) s).getString());
}
else if (s instanceof NumberProperty) {
return ((NumberProperty) s).getValue().toString();
}
else if (s instanceof BooleanProperty) {
return ((BooleanProperty) s).getBooleanObject().toString();
}
else if (s instanceof ElementProperty) {
return fixString(((ElementProperty) s).getElement());
}
else if (s instanceof Slot) {
return slot2String((Slot) s);
}
else if (s != null) {
return fixString(s.toString());
}
return "";
}
//in case Expression is used other than RestrictedValue, only considered as RestrictedValue when 1st operand is LiteralString with "RestrictedValue"
private static String getRestrictedValue(Object s) {
if (s instanceof Expression) { //Expression is NamedElement
List<ValueSpecification> ves = ((Expression) s).getOperand();
if (ves.size() > 0 && ves.get(0) instanceof LiteralString) {
if (((LiteralString) ves.get(0)).getValue().compareTo("RestrictedValue") == 0) { //then assumed to be RestrictedValue
// ves.size() == 3 (LiteralString ("RestrictedValue"), ElementValue, Expression) - see CreatedRestrictedValueAction.actionPerformed
if (ves.size() == 3 && ves.get(1) instanceof ElementValue) {
return fixString((((NamedElement) ((ElementValue) ves.get(1)).getElement()).getName()));
}
else {
return "malformed restricted value";
}
}
}
}
return null; //assume not to be a RestrictedValue
}
public static Object getLiteralValue(Object s, boolean convertHtml) {
if (s instanceof String) {
return fixString(s, convertHtml);
}
else if (s instanceof Integer) {
return s;
}
else if (s instanceof InstanceValue) {
InstanceSpecification is = ((InstanceValue) s).getInstance();
if (is != null) {
return getLiteralValue(is.getName(), convertHtml);
}
}
else if (s instanceof ElementValue) {
Element e = ((ElementValue) s).getElement();
return getLiteralValue(e, convertHtml);
}
else if (s instanceof LiteralBoolean) {
return ((LiteralBoolean) s).isValue();
}
else if (s instanceof LiteralString) {
return ((LiteralString) s).getValue();
}
else if (s instanceof LiteralInteger) {
return ((LiteralInteger) s).getValue();
}
else if (s instanceof LiteralUnlimitedNatural) {
return ((LiteralUnlimitedNatural) s).getValue();
}
else if (s instanceof LiteralReal) {
return ((LiteralReal) s).getValue();
}
else if (s instanceof NamedElement) {
return ((NamedElement) s).getName();
}
else if (s instanceof Comment) {
return getLiteralValue(((Comment) s).getBody(), convertHtml);
}
else if (s instanceof StringProperty) {
return getLiteralValue(((StringProperty) s).getString(), convertHtml);
}
else if (s instanceof NumberProperty) {
return ((NumberProperty) s).getValue();
}
else if (s instanceof BooleanProperty) {
return ((BooleanProperty) s).getBooleanObject();
}
else if (s instanceof ElementProperty) {
return getLiteralValue(((ElementProperty) s).getElement(), convertHtml);
}
else if (s instanceof Slot) {
return slot2String((Slot) s);
}
return s;
}
/**
* gives sensible text representation for slot element
*
* @param s
* @return
*/
public static String slot2String(Slot s) {
return slot2String(s, true);
}
public static String slot2String(Slot s, boolean includeName) {
String string = (includeName ? s.getDefiningFeature().getName() + " = " : "");
List<String> values = new ArrayList<String>();
for (ValueSpecification vs : s.getValue()) {
values.add(fixString(vs));
}
return string + gov.nasa.jpl.mbee.mdk.util.Utils.join(values, ", ");
}
/**
* tries to make s into a docbook paragraph if not already one
*
* @param s
* @return
*/
public static String addDocbook(String s) {
String ss = html2docbook(s);
if (ss.matches("(?s).*<para>.*")) // (s?) is the flag for DOTALL mode, .
// doesn't match newlines by default
// if (s.matches("(?s)\\s*<para>.*</para>\\s*"))
{
return ss;
}
return "<para>" + ss + "</para>";
}
/**
* tries to make s into a html paragraph if not already one
*
* @param s
* @return
*/
public static String addP(String s) {
if (s.matches("(?s).*<p>.*")) // (s?) is the flag for DOTALL mode, .
// doesn't match newlines by default
// if (s.matches("(?s)\\s*<para>.*</para>\\s*"))
{
return s;
}
return "<p>" + s + "</p>";
}
/**
* this is to help pdf transfrom be able to do wordwrap at non whitespace
* chars, adds an invisible space to chars that should be able to break
* probably should use some regex instead...
*
* @param s
* @return
*/
public static String addInvisibleSpace(String s) {
return s.replaceAll(";", ";").replaceAll("\\.", ".").replaceAll("\\(", "(")
.replaceAll("\\)", ")").replaceAll(",", ",").replaceAll("/", "/")
.replaceAll("_", "_").replaceAll("::", "::");
}
/**
* if string contains html, converts it to docbook<br/>
* also does some special processing if there's informal table elements,
* removes width on tables so pdf transforms don't get cut off if width is
* set too big should use jsoup processing to replace the regex map above
*
* @param html
* @return
*/
public static String html2docbook(String html) {
if (!html.contains("<html>")) {
return html;
}
String s = null;
Document d = Jsoup.parse(html);
Elements tables = d.select("table.informal");
if (!tables.isEmpty()) {
tables.tagName("informaltable");
tables.removeAttr("width");
}
tables = d.select("table");
tables.removeAttr("width");
Elements paragraphs = d.select("p");
for (org.jsoup.nodes.Element e : paragraphs) {
if (!e.hasText() || e.html().equals(" ") || e.html().equals(" ")) {
e.remove();
}
}
for (org.jsoup.nodes.Element e : d.select("span")) {
if (e.hasAttr("style") && e.attr("style").startsWith("background-color")) {
String style = e.attr("style");
String color = style.substring(style.indexOf('#') + 1);
e.tagName("phrase");
e.removeAttr("style");
e.attr("role", color);
}
}
s = d.toString();
int start = s.indexOf("<body>");
int end = s.indexOf("</body>");
if (start > -1 && end > -1) {
s = s.substring(start + 6, end);
}
for (String key : html2docbookConvert.keySet()) {
s = s.replaceAll(key, html2docbookConvert.get(key));
}
return s;
}
/**
* Generates svg and png files of the diagram passed in, the diagram names
* will be the diagram id in magicdraw
*
* @param diagram the magicdraw diagram element
* @param outputdir directory for docbook xml output (without trailing slash)
* @param outputfilename name of the docbook xml output name (without extension, this
* will be used to generate a folder called outputfilename_files
* inside outputdir where the diagrams will be stored)
* @param genNew true or false, if true, will always generate new image, if
* not, will check whether image is already there before
* generating
* @param debug
* @throws IOException
*/
public static List<String> exportDiagram(Diagram d, File directory, boolean genNew) throws IOException {
GUILog gl = Application.getInstance().getGUILog();
List<String> res = new ArrayList<String>();
DiagramPresentationElement diagram = Application.getInstance().getProject().getDiagram(d);
String pngfilename = diagram.getID() + ".png";
String svgfilename = diagram.getID() + ".svg";
// String templateFolderName = outputdir + "/" + outputfilename +
// "_files";
// File directory = new File(templateFolderName);
File pngdiagramFile = new File(directory, pngfilename);
File svgdiagramFile = new File(directory, svgfilename);
// String svgfname = outputfilename + "_files" + "/" + svgfilename;
String svgfname = "images/" + svgfilename;// System.getProperty("file.separator")
// + svgfilename;
res.add(svgfname);
if (genNew || !pngdiagramFile.exists()) {
try {
gl.log("[DocGen] Exporting Diagram " + diagram.getName() + " " + svgfname);
ImageExporter.export(diagram, ImageExporter.SVG, svgdiagramFile);
ImageExporter.export(diagram, ImageExporter.PNG, pngdiagramFile);
} catch (IOException e) {
e.printStackTrace();
return res;
}
}
else {
gl.log("[DocGen] Exporting diagram: Image file for " + diagram.getName()
+ " exists. Using previously generated file.");
}
// whether to scale to width or not, in svg file width is specified in
// inches, check it's less than width of pdf portrait paper
String scale = "true";
try {
BufferedReader svg = new BufferedReader(new FileReader(svgdiagramFile));
String line = svg.readLine();
while (line != null) {
if (line.startsWith("<svg")) {
int widthindex = line.indexOf("width");
if (widthindex > -1) {
int endindex = line.indexOf("\"", widthindex + 7);
String w = line.substring(widthindex + 7, endindex - 2);
double wd = Double.parseDouble(w);
if (wd < 5.5) {
scale = "false";
}
}
break;
}
line = svg.readLine();
}
svg.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (NumberFormatException e) {
e.printStackTrace();
}
res.add(scale);
return res;
}
/**
* return names of a collection of named elements
*
* @param elements
* @return
*/
public static List<String> getElementNames(Collection<NamedElement> elements) {
List<String> names = new ArrayList<String>();
for (NamedElement e : elements) {
names.add(e.getName());
}
return names;
}
public static DocumentElement ecoreTranslateView(ViewElement ve, boolean forViewEditor) {
DocumentElement de = null;
de = (new DocGenViewDBSwitch(forViewEditor)).doSwitch(ve);
return de;
}
}