package gov.nasa.jpl.mbee.mdk;
import com.nomagic.actions.ActionsCategory;
import com.nomagic.actions.ActionsManager;
import com.nomagic.actions.NMAction;
import com.nomagic.magicdraw.actions.*;
import com.nomagic.magicdraw.core.Project;
import com.nomagic.magicdraw.ui.browser.Node;
import com.nomagic.magicdraw.ui.browser.Tree;
import com.nomagic.magicdraw.uml.symbols.DiagramPresentationElement;
import com.nomagic.magicdraw.uml.symbols.PresentationElement;
import com.nomagic.uml2.ext.jmi.helpers.StereotypesHelper;
import com.nomagic.uml2.ext.magicdraw.activities.mdfundamentalactivities.Activity;
import com.nomagic.uml2.ext.magicdraw.auxiliaryconstructs.mdmodels.Model;
import com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Class;
import com.nomagic.uml2.ext.magicdraw.classes.mdkernel.*;
import com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Package;
import com.nomagic.uml2.ext.magicdraw.mdprofiles.Stereotype;
import gov.nasa.jpl.mbee.mdk.actions.*;
import gov.nasa.jpl.mbee.mdk.docgen.DocGenProfile;
import gov.nasa.jpl.mbee.mdk.docgen.actions.*;
import gov.nasa.jpl.mbee.mdk.generator.DocumentGenerator;
import gov.nasa.jpl.mbee.mdk.util.MDUtils;
import gov.nasa.jpl.mbee.mdk.util.TicketUtils;
import gov.nasa.jpl.mbee.mdk.util.Utils;
import gov.nasa.jpl.mbee.mdk.util.Utils2;
import gov.nasa.jpl.mbee.mdk.mms.actions.*;
import gov.nasa.jpl.mbee.mdk.model.CollectActionsVisitor;
import gov.nasa.jpl.mbee.mdk.model.Document;
import gov.nasa.jpl.mbee.mdk.model.UserScript;
import gov.nasa.jpl.mbee.mdk.model.actions.RunUserScriptAction;
import gov.nasa.jpl.mbee.mdk.model.actions.RunUserValidationScriptAction;
import java.util.*;
import java.util.stream.IntStream;
public class MDKConfigurator implements BrowserContextAMConfigurator, DiagramContextAMConfigurator {
private Set<ActionsManager> viewQueryCalled = new HashSet<>();
@Override
public int getPriority() {
return ConfiguratorWithPriority.MEDIUM_PRIORITY;
}
@Override
public void configure(ActionsManager manager, Tree browser) {
Node no = browser.getSelectedNode();
if (no == null) {
return;
}
Object o = no.getUserObject();
if (!(o instanceof Element)) {
return;
}
List<Element> elements = new ArrayList<>();
for (Node node : browser.getSelectedNodes()) {
if (node == null) {
continue;
}
Object ob = node.getUserObject();
if (!(ob instanceof Element)) {
continue;
}
elements.add((Element) ob);
}
addElementActions(manager, (Element) o, elements);
}
@Override
public void configure(ActionsManager manager, DiagramPresentationElement diagram,
PresentationElement[] selected, PresentationElement requestor) {
if (repainting()) {
return;
}
List<Element> es = new ArrayList<>();
for (PresentationElement pe : selected) {
if (pe.getElement() != null) {
es.add(pe.getElement());
}
}
if (!es.isEmpty()) {
addElementActions(manager, es.get(0), es);
}
addDiagramActions(manager, diagram);
}
public static boolean repainting() {
StackTraceElement[] trace = Thread.currentThread().getStackTrace();
String lastMethod = "";
for (StackTraceElement traceElem : trace) {
if (traceElem.getClassName().contains("MainFrame") && (traceElem.getMethodName().equals("paint") || lastMethod.equals("paintImmediately"))) {
//Debug.outln( "@@@ repainting() = true" );
return true;
}
if (traceElem.getClassName().endsWith("RepaintManager") && traceElem.getMethodName().equals("paint")) {
//Debug.outln( "@@@ repainting() = true" );
return true;
}
//Debug.outln( "class name = " + traceElem.getClassName() + ", method name = " + traceElem.getMethodName() + ", last method name = " + lastMethod );
lastMethod = traceElem.getMethodName();
}
//Debug.outln( "@@@ repainting() = false:" );
//Debug.outln( MoreToString.Helper.toString( trace ) );
return false;
}
private void dumpCategory(ActionsCategory category, int i) {
IntStream.range(0, i++).forEach(ignored -> System.out.print("-"));
System.out.println("[C] " + category.getID() + " : " + category.getName());
for (ActionsCategory c : category.getCategories()) {
dumpCategory(c, i);
}
for (NMAction action : category.getActions()) {
IntStream.range(0, i).forEach(ignored -> System.out.print("-"));
System.out.println("[A] " + action.getID() + " : " + action.getName());
}
}
private void addElementActions(ActionsManager manager, Element e, List<Element> es) {
//manager.getCategories().forEach(category -> dumpCategory(category, 0));
Project project = Project.getProject(e);
if (project == null && !es.isEmpty()) {
project = Project.getProject(es.iterator().next());
}
if (project == null) {
return;
}
Stereotype sysmlview = Utils.getViewStereotype(project);
Stereotype sysmlviewpoint = Utils.getViewpointStereotype(project);
Stereotype documentView = Utils.getProductStereotype(project);
Stereotype classview = Utils.getViewClassStereotype(project);
Stereotype elementGroupStereotype = Utils.getElementGroupStereotype(project);
// top-level context menu: Refactor With ID
ActionsCategory refactorWithIDActionCat = myCategory(manager, "Refactor With ID", "Refactor With ID");
if (e instanceof com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Class & !(e instanceof com.nomagic.uml2.ext.magicdraw.components.mdbasiccomponents.Component)) {
if (manager.getActionFor(ClassToComponentRefactorWithIDAction.DEFAULT_ID) == null) {
refactorWithIDActionCat.addAction(new ClassToComponentRefactorWithIDAction(es));
}
}
if (e instanceof com.nomagic.uml2.ext.magicdraw.components.mdbasiccomponents.Component) {
if (manager.getActionFor(ComponentToClassRefactorWithIDAction.DEFAULT_ID) == null) {
refactorWithIDActionCat.addAction(new ComponentToClassRefactorWithIDAction(es));
}
}
ActionsCategory modelLoad = myCategory(manager, "MMSContext", "MMS");
if (!TicketUtils.isTicketSet(project)) {
ActionsCategory login = getCategory(manager, "LoginOption", "LoginOption", modelLoad);
if (manager.getActionFor(MMSLoginAction.DEFAULT_ID) == null) {
login.addAction(new MMSLoginAction());
}
}
// Ivan: Little hack to disable category by adding a disabled child action and deriving category state using useActionForDisable
//final MDAction mda = new MDAction(null, null, null, "null");
//mda.updateState();
//mda.setEnabled(false);
//modelLoad.addAction(mda);
ActionsCategory models = getCategory(manager, "MMSModel", "MMSModel", modelLoad);
if (MDUtils.isDeveloperMode()) {
if (e instanceof Model && manager.getActionFor(CommitProjectAction.DEFAULT_ID) == null) {
models.addAction(new CommitProjectAction(project, false, true));
models.addAction(new CommitProjectAction(project, true, true));
}
}
if (manager.getActionFor(ValidateElementRecursivelyAction.DEFAULT_ID) == null) {
models.addAction(new ValidateElementRecursivelyAction(es, "Validate Models"));
}
if (manager.getActionFor(ValidateElementDepthAction.DEFAULT_ID) == null) {
models.addAction(new ValidateElementDepthAction(es, "Validate Models (specified depth)"));
}
if (manager.getActionFor(ValidateElementAction.DEFAULT_ID) == null) {
models.addAction(new ValidateElementAction(es, "Validate Element"));
}
ActionsStateUpdater.updateActionsState();
// add menus in reverse order since they are inserted at top
// View Interaction menu
if (StereotypesHelper.hasStereotypeOrDerived(e, DocGenProfile.validationScriptStereotype)) {
ActionsCategory c = myCategory(manager, "ViewInteraction", "View Interaction");
UserScript us = new UserScript();
us.setDgElement(e);
List<Element> targets = Utils.collectDirectedRelatedElementsByRelationshipStereotypeString(e,
DocGenProfile.queriesStereotype, 1, false, 1);
targets.addAll(Utils.collectDirectedRelatedElementsByRelationshipStereotypeString(e,
DocGenProfile.oldQueriesStereotype, 1, false, 1));
us.setTargets(Utils2.asList(targets, Object.class));
if (manager.getActionFor(RunUserValidationScriptAction.DEFAULT_ID) == null) {
c.addAction(new RunUserValidationScriptAction(us, true));
}
}
else if (StereotypesHelper.hasStereotypeOrDerived(e, DocGenProfile.userScriptStereotype)) {
ActionsCategory c = myCategory(manager, "ViewInteraction", "View Interaction");
UserScript us = new UserScript();
us.setDgElement(e);
List<Element> targets = Utils.collectDirectedRelatedElementsByRelationshipStereotypeString(e,
DocGenProfile.queriesStereotype, 1, false, 1);
targets.addAll(Utils.collectDirectedRelatedElementsByRelationshipStereotypeString(e,
DocGenProfile.oldQueriesStereotype, 1, false, 1));
us.setTargets(Utils2.asList(targets, Object.class));
if (manager.getActionFor(RunUserScriptAction.DEFAULT_ID) == null) {
c.addAction(new RunUserScriptAction(us, true));
}
}
boolean canShowGeneration = true;
for (Element element : es) {
if (!StereotypesHelper.hasStereotypeOrDerived(element, sysmlview) && !StereotypesHelper.hasStereotypeOrDerived(element, elementGroupStereotype)) {
canShowGeneration = false;
break;
}
}
if (canShowGeneration) {
// There may be no view query actions to add, in which case we need
// to avoid adding an empty menu category, so the category is
// removed in this case.
// Not worth implementing multi-selection for this legacy feature. Would require refactoring and testing.
if (classview != null && es.size() == 1 && StereotypesHelper.hasStereotypeOrDerived(e, sysmlview)) {
Boolean collectActions = (Boolean) StereotypesHelper.getStereotypePropertyFirst(e, classview, "collectViewActions");
if (collectActions != null && collectActions) {
ActionsCategory category = (ActionsCategory) manager.getActionFor("ViewInteraction");
if (category == null) {
category = new MDActionsCategory("ViewInteraction", "View Interaction");
category.setNested(true);
boolean added = addViewQueryActions(manager, category, (NamedElement) e);
if (added) {
manager.addCategory(0, category);
}
}
}
}
ActionsCategory modelLoad2 = myCategory(manager, "MMSContext", "MMS");
ActionsCategory viewInstances = getCategory(manager, "MMSViewInstance", "MMSViewInstance", modelLoad2);
NMAction action = manager.getActionFor(GenerateViewPresentationAction.DEFAULT_ID);
if (action == null) {
viewInstances.addAction(new GenerateViewPresentationAction(new LinkedHashSet<>(es), false));
}
action = manager.getActionFor(GenerateViewPresentationAction.RECURSE_DEFAULT_ID);
if (action == null) {
viewInstances.addAction(new GenerateViewPresentationAction(new LinkedHashSet<>(es), true));
}
String url;
if (StereotypesHelper.hasStereotype(project.getPrimaryModel(), "ModelManagementSystem")
&& (url = (String) StereotypesHelper.getStereotypePropertyFirst(project.getPrimaryModel(), "ModelManagementSystem", "MMS URL")) != null
&& !url.isEmpty()) {
ActionsCategory tracingCategory = manager.getCategory("TRACING_CATEGORY");
if (tracingCategory != null) {
action = manager.getActionFor(MMSViewLinkAction.DEFAULT_ID);
if (action == null) {
tracingCategory.addAction(new MMSViewLinkAction(es));
}
}
}
ActionsStateUpdater.updateActionsState();
//ActionsCategory c = myCategory(manager, "ViewEditor", "View Editor");
//action = manager.getActionFor(ExportViewAction.DEFAULT_ID);
//if (action == null)
//addEditableViewActions(c, (NamedElement)e);
}
/*if (StereotypesHelper.hasStereotype(e, ViewEditorProfile.project)) { // REVIEW
// --
// hasStereotypeOrDerived()?
ActionsCategory c = myCategory(manager, "ViewEditor", "View Editor");
NMAction act = manager.getActionFor(OrganizeViewEditorAction.DEFAULT_ID);
if (act == null)
c.addAction(new OrganizeViewEditorAction(e));
act = manager.getActionFor(DeleteProjectAction.DEFAULT_ID);
if (act == null)
c.addAction(new DeleteProjectAction(e));
act = manager.getActionFor(MMSLogoutAction.DEFAULT_ID);
if (act == null)
c.addAction(new MMSLogoutAction());
}
if (StereotypesHelper.hasStereotype(e, ViewEditorProfile.volume)) { // REVIEW
// --
// hasStereotypeOrDerived()?
ActionsCategory c = myCategory(manager, "ViewEditor", "View Editor");
NMAction act = manager.getActionFor(DeleteVolumeAction.DEFAULT_ID);
if (act == null)
c.addAction(new DeleteVolumeAction(e));
act = manager.getActionFor(MMSLogoutAction.DEFAULT_ID);
if (act == null)
c.addAction(new MMSLogoutAction());
}
if (StereotypesHelper.hasStereotype(e, ViewEditorProfile.document)
|| StereotypesHelper.hasStereotypeOrDerived(e, documentView)) {
ActionsCategory c = myCategory(manager, "ViewEditor", "View Editor");
NMAction act = manager.getActionFor(DeleteDocumentAction.DEFAULT_ID);
if (act == null)
c.addAction(new DeleteDocumentAction(e));
if (StereotypesHelper.hasStereotypeOrDerived(e, documentView)) {
act = manager.getActionFor(OrganizeDocumentAction.DEFAULT_ID);
if (act == null)
c.addAction(new OrganizeDocumentAction(e));
}
}*/
/*if (e == project.getPrimaryModel()) {
NMAction act = null;
ActionsCategory c = myCategory(manager, "DocGen", "DocGen");
// DefaultPropertyResourceProvider pp = new
// DefaultPropertyResourceProvider();
act = manager.getActionFor(ValidateOldDocgen.DEFAULT_ID);
if (act == null) {
c.addAction(new ValidateOldDocgen());
}
}
*/
// DocGen menu
if ((e instanceof Activity && StereotypesHelper.hasStereotypeOrDerived(e,
DocGenProfile.documentStereotype)) || StereotypesHelper.hasStereotypeOrDerived(e, sysmlview)) {
NMAction act = null;
ActionsCategory c = myCategory(manager, "DocGen", "DocGen");
// DefaultPropertyResourceProvider pp = new
// DefaultPropertyResourceProvider();
act = manager.getActionFor(ValidateDocument3Action.DEFAULT_ID);
if (act == null) {
c.addAction(new ValidateDocument3Action(e));
}
act = manager.getActionFor(ValidateViewStructureAction.DEFAULT_ID);
if (act == null && e instanceof Classifier) {
c.addAction(new ValidateViewStructureAction(e));
}
act = manager.getActionFor(ViewDocument3Action.DEFAULT_ID);
if (act == null) {
c.addAction(new ViewDocument3Action(e));
}
act = manager.getActionFor(GenerateDocumentAction.DEFAULT_ID);
if (act == null) {
c.addAction(new GenerateDocumentAction(e));
}
if (StereotypesHelper.hasStereotypeOrDerived(e, documentView)) {
if (e instanceof Package) {
act = manager.getActionFor(NumberDependencyAction.DEFAULT_ID);
if (act == null) {
c.addAction(new NumberDependencyAction(e));
}
act = manager.getActionFor(MigrateToClassViewAction.DEFAULT_ID);
if (act == null) {
c.addAction(new MigrateToClassViewAction(e));
}
}
if (e instanceof Class) {
act = manager.getActionFor(NumberAssociationAction.DEFAULT_ID);
if (act == null) {
c.addAction(new NumberAssociationAction((Class) e));
}
}
//act = manager.getActionFor(PublishDocWebAction.DEFAULT_ID);
//if (act == null)
// c.addAction(new PublishDocWebAction((NamedElement)e));
}
/*
* if (e instanceof Activity &&
* StereotypesHelper.hasStereotypeOrDerived(e,
* DocGenProfile.documentStereotype)) { act =
* manager.getActionFor(PublishDocWebAction.DEFAULT_ID); if (act ==
* null) c.addAction(new PublishDocWebAction((NamedElement)e)); }
*/
}
if (StereotypesHelper.hasStereotypeOrDerived(e, sysmlviewpoint)) {
ActionsCategory c = myCategory(manager, "DocGen", "DocGen");
NMAction act = manager.getActionFor(InstanceViewpointAction.DEFAULT_ID);
if (act == null) {
c.addAction(new InstanceViewpointAction(e));
}
}
// if ( ( e instanceof Activity &&
// StereotypesHelper.hasStereotypeOrDerived( e,
// DocGenProfile.documentStereotype ) ) ||
// StereotypesHelper.hasStereotypeOrDerived( e, sysmlview ) ) {
// ActionsCategory c = myCategory( manager, "DocGen", "DocGen" );
// NMAction act = manager.getActionFor( "DocGenComments" );
// if ( act == null ) addCommentActions( c, (NamedElement)e );
// }
// if (e instanceof Property) {
// ArrayList<Property> els = new ArrayList<Property>();
// for (Element el: es) {
// if (el instanceof Property)
// els.add((Property)el);
// }
// ActionsCategory c = myCategory(manager, "DocGen", "DocGen");
// NMAction act = manager.getActionFor(CreateRestrictedValueAction.DEFAULT_ID);
// if (act == null)
// c.addAction(new CreateRestrictedValueAction((Property) e, els));
// }
ArrayList<Property> selectedProperties = new ArrayList<Property>();
for (Element el : es) {
if (el instanceof Property) {
selectedProperties.add((Property) el);
}
}
if (!(selectedProperties.isEmpty())) {
ActionsCategory c = myCategory(manager, "DocGen", "DocGen");
NMAction act = manager.getActionFor(CreateRestrictedValueAction.DEFAULT_ID);
if (act == null) {
c.addAction(new CreateRestrictedValueAction(selectedProperties));
}
}
}
private void addDiagramActions(ActionsManager manager, DiagramPresentationElement diagram) {
if (diagram == null) {
return;
}
Element element = diagram.getActualElement();
if (element == null) {
return;
}
Element owner = element.getOwner();
if (owner == null || !(owner instanceof NamedElement)) {
return;
}
// //this add actions for syncing to docweb comments
// if (StereotypesHelper.hasStereotypeOrDerived(owner,
// DocGenProfile.documentViewStereotype)) {
// ActionsCategory category = myCategory(manager, "DocGen", "DocGen");
// NMAction action = manager.getActionFor("DocGenComments");
// if (action == null)
// addCommentActions(category, (NamedElement) owner);
// }
}
/**
* add actions related to view editor (this includes view comments)
*
* @param parent
* @param e
*/
/* private void addEditableViewActions(ActionsCategory parent, NamedElement e) {
ActionsCategory c = parent; // new ActionsCategory("EditableView",
// "Editable View");
c.addAction(new ImportViewDryAction(e));
c.addAction(new ExportViewAction(e));
c.addAction(new ExportViewHierarchyAction(e));
c.addAction(new ImportViewAction(e));
c.addAction(new SynchronizeViewAction(e));
c.addAction(new ExportViewCommentsAction(e));
c.addAction(new ImportViewCommentsAction(e));
c.addAction(new ViewViewCommentsAction(e));
ActionsCategory a = new MDActionsCategory("AdvanceEditor", "ModelLoad");
a.setNested(true);
a.addAction(new ImportViewRecursiveAction(e));
a.addAction(new ExportViewRecursiveAction(e));
a.addAction(new SynchronizeViewRecursiveAction(e));
c.addAction(a);
// c.setNested(true);
// synchronized (this) { // saw a concurrency error at some point
// parent.addAction(c);
// parent.getCategories().add(c);
// }
}
*/
/**
* Gets the specified category, creates it if necessary.
*
* @param manager
* @param id
* @param name
* @return category with given id/name
*/
private ActionsCategory myCategory(ActionsManager manager, String id, String name) {
ActionsCategory category = (ActionsCategory) manager.getActionFor(id); // getCategory(id);
if (category == null) {
category = new MDActionsCategory(id, name);
category.setNested(true);
category.setUseActionForDisable(true);
manager.addCategory(0, category);
}
return category;
}
private ActionsCategory getCategory(ActionsManager manager, String id, String name, ActionsCategory parent) {
ActionsCategory category = (ActionsCategory) manager.getActionFor(id);
if (category == null) {
//category = myCategory(manager, id, name);
category = new MDActionsCategory(id, name);
category.setNested(false); //this is to just get separators, not actual nested category
parent.addAction(category);
}
return category;
}
/**
* this should be used to add actions that're possible when user right
* clicks on a view<br/>
* it parses the single view, gets any document model that'll result in
* running script, editable table, validation rule
*
* @param parent
* @param e
*/
private boolean addViewQueryActions(ActionsManager manager, ActionsCategory parent, NamedElement e) {
if (viewQueryCalled.contains(manager)) {
return false;
}
DocumentGenerator dg = new DocumentGenerator(e, null, null);
Document dge = dg.parseDocument(true, false, false);
CollectActionsVisitor cav = new CollectActionsVisitor();
dge.accept(cav);
boolean added = false;
if (cav.getActions().size() > 0) {
for (MDAction a : cav.getActions()) {
parent.addAction(a);
}
added = true;
}
parent.setNested(true);
viewQueryCalled.clear();
viewQueryCalled.add(manager);
return added;
}
}