| /** |
| ******************************************************************************** |
| * 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); |
| } |
| } |
| } |