package gov.nasa.jpl.mbee.mdk.viewedit;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.nomagic.magicdraw.core.Application;
import com.nomagic.magicdraw.core.GUILog;
import com.nomagic.magicdraw.core.Project;
import com.nomagic.magicdraw.export.image.ImageExporter;
import com.nomagic.magicdraw.uml.symbols.DiagramPresentationElement;
import com.nomagic.uml2.ext.jmi.helpers.StereotypesHelper;
import com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Element;
import com.nomagic.uml2.ext.magicdraw.classes.mdkernel.ElementValue;
import com.nomagic.uml2.ext.magicdraw.classes.mdkernel.InstanceSpecification;
import com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Slot;
import gov.nasa.jpl.mbee.mdk.api.docgen.presentation_elements.PresentationElementEnum;
import gov.nasa.jpl.mbee.mdk.api.incubating.MDKConstants;
import gov.nasa.jpl.mbee.mdk.api.incubating.convert.Converters;
import gov.nasa.jpl.mbee.mdk.docgen.DocGenUtils;
import gov.nasa.jpl.mbee.mdk.docgen.docbook.*;
import gov.nasa.jpl.mbee.mdk.generator.PresentationElementInfo;
import gov.nasa.jpl.mbee.mdk.generator.PresentationElementInstance;
import gov.nasa.jpl.mbee.mdk.generator.PresentationElementUtils;
import gov.nasa.jpl.mbee.mdk.json.JacksonUtils;
import gov.nasa.jpl.mbee.mdk.util.Utils;
import gov.nasa.jpl.mbee.mdk.model.Section;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.*;
import java.util.zip.CRC32;
import java.util.zip.Checksum;
public class DBAlfrescoVisitor extends DBAbstractVisitor {
private JSONObject views = new JSONObject();
private Stack<JSONArray> curContains = new Stack<>(); //MDEV #674 -- change to a Stack of JSONArrays
private Stack<JSONArray> sibviews = new Stack<>(); //sibling views (array of view ids)
private Stack<List<Element>> sibviewsElements = new Stack<>();
private Stack<Set<String>> viewElements = new Stack<>(); //ids of view elements
private Map<String, ObjectNode> images = new HashMap<>();
protected boolean recurse;
private GUILog gl = Application.getInstance().getGUILog();
private static String FILE_EXTENSION = "svg";
private Map<From, String> sourceMapping = new HashMap<>();
private JSONObject view2view = new JSONObject(); //parent view id to array of children view ids (from sibviews)
private Map<Element, List<Element>> view2viewElements = new HashMap<>();
private JSONArray noSections = new JSONArray();
private boolean doc;
protected Set<Element> elementSet = new HashSet<>();
//for ems 2.2 reference tree
// these are linked hash maps to make recursive sense in ViewPresentationGenerator
private Map<Element, JSONArray> view2elements = new LinkedHashMap<>();
private Map<Element, List<PresentationElementInstance>> view2pe = new LinkedHashMap<>();
private Map<Element, List<PresentationElementInstance>> view2peOld = new LinkedHashMap<>();
private Stack<Element> currentView = new Stack<>();
private Stack<PresentationElementInstance> currentSection = new Stack<>(); //if currently in section, sections cannot cross views
private Stack<List<InstanceSpecification>> currentInstanceList = new Stack<>();
private Stack<List<InstanceSpecification>> currentTableInstances = new Stack<>();
private Stack<List<InstanceSpecification>> currentListInstances = new Stack<>();
private Stack<List<InstanceSpecification>> currentParaInstances = new Stack<>();
private Stack<List<InstanceSpecification>> currentSectionInstances = new Stack<>();
private Stack<List<InstanceSpecification>> currentImageInstances = new Stack<>();
private Stack<List<InstanceSpecification>> currentManualInstances = new Stack<>();
private Stack<List<InstanceSpecification>> currentUnusedInstances = new Stack<>();
private Stack<List<PresentationElementInstance>> newpe = new Stack<>();
private boolean main = false; //for ems 2.2 reference tree, only consider generated pe from main view and
//not nested tables/lists since those are embedded in json blob, main is false for Table and List Visitor
private InstanceSpecification viewDocHack = null;
private PresentationElementUtils viu = new PresentationElementUtils();
public DBAlfrescoVisitor(boolean recurse) {
this(recurse, false);
}
public DBAlfrescoVisitor(boolean recurse, boolean main) {
this.recurse = recurse;
sourceMapping.put(From.DOCUMENTATION, "documentation");
sourceMapping.put(From.DVALUE, "value");
sourceMapping.put(From.NAME, "name");
this.main = main;
}
/**
* Simple getter for images
*/
public Map<String, ObjectNode> getImages() {
return images;
}
@SuppressWarnings("unchecked")
@Override
public void visit(DBBook book) {
JSONArray childviews = new JSONArray();
sibviews.push(childviews);
sibviewsElements.push(new ArrayList<Element>());
if (book.getFrom() != null) {
doc = true;
Element docview = book.getFrom();
startView(docview);
for (DocumentElement de : book.getChildren()) {
if (de instanceof DBSection && ((DBSection) de).isView()) {
break;
}
de.accept(this);
}
}
if (recurse || !doc) {
for (DocumentElement de : book.getChildren()) {
if (de instanceof DBSection && ((DBSection) de).isView()) {
de.accept(this);
if (!recurse) {
break;
}
}
}
}
if (doc) {
endView(book.getFrom());
}
sibviews.pop();
sibviewsElements.pop();
}
@Override
public void visit(DBColSpec colspec) {
}
@SuppressWarnings("unchecked")
@Override
public void visit(DBImage image) {
//need to populate view elements with elements in image
JSONObject entry = new JSONObject();
ObjectNode imageEntry = JacksonUtils.getObjectMapper().createObjectNode();
//for (Element e: Project.getProject(image.getImage()).getDiagram(image.getImage()).getUsedModelElements(false)) {
// addToElements(e);
//}
// export image - also keep track of exported images
DiagramPresentationElement diagram = Application.getInstance().getProject()
.getDiagram(image.getImage());
String svgFilename = Converters.getElementToIdConverter().apply(image.getImage());
// create image file
String userhome = System.getProperty("user.home");
File directory = null;
if (userhome != null) {
directory = new File(userhome + File.separator + "mdkimages");
}
else {
directory = new File("mdkimages");
}
if (!directory.exists()) {
directory.mkdirs();
}
// export the image file
File svgDiagramFile = new File(directory, svgFilename);
try {
ImageExporter.export(diagram, ImageExporter.SVG, svgDiagramFile);
} catch (IOException e) {
e.printStackTrace();
}
// calculate the checksum
long cs = 0;
try {
RandomAccessFile f = new RandomAccessFile(svgDiagramFile.getAbsolutePath(), "r");
byte[] data = new byte[(int) f.length()];
f.read(data);
f.close();
Checksum checksum = new CRC32();
checksum.update(data, 0, data.length);
cs = checksum.getValue();
} catch (IOException e) {
gl.log("Could not calculate checksum: " + e.getMessage());
e.printStackTrace();
}
// Lets rename the file to have the hash code
// make sure this matches what's in the View Editor ImageResource.java
String svgCrcFilename = Converters.getElementToIdConverter().apply(image.getImage()) + "_latest" + FILE_EXTENSION;
//gl.log("Exporting diagram to: " + svgDiagramFile.getAbsolutePath());
// keep record of all images found
imageEntry.put("cs", String.valueOf(cs));
imageEntry.put("abspath", svgDiagramFile.getAbsolutePath());
imageEntry.put("extension", FILE_EXTENSION);
images.put(svgFilename, imageEntry);
//MDEV #674 -- Update the type and id: was hard coded.
//
entry.put("type", "Image");
entry.put(MDKConstants.ID_KEY, Converters.getElementToIdConverter().apply(image.getImage()));
entry.put("title", image.getTitle());
curContains.peek().add(entry);
//for ems 2.2 reference tree
if (!main) {
return;
}
InstanceSpecification i = null;
if (!currentImageInstances.peek().isEmpty()) {
i = currentImageInstances.peek().remove(0);
currentInstanceList.peek().remove(0);
}
PresentationElementInstance parentSec = currentSection.isEmpty() ? null : currentSection.peek();
PresentationElementInstance ipe = new PresentationElementInstance(i, entry, PresentationElementEnum.IMAGE, currentView.peek(), (image.getTitle() == null ? "image" : image.getTitle()), parentSec, null);
newpe.peek().add(ipe);
Application.getInstance().getProject().getElementsFactory().createInstanceValueInstance();
}
@SuppressWarnings("unchecked")
@Override
public void visit(DBList list) {
DBAlfrescoListVisitor l = new DBAlfrescoListVisitor(recurse);
list.accept(l);
curContains.peek().add(l.getObject());
viewElements.peek().addAll(l.getListElements());
elementSet.addAll(l.getElementSet());
//for ems 2.2 reference tree
if (!main) {
return;
}
InstanceSpecification i = null;
if (!currentListInstances.peek().isEmpty()) {
i = currentListInstances.peek().remove(0);
currentInstanceList.peek().remove(0);
}
PresentationElementInstance parentSec = currentSection.isEmpty() ? null : currentSection.peek();
PresentationElementInstance ipe = new PresentationElementInstance(i, l.getObject(), PresentationElementEnum.LIST, currentView.peek(), "list", parentSec, null);
newpe.peek().add(ipe);
}
@SuppressWarnings("unchecked")
@Override
public void visit(DBParagraph para) {
JSONObject entry = getJSONForDBParagraph(para);
curContains.peek().add(entry);
//for ems 2.2 reference tree
if (!main) {
return;
}
InstanceSpecification i = null;
if (!currentParaInstances.peek().isEmpty()) {
i = currentParaInstances.peek().remove(0);
currentInstanceList.peek().remove(0);
}
PresentationElementInstance parentSec = currentSection.isEmpty() ? null : currentSection.peek();
PresentationElementInstance ipe = new PresentationElementInstance(i, entry, PresentationElementEnum.PARAGRAPH, currentView.peek(), "paragraph", parentSec, null);
newpe.peek().add(ipe);
}
@SuppressWarnings("unchecked")
protected JSONObject getJSONForDBParagraph(DBParagraph para) {
JSONObject entry = new JSONObject();
if (para.getFrom() != null && para.getFromProperty() != null) {
entry.put("sourceType", "reference");
entry.put("source", Converters.getElementToIdConverter().apply(para.getFrom()));
entry.put("sourceProperty", sourceMapping.get(para.getFromProperty()));
}
else {
entry.put("sourceType", "text");
entry.put("text", DocGenUtils.addP(DocGenUtils.fixString(para.getText(), false)));
}
entry.put("nonEditable", para.isEditable() != null && !para.isEditable());
entry.put("type", "Paragraph");
return entry;
}
@SuppressWarnings("unchecked")
@Override
public void visit(DBText text) {
JSONObject entry = getJSONForDBText(text);
curContains.peek().add(entry);
//for ems 2.2 reference tree
if (!main) {
return;
}
InstanceSpecification i = null;
if (!currentParaInstances.peek().isEmpty()) {
i = currentParaInstances.peek().remove(0);
currentInstanceList.peek().remove(0);
}
PresentationElementInstance parentSec = currentSection.isEmpty() ? null : currentSection.peek();
PresentationElementInstance ipe = new PresentationElementInstance(i, entry, PresentationElementEnum.PARAGRAPH, currentView.peek(), "paragraph", parentSec, null);
newpe.peek().add(ipe);
}
@SuppressWarnings("unchecked")
protected JSONObject getJSONForDBText(DBText text) {
JSONObject entry = new JSONObject();
if (text.getFrom() != null && text.getFromProperty() != null) {
entry.put("sourceType", "reference");
entry.put("source", Converters.getElementToIdConverter().apply(text.getFrom()));
entry.put("sourceProperty", sourceMapping.get(text.getFromProperty()));
}
else {
entry.put("sourceType", "text");
entry.put("text", DocGenUtils.addP(DocGenUtils.fixString(text.getText(), false)));
}
entry.put("type", "Paragraph");
return entry;
}
@SuppressWarnings("unchecked")
@Override
public void visit(DBSection section) {
if (section.isView()) {
Element eview = section.getFrom();
startView(eview);
for (DocumentElement de : section.getChildren()) {
// if (recurse || !(de instanceof DBSection))
if (!recurse && de instanceof DBSection && ((DBSection) de).isView()) {
break;
}
de.accept(this);
addManualInstances(false);
}
//sibviews.pop();
if (section.isNoSection()) {
noSections.add(Converters.getElementToIdConverter().apply(eview));
}
endView(eview);
}
else {
startSection(section);
for (DocumentElement de : section.getChildren()) {
de.accept(this);
addManualInstances(false);
}
endSection(section);
}
}
@SuppressWarnings("unchecked")
@Override
public void visit(DBSimpleList simplelist) {
DBHTMLVisitor html = new DBHTMLVisitor();
simplelist.accept(html);
JSONObject entry = new JSONObject();
entry.put("sourceType", "text");
entry.put("text", html.getOut());
entry.put("type", "Paragraph"); // just show it as html for now
curContains.peek().add(entry);
}
@Override
public void visit(DBTomSawyerDiagram tomSawyerDiagram) {
// super.visit(tomSawyerDiagram);
//` tomSawyerDiagram.accept();
JSONObject entry = new JSONObject();
// entry.put("sourceType", "text");
entry.put("type", "Tsp");
entry.put("tstype", tomSawyerDiagram.getShortType().toString());
// here enter a list of all the elements we need.
JSONArray elements = new JSONArray();
for (Element elem : tomSawyerDiagram.getElements()) {
elements.add(Converters.getElementToIdConverter().apply(elem));
}
entry.put("elements", elements);
curContains.peek().add(entry);
InstanceSpecification i = null;
if (!currentTableInstances.peek().isEmpty()) {
i = currentTableInstances.peek().remove(0);
currentInstanceList.remove(i);
}
PresentationElementInstance parentSec = currentSection.isEmpty() ? null : currentSection.peek();
PresentationElementInstance ipe = new PresentationElementInstance(i, entry, PresentationElementEnum.TABLE, currentView.peek(), "tomsawyer_diagram", parentSec, null);
System.out.println(entry.toJSONString());
newpe.peek().add(ipe);
}
@SuppressWarnings("unchecked")
@Override
public void visit(DBTable table) {
DBAlfrescoTableVisitor v = new DBAlfrescoTableVisitor(this.recurse);
table.accept(v);
curContains.peek().add(v.getObject());
viewElements.peek().addAll(v.getTableElements());
elementSet.addAll(v.getElementSet());
//for ems 2.2 reference tree
if (!main) {
return;
}
InstanceSpecification i = null;
if (!currentTableInstances.peek().isEmpty()) {
i = currentTableInstances.peek().remove(0);
currentInstanceList.peek().remove(0);
}
PresentationElementInstance parentSec = currentSection.isEmpty() ? null : currentSection.peek();
PresentationElementInstance ipe = new PresentationElementInstance(i, v.getObject(), PresentationElementEnum.TABLE, currentView.peek(), table.getTitle() != null ? table.getTitle() : "table", parentSec, null);
newpe.peek().add(ipe);
}
@SuppressWarnings("unchecked")
public void startView(Element e) {
JSONObject view = new JSONObject();
Project project = Project.getProject(e);
// JSONObject specialization = new JSONObject();
//MDEV #673
//Update code to create a specialization
//object and then insert appropriate
//sub-elements in that specialization object.
//
if (StereotypesHelper.hasStereotypeOrDerived(e, Utils.getProductStereotype(project))) {
view.put("type", "Product");
}
else {
view.put("type", "View");
}
String id = Converters.getElementToIdConverter().apply(e);
view.put(MDKConstants.ID_KEY, id);
views.put(id, view);
Set<String> viewE = new HashSet<String>();
viewElements.push(viewE);
//JJS : may need to make this a Stack
JSONArray contains = new JSONArray();
view.put("contains", contains);
this.curContains.push(contains);
//MDEV-443 add view exposed elements to view elements
/*for (Element exposed: Utils.collectDirectedRelatedElementsByRelationshipStereotypeString(e,
DocGen3Profile.queriesStereotype, 1, false, 1))
addToElements(exposed);*/
sibviews.peek().add(Converters.getElementToIdConverter().apply(e));
sibviewsElements.peek().add(e);
JSONArray childViews = new JSONArray();
sibviews.push(childViews);
sibviewsElements.push(new ArrayList<Element>());
//for ems 2.2 reference tree
currentView.push(e);
List<PresentationElementInstance> viewChildren = new ArrayList<PresentationElementInstance>();
newpe.push(viewChildren);
view2pe.put(e, viewChildren);
view2peOld.put(e, new ArrayList<PresentationElementInstance>());
processCurrentInstances(e, e);
if (currentInstanceList.peek().isEmpty()) { //new view, add view doc hack
PresentationElementInstance hack = new PresentationElementInstance(null, null, null, e, null, null, null);
hack.setManual(true);
hack.setViewDocHack(true);
newpe.peek().add(hack);
}
addManualInstances(false);
}
@SuppressWarnings("unchecked")
public void endView(Element e) {
JSONArray viewEs = new JSONArray();
viewEs.addAll(viewElements.pop());
//MDEV #673: update code to use the
//specialization element.
//
JSONObject view = (JSONObject) views.get(Converters.getElementToIdConverter().apply(e));
view.put("displayedElements", viewEs);
view.put("allowedElements", viewEs);
if (recurse && !doc) {
view.put("childrenViews", sibviews.peek());
}
view2view.put(Converters.getElementToIdConverter().apply(e), sibviews.pop());
view2viewElements.put(e, sibviewsElements.pop());
this.curContains.pop();
//for ems 2.2 reference tree
view2elements.put(e, viewEs);
addManualInstances(true);
processUnusedInstances(e);
List<PresentationElementInstance> pes = newpe.pop();
if (pes.isEmpty()) {
//new view with nothing, auto add a pe that with cf that points to view doc
PresentationElementInstance hack = new PresentationElementInstance(null, null, null, e, null, null, null);
hack.setManual(true);
hack.setViewDocHack(true);
pes.add(hack);
}
currentView.pop();
currentManualInstances.pop();
currentImageInstances.pop();
currentSectionInstances.pop();
currentParaInstances.pop();
currentListInstances.pop();
currentTableInstances.pop();
currentInstanceList.pop();
currentUnusedInstances.pop();
}
protected void startSection(DBSection section) {
JSONObject newSection = new JSONObject();
newSection.put("type", "Section");
newSection.put("name", section.getTitle());
JSONArray secArray = new JSONArray();
newSection.put("contains", secArray);
this.curContains.peek().add(newSection);
this.curContains.push(secArray);
//for ems 2.2 reference tree
InstanceSpecification sec = null;
Element loopElement = null;
if (section.getDgElement() instanceof Section) {
if (((Section) section.getDgElement()).getLoopElement() != null) {
loopElement = ((Section) section.getDgElement()).getLoopElement();
sec = findInstanceForSection(loopElement);
}
else {
sec = findInstanceForSection(null);
}
}
if (sec != null) {
currentInstanceList.peek().remove(sec);
currentSectionInstances.peek().remove(sec);
}
PresentationElementInstance parentSec = currentSection.isEmpty() ? null : currentSection.peek();
List<PresentationElementInstance> secChildren = new ArrayList<PresentationElementInstance>();
PresentationElementInstance pe = new PresentationElementInstance(sec, newSection, PresentationElementEnum.SECTION, currentView.peek(), section.getTitle() != null ? section.getTitle() : "section", parentSec, secChildren);
pe.setLoopElement(loopElement);
newpe.peek().add(pe);
currentSection.push(pe);
newpe.push(secChildren);
processCurrentInstances(sec, currentView.peek());
addManualInstances(false);
}
protected void endSection(DBSection section) {
this.curContains.pop();
//for ems 2.2 reference tree
addManualInstances(true);
processUnusedInstances(currentView.peek());
newpe.pop();
currentSection.pop();
currentManualInstances.pop();
currentImageInstances.pop();
currentSectionInstances.pop();
currentParaInstances.pop();
currentListInstances.pop();
currentTableInstances.pop();
currentInstanceList.pop();
currentUnusedInstances.pop();
}
@Deprecated
public JSONObject getViews() {
return views;
}
public JSONObject getHierarchy() {
return view2view;
}
public Map<Element, List<Element>> getHierarchyElements() {
return view2viewElements;
}
public JSONArray getNosections() {
return noSections;
}
public Map<Element, JSONArray> getView2Elements() {
return view2elements;
}
public Set<Element> getElementSet() {
return elementSet;
}
public Map<Element, List<PresentationElementInstance>> getView2Pe() {
return view2pe;
}
public Map<Element, List<PresentationElementInstance>> getView2Unused() {
return view2peOld;
}
private void processCurrentInstances(Element viewOrSection, Element view) {
PresentationElementInfo info = viu.getCurrentInstances(viewOrSection, view);
currentInstanceList.push(info.getAll());
currentImageInstances.push(info.getImages());
currentTableInstances.push(info.getTables());
currentParaInstances.push(info.getParas());
currentListInstances.push(info.getLists());
currentSectionInstances.push(info.getSections());
currentManualInstances.push(info.getManuals());
currentUnusedInstances.push(info.getUnused());
if (info.getViewDocHack() != null) {
viewDocHack = info.getViewDocHack();
}
}
private InstanceSpecification findInstanceForSection(Element e) {
if (e != null) {
for (InstanceSpecification is : currentSectionInstances.peek()) {
for (Element el : is.getOwnedElement()) {
if (el instanceof Slot && ((Slot) el).getDefiningFeature().getName().equals("generatedFromElement") &&
!((Slot) el).getValue().isEmpty() && ((Slot) el).getValue().get(0) instanceof ElementValue &&
((ElementValue) ((Slot) el).getValue().get(0)).getElement() == e) {
return is;
}
}
}
return null;
}
for (InstanceSpecification is : currentSectionInstances.peek()) {
boolean loop = false;
for (Element el : is.getOwnedElement()) {
if (el instanceof Slot && ((Slot) el).getDefiningFeature().getName().equals("generatedFromElement")) {
loop = true;
}
break;
}
if (loop) {
continue;
}
return is;
}
return null;
}
private void addManualInstances(boolean all) {
List<InstanceSpecification> instances = currentInstanceList.peek();
List<InstanceSpecification> manuals = currentManualInstances.peek();
while (!instances.isEmpty() && manuals.contains(instances.get(0))) {
InstanceSpecification is = instances.get(0);
PresentationElementInstance pe = new PresentationElementInstance(is, null, null, null, null, null, null);
pe.setManual(true);
if (is == viewDocHack) {
pe.setViewDocHack(true);
pe.setView(currentView.peek());
viewDocHack = null;
}
newpe.peek().add(pe);
manuals.remove(is);
instances.remove(is);
}
if (all) {
for (InstanceSpecification is : new ArrayList<InstanceSpecification>(manuals)) {
PresentationElementInstance pe = new PresentationElementInstance(is, null, null, null, null, null, null);
pe.setManual(true);
if (is == viewDocHack) {
pe.setViewDocHack(true);
pe.setView(currentView.peek());
viewDocHack = null;
}
newpe.peek().add(pe);
manuals.remove(is);
instances.remove(is);
}
}
}
private void processUnusedInstances(Element v) {
for (InstanceSpecification is : currentTableInstances.peek()) {
view2peOld.get(v).add(new PresentationElementInstance(is, null, PresentationElementEnum.TABLE, v, is.getName(), null, null));
}
for (InstanceSpecification is : currentListInstances.peek()) {
view2peOld.get(v).add(new PresentationElementInstance(is, null, PresentationElementEnum.LIST, v, is.getName(), null, null));
}
for (InstanceSpecification is : currentParaInstances.peek()) {
view2peOld.get(v).add(new PresentationElementInstance(is, null, PresentationElementEnum.PARAGRAPH, v, is.getName(), null, null));
}
for (InstanceSpecification is : currentImageInstances.peek()) {
view2peOld.get(v).add(new PresentationElementInstance(is, null, PresentationElementEnum.IMAGE, v, is.getName(), null, null));
}
for (InstanceSpecification is : currentSectionInstances.peek()) {
view2peOld.get(v).add(new PresentationElementInstance(is, null, PresentationElementEnum.SECTION, v, is.getName(), null, null));
}
for (InstanceSpecification is : currentUnusedInstances.peek()) {
view2peOld.get(v).add(new PresentationElementInstance(is, null, null, v, is.getName(), null, null));
}
}
}