Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improved Support for Imported Reactors in Diagrams #1055

Merged
merged 3 commits into from
Mar 30, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion org.lflang.ui/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,11 @@ Require-Bundle: org.lflang,
org.eclipse.core.runtime,
org.eclipse.core.resources,
org.eclipse.pde.core,
org.eclipse.ui.forms
org.eclipse.ui.forms,
de.cau.cs.kieler.klighd;bundle-version="2.1.0",
de.cau.cs.kieler.klighd.ui;bundle-version="2.1.0",
de.cau.cs.kieler.klighd.ui.view;bundle-version="2.1.0",
org.eclipse.elk.core;bundle-version="0.7.1"
Import-Package: org.apache.log4j
Bundle-RequiredExecutionEnvironment: JavaSE-11
Export-Package: org.lflang.ui.internal,
Expand Down
13 changes: 13 additions & 0 deletions org.lflang.ui/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -661,4 +661,17 @@
</toolbar>
</menuContribution>
</extension>
<extension
point="de.cau.cs.kieler.klighd.ui.view.controller">
<controller
class="org.lflang.ui.diagram.LinguaFrancaDiagramUpdateController">
</controller>
</extension>
<extension
point="de.cau.cs.kieler.klighd.ui.view.editor">
<editor
controllerID="org.lflang.ui.diagram.LinguaFrancaDiagramUpdateController"
editorID="org.lflang.LF">
</editor>
</extension>
</plugin>
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/*
* KIELER - Kiel Integrated Environment for Layout Eclipse RichClient
*
* http://rtsys.informatik.uni-kiel.de/kieler
*
* Copyright 2022 by
* + Kiel University
* + Department of Computer Science
* + Real-Time and Embedded Systems Group
*
* This code is provided under the terms of the Eclipse Public License (EPL).
*/
package org.lflang.ui.diagram;

import java.util.HashMap;
import java.util.Iterator;

import org.eclipse.elk.core.util.Pair;
import org.eclipse.emf.ecore.EObject;
import org.lflang.lf.ImportedReactor;
import org.lflang.lf.Model;
import org.lflang.lf.Reactor;

import com.google.common.base.Function;
import com.google.common.collect.Iterators;

import de.cau.cs.kieler.klighd.KlighdTreeSelection;

/**
* @author als
*/
public class LinguaFrancaAdjustedKlighdTreeSelection extends KlighdTreeSelection {

/** The LF model */
private final Model model;
/** Cached map of imports */
private final HashMap<Reactor, ImportedReactor> importedReactors = new HashMap<Reactor, ImportedReactor>();

/**
* Create LF adjusted wrapper around KlighdTreeSelection.
*
* @param source copy data from original KlighdTreeSelection
*/
LinguaFrancaAdjustedKlighdTreeSelection(KlighdTreeSelection source, Model lfModel) {
super(source.getViewContext(), source.getPaths());

model = lfModel;
// Create import map
for (var imp : lfModel.getImports()) {
for (var impReactor : imp.getReactorClasses()) {
importedReactors.put(impReactor.getReactorClass(), impReactor);
}
}
}

/**
* {@inheritDoc}
*/
@Override
public Iterator<Object> sourceElementIterator() {
return Iterators.transform(iterator(),
new Function<EObject, Object>() {
public Object apply(final EObject diagramElement) {
var source = getViewContext().getSourceElement(diagramElement);
// Adjust for imported elements
if (!importedReactors.isEmpty() && source instanceof EObject) {
var sourceEObj = (EObject) source;
if (sourceEObj.eResource() != model.eResource()) { // If this source element is imported
source = findImport(sourceEObj, diagramElement);
}
}
return source;
}
});
}

/**
* {@inheritDoc}
*/
@Override
public Iterator<Pair<EObject, Object>> sourceViewPairIterator() {
return Iterators.transform(iterator(),
new Function<EObject, Pair<EObject, Object>>() {
public Pair<EObject, Object> apply(final EObject diagramElement) {
var source = getViewContext().getSourceElement(diagramElement);
// Adjust for imported elements
if (!importedReactors.isEmpty() && source instanceof EObject) {
var sourceEObj = (EObject) source;
if (sourceEObj.eResource() != model.eResource()) { // If this source element is imported
source = findImport(sourceEObj, diagramElement);
}
}
return Pair.of(diagramElement, source);
}
});
}

/**
* Find the reactor import for the given model element.
*
* @param sourceEObj the original associate
* @param diagramElement the diagram element
* @return the associated reactor import or the original associate
*/
private Object findImport(EObject sourceEObj, EObject diagramElement) {
var parent = diagramElement;
do {
var parentSource = getViewContext().getSourceElement(parent);
if (importedReactors.containsKey(parentSource)) {
return importedReactors.get(parentSource); // associate with import
}
parent = parent.eContainer();
} while (parent != null);
return sourceEObj; // keep old association
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
/*************
* Copyright (c) 2022, Kiel University.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
***************/
package org.lflang.ui.diagram;

import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.xtext.ui.editor.LanguageSpecificURIEditorOpener;
import org.eclipse.xtext.ui.editor.XtextEditor;
import org.lflang.lf.Model;
import org.lflang.ui.internal.LflangActivator;

import com.google.inject.Injector;

import de.cau.cs.kieler.klighd.KlighdTreeSelection;
import de.cau.cs.kieler.klighd.piccolo.internal.KlighdCanvas;
import de.cau.cs.kieler.klighd.piccolo.internal.events.KlighdInputManager.KlighdInputEvent;
import de.cau.cs.kieler.klighd.piccolo.internal.nodes.KlighdMainCamera;
import de.cau.cs.kieler.klighd.ui.view.DiagramView;
import de.cau.cs.kieler.klighd.ui.view.controllers.EcoreXtextSaveUpdateController;
import de.cau.cs.kieler.klighd.ui.view.controllers.XtextSelectionHighlighter;
import de.cau.cs.kieler.klighd.util.KlighdSynthesisProperties;
import edu.umd.cs.piccolo.event.PInputEvent;
import edu.umd.cs.piccolo.event.PInputEventListener;

/**
* Controller that handles the behavior between the diagram view and the Lingua Franca editor.
*
* This class is registered and associated with the LF editor via the plugin.xml, if the class name
* changes, update the plugin.xml!
*
* @author{Alexander Schulz-Rosengarten <als@informatik.uni-kiel.de>}
*/
public class LinguaFrancaDiagramUpdateController extends EcoreXtextSaveUpdateController implements PInputEventListener {

/** The xtext injector for LF */
private Injector injector;
/** Xtext utility class that is normally used for jumpt-to-declaration actions in the editor */
private LanguageSpecificURIEditorOpener uriOpener;
/** The camera the key listener is registered on */
private KlighdMainCamera camera;
/** Flag to activate code association for diagram elements which source is outside the current editor */
private boolean jumpToFile = false;

/**
* {@inheritDoc}
*/
@Override
public String getID() {
return this.getClass().getName();
}

/**
* {@inheritDoc}
*/
@Override
public void initialize(final DiagramView parentDiagramView) {
super.initialize(parentDiagramView);

// Get LF Xtext injector
injector = LflangActivator.getInstance().getInjector(LflangActivator.ORG_LFLANG_LF);
uriOpener = injector.getInstance(LanguageSpecificURIEditorOpener.class);
}

/**
* {@inheritDoc}
*/
@Override
public void onDiagramUpdate(final Object model, final KlighdSynthesisProperties properties) {
if (getDiagramView().getViewer() != null && getDiagramView().getViewer().getControl() instanceof KlighdCanvas) {
var canvas = (KlighdCanvas) getDiagramView().getViewer().getControl();
var cam = canvas.getCamera();
if (camera != cam) {
if (camera != null) {
camera.removeInputEventListener(this);
}

camera = cam;
jumpToFile = false;
camera.addInputEventListener(this);
}
}
}

/**
* {@inheritDoc}
*/
@Override
public void onDispose() {
if (camera != null) {
camera.removeInputEventListener(this);
}
}

/**
* {@inheritDoc}
*/
@Override
public void processEvent(PInputEvent event, int type) {
if (event instanceof KlighdInputEvent) {
// Check for ALT key to activate cross file association
jumpToFile = ((KlighdInputEvent) event).isAltDown();
}
}

/**
* {@inheritDoc}
*/
@Override
public void selectionChanged(final SelectionChangedEvent event) {
if (getEditor() instanceof XtextEditor) {
if (event.getSelection() instanceof KlighdTreeSelection) {
var selection = (KlighdTreeSelection) event.getSelection();

// Perform jump to potentially different editor?
if (jumpToFile && selection.size() == 1) {
var source = selection.sourceElementIterator().next();
if (source instanceof EObject) { // Source is LF model element
var sourceURI = EcoreUtil.getURI((EObject) source);
if (uriOpener != null && sourceURI != null) {
var editor = uriOpener.open(sourceURI, false);
if (editor instanceof XtextEditor) {
XtextSelectionHighlighter.highlightSelection((XtextEditor) editor, event.getSelection());
}
return; // Suppress other behavior
}
}
}

// Wrap selection in adjusted one such that elements from imported reactors are associated with the import
if (getModel() instanceof Model) {
selection = new LinguaFrancaAdjustedKlighdTreeSelection(selection, (Model) getModel());
}
XtextSelectionHighlighter.highlightSelection((XtextEditor) getEditor(), selection);
}
}
}

}