blob: 80efcad5b28faeaa9e5180ce6b90680e2f9862a1 [file] [log] [blame]
/**
********************************************************************************
* Copyright (c) 2017-2020 Robert Bosch GmbH.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Robert Bosch GmbH - initial API and implementation
********************************************************************************
*/
package org.eclipse.app4mc.emf.viewer.plantuml.views;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Deque;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
import javax.inject.Named;
import org.eclipse.app4mc.emf.viewer.plantuml.handlers.AbstractPlantUMLHandler;
import org.eclipse.app4mc.emf.viewer.plantuml.handlers.EClassAllReferencesHandler;
import org.eclipse.app4mc.emf.viewer.plantuml.handlers.EClassContentsAndHierarchyHandler;
import org.eclipse.app4mc.emf.viewer.plantuml.handlers.EClassContentsFromReferenceHandler;
import org.eclipse.app4mc.emf.viewer.plantuml.handlers.EClassHierarchyHandler;
import org.eclipse.app4mc.emf.viewer.plantuml.handlers.EObjectRefsHandler;
import org.eclipse.app4mc.emf.viewer.plantuml.handlers.ObjectContentHandler;
import org.eclipse.app4mc.emf.viewer.plantuml.preferences.PreferenceConstants;
import org.eclipse.app4mc.emf.viewer.plantuml.utils.ExecutionCategory;
import org.eclipse.app4mc.emf.viewer.plantuml.utils.ExecutionUtil;
import org.eclipse.e4.core.di.annotations.Optional;
import org.eclipse.e4.core.di.extensions.Preference;
import org.eclipse.e4.core.services.events.IEventBroker;
import org.eclipse.e4.ui.di.Focus;
import org.eclipse.e4.ui.di.PersistState;
import org.eclipse.e4.ui.di.UISynchronize;
import org.eclipse.e4.ui.model.application.ui.basic.MPart;
import org.eclipse.e4.ui.model.application.ui.menu.MDirectToolItem;
import org.eclipse.e4.ui.model.application.ui.menu.MToolBarElement;
import org.eclipse.e4.ui.services.IServiceConstants;
import org.eclipse.e4.ui.workbench.modeling.EPartService;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.browser.Browser;
import org.eclipse.swt.browser.LocationAdapter;
import org.eclipse.swt.browser.LocationEvent;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Shell;
import net.sourceforge.plantuml.eclipse.utils.PlantumlConstants;
@SuppressWarnings("restriction")
public class DiagramView {
/**
* The ID of the view.
*/
public static final String ID = "org.eclipse.app4mc.emf.viewer.plantuml.views.DiagramView";
private static final String LINKED_KEY = "LINKED_WITH_EDITOR";
private boolean linkedWithEditor = false;
private Browser browser;
final HashMap<String, Object> idObjectsMap = new HashMap<>();
@Inject
Shell shell;
@Inject
UISynchronize sync;
@Inject
EPartService partService;
@Inject
IEventBroker broker;
@Inject
@Optional
@Preference(
nodePath = "net.sourceforge.plantuml.eclipse",
value = PlantumlConstants.GRAPHVIZ_PATH)
String dotPath;
@Inject
@Optional
@Preference(
nodePath = "org.eclipse.app4mc.emf.viewer.plantuml",
value = PreferenceConstants.P_FOLDER_PATH)
String genFolderPath;
private Deque<String> history = new ArrayDeque<>();
/**
* This is a callback that will allow us to create the viewer and initialize it.
*/
@PostConstruct
public void createPartControl(Composite parent, MPart part) {
Map<String, String> state = part.getPersistedState();
String value = state.get(LINKED_KEY);
this.linkedWithEditor = value != null && Boolean.valueOf(value);
this.browser = new Browser(parent, SWT.None);
this.browser.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
this.browser.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.character == '\b' && !history.isEmpty()) {
String previous = history.pop();
if (previous != null) {
// extract ID from URL
int lastIndexOf = previous.lastIndexOf('/');
String id = "";
if (lastIndexOf != -1) {
id = previous.substring(lastIndexOf + 1, previous.lastIndexOf('.'));
}
// get object of the previous view
Object eObject = idObjectsMap.get(id);
if (eObject != null && isLinkedWithEditor()) {
if (eObject instanceof EClass) {
updateDiagram((eObject), null);
} else if (eObject instanceof EObject) {
updateDiagram(((EObject) eObject).eClass(), (EObject) eObject);
}
selectInEditor(eObject);
e.doit = false;
}
}
}
}
});
this.browser.addLocationListener(new LocationAdapter() {
@Override
public void changing(LocationEvent event) {
String location = event.location;
if (!location.endsWith(".svg")) {
int lastIndexOf = location.lastIndexOf('/');
String id = "";
if (lastIndexOf != -1) {
id = location.substring(lastIndexOf + 1, location.length());
}
Object eObject = idObjectsMap.get(id);
if (eObject != null) {
if (eObject instanceof EClass) {
updateDiagram((eObject), null);
} else if (eObject instanceof EObject) {
updateDiagram(((EObject) eObject).eClass(), (EObject) eObject);
} else if (eObject instanceof EList<?>) {
// we pass the eObject which is a EList so the ObjectContentHandler can handle the EList special case
updateDiagram(eObject, null);
}
if (isLinkedWithEditor()) {
selectInEditor(eObject);
}
}
event.doit = false;
}
}
});
}
@Inject
@Optional
void handleSelection(@Named(IServiceConstants.ACTIVE_SELECTION) IStructuredSelection selection) {
if (isLinkedWithEditor() && selection != null && !selection.isEmpty()) {
Object firstElement = selection.getFirstElement();
if (firstElement instanceof EObject) {
updateDiagram(((EObject)firstElement).eClass(), (EObject)firstElement);
}
}
}
private void updateDiagram(Object eClass, EObject eObject) {
Object selected = eClass;
/*
* Regeneration of SVG Buffer for populating content specific to the selection
*/
AbstractPlantUMLHandler handler = null;
if (ExecutionUtil.isExecuting(ExecutionCategory.eClassHierarchy)) {
handler = new EClassHierarchyHandler();
} else if (ExecutionUtil.isExecuting(ExecutionCategory.eClassContentsAndHierarchy)) {
handler = new EClassContentsAndHierarchyHandler();
} else if (ExecutionUtil.isExecuting(ExecutionCategory.eClassAllReferences)) {
handler = new EClassAllReferencesHandler();
} else if (ExecutionUtil.isExecuting(ExecutionCategory.eClassContentsFromReference)) {
handler = new EClassContentsFromReferenceHandler();
} else if (ExecutionUtil.isExecuting(ExecutionCategory.eObjectRefs)) {
handler = new EObjectRefsHandler();
if (eObject != null) {
// if the eObject is not null we use this for the visualization, otherwise maybe a collection was selected
selected = eObject;
}
} else {
/*- EClassContents and EObjectRefs behaviour is included in ObjectContentBuilder java class */
handler = new ObjectContentHandler();
if (eObject != null) {
// if the eObject is not null we use this for the visualization, otherwise maybe a collection was selected
selected = eObject;
}
}
handler.execute(shell, sync, dotPath, genFolderPath, partService, selected, eObject);
}
/**
* This method is used to select specified AMALTHEA element in the Sphinx editor
*
* @param element
*/
private void selectInEditor(Object element) {
HashMap<String, Object> data = new HashMap<>();
data.put("modelElements", Arrays.asList(element));
broker.send("org/eclipse/app4mc/amalthea/editor/SELECT", data);
}
@Focus
public void setFocus(MPart activePart) {
for (MToolBarElement element : activePart.getToolbar().getChildren()) {
if (element.getElementId().equals("org.eclipse.app4mc.emf.viewer.plantuml.directtoolitem.select")) {
MDirectToolItem toolItem = (MDirectToolItem) element;
toolItem.setSelected(linkedWithEditor);
}
}
}
@PersistState
public void persistState(MPart part) {
Map<String, String> state = part.getPersistedState();
state.put(LINKED_KEY, Boolean.toString(linkedWithEditor));
}
public boolean isLinkedWithEditor() {
return this.linkedWithEditor;
}
public void setLinkedWithEditor(boolean linked) {
this.linkedWithEditor = linked;
}
public void showDiagram(String fileNamePrefix, Object element, String svgPath, Map<String, Object> id2ObjectsMap) {
if (this.browser != null && !this.browser.isDisposed()) {
if (element != null) {
this.idObjectsMap.put(fileNamePrefix, element);
}
this.idObjectsMap.putAll(id2ObjectsMap);
this.history.add(browser.getUrl());
this.browser.setUrl(svgPath);
}
}
}