diff --git a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/javafx/control/treetableview/.ratings b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/javafx/control/treetableview/.ratings index 74ecded403f0ef2a8aa97292c26a5a964c6c4788..a9e7b2649e4e8c1ba99a18ed8aa4009f5ea1b998 100644 --- a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/javafx/control/treetableview/.ratings +++ b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/javafx/control/treetableview/.ratings @@ -1 +1 @@ -HierarchicModelElementTreeViewer.java 8662b201c3a459f3ebe3620970652f7a3f90270e GREEN +ModelElementTreeViewer.java 8a555fc12eb6c0531f6334fdc61e9abf52ff784d YELLOW diff --git a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/javafx/control/treetableview/HierarchicModelElementTreeViewer.java b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/javafx/control/treetableview/ModelElementTreeViewer.java similarity index 65% rename from org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/javafx/control/treetableview/HierarchicModelElementTreeViewer.java rename to org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/javafx/control/treetableview/ModelElementTreeViewer.java index 8662b201c3a459f3ebe3620970652f7a3f90270e..8a555fc12eb6c0531f6334fdc61e9abf52ff784d 100644 --- a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/javafx/control/treetableview/HierarchicModelElementTreeViewer.java +++ b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/javafx/control/treetableview/ModelElementTreeViewer.java @@ -15,35 +15,54 @@ +--------------------------------------------------------------------------*/ package org.fortiss.tooling.base.ui.javafx.control.treetableview; -import javafx.embed.swt.SWTFXUtils; -import javafx.scene.Node; -import javafx.scene.control.ContextMenu; -import javafx.scene.control.TreeView; -import javafx.scene.image.Image; -import javafx.scene.image.ImageView; +import static javafx.embed.swt.SWTFXUtils.toFXImage; + +import java.util.ArrayList; +import java.util.List; import org.fortiss.tooling.base.model.element.IHierarchicElement; +import org.fortiss.tooling.base.model.element.IModelElement; +import org.fortiss.tooling.common.ui.javafx.AF3FXViewPart; import org.fortiss.tooling.common.ui.javafx.control.treetableview.DynamicTreeContentProviderBase; import org.fortiss.tooling.common.ui.javafx.control.treetableview.DynamicTreeUIProviderBase; import org.fortiss.tooling.common.ui.javafx.control.treetableview.DynamicTreeViewer; import org.fortiss.tooling.kernel.model.INamedCommentedElement; import org.fortiss.tooling.kernel.ui.service.IModelElementHandlerService; +import javafx.scene.Node; +import javafx.scene.control.ContextMenu; +import javafx.scene.control.MenuItem; +import javafx.scene.control.TreeView; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; + /** + * This class is a support fragment for creating tree views in JavaFX Controls. It provides the view + * counter part of a {@link DynamicTreeContentProviderBase}. The content provider is responsible + * for selecting elements from a base model and ordering them acc. to a custom defined structure. + * These two fragments have to be combined in a JavaFX controller inheriting from the base class + * {@link AF3FXViewPart}. + * <p> * TreeViewer that constructs a DynamicTreeViewer whose UI provider uses the methods and kernel * services for {@link IHierarchicElement}s and {@link INamedCommentedElement}s. - * The Viewer uses a composition principle where a client must provide {@link TreeView} (JavaFX), a - * root element (AF3), and a content provider that selects the elements to be displayed. + * The Viewer uses a composition principle where a client must provide a {@link TreeView} (JavaFX), + * a root element (AF3), and a content provider that selects the elements to be displayed. * * @author diewald */ -public class HierarchicModelElementTreeViewer<T extends IHierarchicElement & INamedCommentedElement> { +public class ModelElementTreeViewer<T extends IModelElement & INamedCommentedElement> { /** References the constructed {@link DynamicTreeViewer} "controller". */ protected DynamicTreeViewer<T> dynTreeViewer; + /** + * List of context menu entries that supplied by clients (see + * {@link #addContextMenuEntry(MenuItem)}). + */ + protected List<MenuItem> contextMenuEntries = new ArrayList<>(); + /** Constructor. */ - public HierarchicModelElementTreeViewer(TreeView<T> treeView, T modelRoot, + public ModelElementTreeViewer(TreeView<T> treeView, T modelRoot, DynamicTreeContentProviderBase<T> contentProvider) { DynamicTreeUIProviderBase<T> uiProvider = createContentUIProvider(); dynTreeViewer = @@ -54,8 +73,6 @@ public class HierarchicModelElementTreeViewer<T extends IHierarchicElement & INa * Creates a default UI provider for {@link DynamicTreeViewer}s that present * {@link IHierarchicElement}s. It also uses the {@link IModelElementHandlerService} for a nicer * visual appearance. - * <p> - * Externalize this method into a class when subclassing seems appropriate. * * @return The constructed UI Provider. */ @@ -74,7 +91,7 @@ public class HierarchicModelElementTreeViewer<T extends IHierarchicElement & INa org.eclipse.swt.graphics.Image icon = IModelElementHandlerService.getInstance().getIcon(element); if(icon != null) { - Image fxImage = SWTFXUtils.toFXImage(icon.getImageData(), null); + Image fxImage = toFXImage(icon.getImageData(), null); return new ImageView(fxImage); } return null; @@ -84,18 +101,26 @@ public class HierarchicModelElementTreeViewer<T extends IHierarchicElement & INa @Override public ContextMenu createContextMenu(T element) { ContextMenu menu = new ContextMenu(); - // TODO (3209): Add sensible default operations for AF3 elements, if they are - // needed. Otherwise, use a list provided by subclasses that defines the menu items - // and their order. The code below is taken from SystemFOCUS and shall serve as a - // reference. - // MenuItem item = new MenuItem("Delete '" + getLabel(element) + "'."); - // item.setOnAction(event -> { - // new DeleteOperation(element).delete(); - // viewer.update(); - // }); - // menu.getItems().add(item); + contextMenuEntries.forEach(entry -> menu.getItems().add(entry)); return menu; } }; } + + /** + * Method to define context {@link MenuItem}s for items of the underlying content. The given + * function may use the selected element (given as a parameter to the function) to define + * dynamic behavior. + * + * @param menuItem + * {@link MenuItem} and its logic to be added to the context menu. + */ + public void addContextMenuEntry(MenuItem menuItem) { + contextMenuEntries.add(menuItem); + } + + /** Update the internal viewer. */ + public void update() { + dynTreeViewer.update(); + } } diff --git a/org.fortiss.tooling.common.ui/META-INF/MANIFEST.MF b/org.fortiss.tooling.common.ui/META-INF/MANIFEST.MF index 4446d79643ee60e872eeb2569df18e86b8439526..eae4c43bffbbe5c25160fe02960debfbd6b0c7ba 100644 --- a/org.fortiss.tooling.common.ui/META-INF/MANIFEST.MF +++ b/org.fortiss.tooling.common.ui/META-INF/MANIFEST.MF @@ -75,7 +75,8 @@ Export-Package: org.conqat.ide.commons.gef, org.eclipse.wb.swt.widgets.baseline, org.fortiss.tooling.common.ui, org.fortiss.tooling.common.ui.javafx, - org.fortiss.tooling.common.ui.javafx.control.treetableview + org.fortiss.tooling.common.ui.javafx.control.treetableview, + org.fortiss.tooling.common.ui.javafx.util Bundle-Vendor: fortiss GmbH Bundle-Activator: org.fortiss.tooling.common.ui.ToolingCommonUIActivator Bundle-ActivationPolicy: lazy diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/.ratings b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/.ratings index 11e7ae0c9873a9b3038cd5b7ca314736e288dedd..cda5008e78c311265a3863a67023ec9e0c7a1e28 100644 --- a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/.ratings +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/.ratings @@ -1 +1 @@ -AF3FXViewPart.java f1b4c05a7a22cd7a0a2524600204a0ad6365c419 GREEN +AF3FXViewPart.java 1af5c5fba174c2f81ebdde4e90552a8501f1d86e GREEN diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/AF3FXViewPart.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/AF3FXViewPart.java index f1b4c05a7a22cd7a0a2524600204a0ad6365c419..1af5c5fba174c2f81ebdde4e90552a8501f1d86e 100644 --- a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/AF3FXViewPart.java +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/AF3FXViewPart.java @@ -18,31 +18,29 @@ package org.fortiss.tooling.common.ui.javafx; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.net.URL; +import java.util.ResourceBundle; + +import org.apache.commons.lang3.SystemUtils; +import org.eclipse.fx.ui.workbench3.FXViewPart; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.ui.part.ViewPart; import javafx.application.Platform; import javafx.fxml.FXMLLoader; +import javafx.fxml.Initializable; import javafx.scene.Node; import javafx.scene.Scene; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.Pane; -import org.apache.commons.lang3.SystemUtils; -import org.eclipse.fx.ui.workbench3.FXViewPart; -import org.eclipse.swt.SWT; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.ui.part.ViewPart; - /** * Base class to create eclipse (SWT-based) views using JavaFX GUI elements. This class requires * FXML file that defines the GUI elements of the view. * * @author diewald */ -/** - * This class will be used for the migration to JavaFX, which is work-in-progress. Hence, mark the - * class for non-removal. - */ -public abstract class AF3FXViewPart extends FXViewPart { // NO_UCD +public abstract class AF3FXViewPart extends FXViewPart implements Initializable { /** For later use, if we need to define different styles. */ private String cssLocation; @@ -124,6 +122,7 @@ public abstract class AF3FXViewPart extends FXViewPart { // NO_UCD protected Scene createFxScene() { FXMLLoader loader = new FXMLLoader(); loader.setClassLoader(viewerClass.getClassLoader()); + loader.setController(this); if(fXMLLocation != null) { loader.setLocation(viewerClass.getResource(fXMLLocation)); try { @@ -135,7 +134,8 @@ public abstract class AF3FXViewPart extends FXViewPart { // NO_UCD } else { root = new AnchorPane(); try { - viewerClass.getMethod("initialize").invoke(this); + viewerClass.getMethod("initialize", URL.class, ResourceBundle.class).invoke(this, + null, null); } catch(IllegalAccessException | IllegalArgumentException | InvocationTargetException | SecurityException e) { throw new RuntimeException("The initialize method of the View " + @@ -164,17 +164,6 @@ public abstract class AF3FXViewPart extends FXViewPart { // NO_UCD // Not needed. } - /** - * Init method that is called after the JavaFX toolkit has been launched. The initialize method - * is needed for single-node/view/control {@link ViewPart}s and for controls in a FXML-based - * view that require an initial configuration. If no initial configuration is required place a - * stub. - * <p> - * For FXML, the initialize method is called via the JavaFX framework. Otherwise, it is called - * via reflection from this class. - */ - protected abstract void initialize(); - /** * Adds a single {@link Node} to this view. It embeds the given {@link Node} to the * {@link AnchorPane} of this view and stretches it to the borders of the view. diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/control/treetableview/.ratings b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/control/treetableview/.ratings index ae65b55ceb5b989a058739bfe8f94c7bf256987b..1568ae343836e6326b053543824c1baf46dc626c 100644 --- a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/control/treetableview/.ratings +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/control/treetableview/.ratings @@ -3,6 +3,6 @@ DynamicTreeItem.java afc105cf5acf3d2506d89e0892555100c234ce5b GREEN DynamicTreeTableUIProviderBase.java fd9fce19a65eb1006ceacb0d869bbe90a8c578b3 GREEN DynamicTreeTableViewer.java e474f3a890fd6525db7de8e299d7fbe67f932a15 GREEN DynamicTreeUIProviderBase.java 56fe4df4577b35f1e5e6e4c4be189b706c852d52 GREEN -DynamicTreeViewer.java d5b9f87862d9c42327c46bce02fb34d64673d413 GREEN +DynamicTreeViewer.java ffead9e76286fb7a17723cd201fdc187187a737f GREEN DynamicTreeViewerBase.java 47124c847de322a0ae26eb7a114f85ce4bd02d7e GREEN IDoubleClickHandler.java 447f7769dead9a106b3ea3139ef0da51eb0b9a89 GREEN diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/control/treetableview/DynamicTreeViewer.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/control/treetableview/DynamicTreeViewer.java index d5b9f87862d9c42327c46bce02fb34d64673d413..ffead9e76286fb7a17723cd201fdc187187a737f 100644 --- a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/control/treetableview/DynamicTreeViewer.java +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/control/treetableview/DynamicTreeViewer.java @@ -34,6 +34,8 @@ import javafx.scene.input.MouseEvent; public final class DynamicTreeViewer<T> extends DynamicTreeViewerBase<T> { /** The {@link TreeView} control to be managed. */ private final TreeView<T> view; + /** {@link TreeItem} constructed for the root object. */ + private final DynamicTreeItem<T> rootItem; /** The UI provider implementation. */ private final DynamicTreeUIProviderBase<T> uiProvider; /** The selection change listener. */ @@ -49,7 +51,7 @@ public final class DynamicTreeViewer<T> extends DynamicTreeViewerBase<T> { this.uiProvider = uiProvider; // construct view this.view = view; - DynamicTreeItem<T> rootItem = new DynamicTreeItem<T>(root, this); + rootItem = new DynamicTreeItem<T>(root, this); view.setRoot(rootItem); view.setShowRoot(showRoot); configureCellFactory(); @@ -88,6 +90,11 @@ public final class DynamicTreeViewer<T> extends DynamicTreeViewerBase<T> { view.getSelectionModel().select(row); } + /** Searches the {@link DynamicTreeItem} for the given value from the root. */ + public DynamicTreeItem<T> findItem(T value) { + return findItem(rootItem, value); + } + /** Searches the {@link DynamicTreeItem} for the given value. */ public DynamicTreeItem<T> findItem(DynamicTreeItem<T> item, Object value) { if(item == null || value == null) { @@ -142,6 +149,14 @@ public final class DynamicTreeViewer<T> extends DynamicTreeViewerBase<T> { }); } + /** Expands the tree to the given item. */ + public void expandItem(TreeItem<T> item) { + while(item.getParent() != null) { + item = item.getParent(); + item.setExpanded(true); + } + } + /** Expands items up to the given level. */ private void expandItem(TreeItem<T> parentItem, int revealLevel) { if(revealLevel <= 0) { @@ -165,7 +180,7 @@ public final class DynamicTreeViewer<T> extends DynamicTreeViewerBase<T> { } /** - * Customized event dispatcher to fix double-click behavior. Thiss prevents opening/closing of + * Customized event dispatcher to fix double-click behavior. This prevents opening/closing of * tree items with double click listeners assigned. */ private class DoubleClickEventDispatcher implements EventDispatcher { diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/util/.ratings b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/util/.ratings new file mode 100644 index 0000000000000000000000000000000000000000..cd9b77b00f1d5a5ab51c9a5d37aef27a53e00ed2 --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/util/.ratings @@ -0,0 +1 @@ +SceneGraphUtils.java f54304c2eb604934de9afdf9d2a8ca88a762398a GREEN diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/util/SceneGraphUtils.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/util/SceneGraphUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..f54304c2eb604934de9afdf9d2a8ca88a762398a --- /dev/null +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/util/SceneGraphUtils.java @@ -0,0 +1,175 @@ +/*-------------------------------------------------------------------------+ +| Copyright 2019 fortiss GmbH | +| | +| Licensed under the Apache License, Version 2.0 (the "License"); | +| you may not use this file except in compliance with the License. | +| You may obtain a copy of the License at | +| | +| http://www.apache.org/licenses/LICENSE-2.0 | +| | +| Unless required by applicable law or agreed to in writing, software | +| distributed under the License is distributed on an "AS IS" BASIS, | +| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | +| See the License for the specific language governing permissions and | +| limitations under the License. | ++--------------------------------------------------------------------------*/ +package org.fortiss.tooling.common.ui.javafx.util; + +import java.util.LinkedList; +import java.util.Queue; +import java.util.function.Predicate; + +import javafx.scene.Node; +import javafx.scene.Parent; +import javafx.scene.Scene; + +/** + * Utility methods for {@link Scene} graph. + * + * @author hoelzl + * @author diewald + */ +public final class SceneGraphUtils { + + /** Prevent instantiation. */ + private SceneGraphUtils() { + // Nothing to do + } + + /** + * Searches the scene graph hierarchy for the nearest parent node of the given class. + * + * @param start + * the start node + * @param clazz + * the target class of the node to be found + * @return the nearest parent node + */ + @SuppressWarnings("unchecked") + public static <T extends Node> T findParentNode(Parent start, Class<T> clazz) { + return (T)findParentNode(start, t -> clazz.isInstance(t)); + } + + /** + * Searches the scene graph hierarchy for the nearest child node with the given id. + * + * @param start + * the start node + * @param id + * string identified (field "fx:id" in FXML files). + * @return the nearest child node + */ + public static Node findParentById(Parent start, String id) { + return findParentNode(start, t -> t.getId().equals(id)); + } + + /** + * Searches the scene graph hierarchy for the nearest parent node with the given id and type. + * + * @param start + * the start node + * @param id + * string identified (field "fx:id" in FXML files). + * @param type + * the target class of the node to be found + * @return the nearest child node + */ + @SuppressWarnings("unchecked") + public static <T extends Node> T findParentByIdType(Parent start, String id, Class<T> type) { + return (T)findChildNode(start, + t -> (type.isAssignableFrom(type)) && (t.getId().equals(id))); + } + + /** + * Searches the scene graph hierarchy for the nearest parent node fulfilling the given + * {@link Predicate}. + * + * @param start + * the start node + * @param pred + * predicate evaluated during the parent walk + * @return the nearest parent node + */ + public static Node findParentNode(Parent start, Predicate<Node> pred) { + while(start != null) { + start = start.getParent(); + if(pred.test(start)) { + return start; + } + } + return null; + } + + /** + * Searches the scene graph hierarchy for the nearest child node of the given class. + * + * @param start + * the start node + * @param clazz + * the target class of the node to be found + * @return the nearest child node + */ + @SuppressWarnings("unchecked") + public static <T extends Node> T findChildNode(Parent start, Class<T> clazz) { + return (T)findChildNode(start, t -> clazz.isInstance(t)); + } + + /** + * Searches the scene graph hierarchy for the nearest child node with the given id. + * + * @param start + * the start node + * @param id + * string identified (field "fx:id" in FXML files). + * @return the nearest child node + */ + public static Node findChildById(Parent start, String id) { + return findChildNode(start, t -> t.getId().equals(id)); + } + + /** + * Searches the scene graph hierarchy for the nearest child node with the given id and type. + * + * @param start + * the start node + * @param id + * string identified (field "fx:id" in FXML files). + * @param type + * the target class of the node to be found + * @return the nearest child node + */ + @SuppressWarnings("unchecked") + public static <T extends Node> T findChildByIdType(Parent start, String id, Class<T> type) { + // @CodeFormatterOff + return (T)findChildNode(start, + t -> (t.getId() != null + && (t.getId().equals(id)) + && type.isAssignableFrom(t.getClass()))); + // @CodeFormatterOn + } + + /** + * Searches the scene graph hierarchy for the nearest child node of the given fulfilling the + * given {@link Predicate}. + * + * @param start + * the start node + * @param pred + * predicate evaluated during the child walk + * @return the nearest parent node + */ + public static Node findChildNode(Parent start, Predicate<Node> pred) { + Queue<Node> children = new LinkedList<>(); + children.addAll(start.getChildrenUnmodifiable()); + while(!children.isEmpty()) { + Node child = children.poll(); + if(pred.test(child)) { + return child; + } + if(child instanceof Parent) { + children.addAll(((Parent)child).getChildrenUnmodifiable()); + } + } + return null; + } +} diff --git a/org.fortiss.tooling.kernel/META-INF/MANIFEST.MF b/org.fortiss.tooling.kernel/META-INF/MANIFEST.MF index ff4058a1982894c047f24d3b3a5220958bc974a4..82d3b769cb3573daa9d2f94ae13ac28c27229dd6 100644 --- a/org.fortiss.tooling.kernel/META-INF/MANIFEST.MF +++ b/org.fortiss.tooling.kernel/META-INF/MANIFEST.MF @@ -1,44 +1,45 @@ -Manifest-Version: 1.0 -Bundle-ManifestVersion: 2 -Bundle-Name: %pluginName -Bundle-SymbolicName: org.fortiss.tooling.kernel;singleton:=true -Bundle-Version: 2.14.0.qualifier -Bundle-ClassPath: . -Bundle-Activator: org.fortiss.tooling.kernel.ToolingKernelActivator -Bundle-Vendor: %providerName -Bundle-Localization: plugin -Require-Bundle: org.fortiss.tooling.common;visibility:=reexport;bundle-version="2.14.0", - org.eclipse.core.runtime, - org.eclipse.emf.ecore;visibility:=reexport;bundle-version="2.7.0", - org.eclipse.emf.transaction;visibility:=reexport;bundle-version="1.4.0", - org.eclipse.emf.ecore.xmi;visibility:=reexport;bundle-version="2.7.0" -Bundle-RequiredExecutionEnvironment: JavaSE-11 -Bundle-ActivationPolicy: lazy -Export-Package: org.fortiss.tooling.kernel;uses:="org.eclipse.core.runtime, - org.osgi.framework", - org.fortiss.tooling.kernel.constraint, - org.fortiss.tooling.kernel.extension, - org.fortiss.tooling.kernel.extension.base, - org.fortiss.tooling.kernel.extension.data, - org.fortiss.tooling.kernel.extension.exception, - org.fortiss.tooling.kernel.introspection, - org.fortiss.tooling.kernel.introspection.items, - org.fortiss.tooling.kernel.model;uses:=org.eclipse.emf.ecore, - org.fortiss.tooling.kernel.model.constraints, - org.fortiss.tooling.kernel.model.constraints.impl, - org.fortiss.tooling.kernel.model.constraints.util, - org.fortiss.tooling.kernel.model.impl;uses:=org.fortiss.tooling.kernel.model, - org.fortiss.tooling.kernel.model.util;uses:="org.eclipse.emf.ecore, - org.fortiss.tooling.kernel.model, - org.eclipse.emf.common.notify.impl, - org.eclipse.emf.common.notify", - org.fortiss.tooling.kernel.service;uses:="org.eclipse.core.runtime, - org.eclipse.emf.ecore, - org.fortiss.tooling.kernel.interfaces, - org.conqat.lib.commons.collections", - org.fortiss.tooling.kernel.service.base, - org.fortiss.tooling.kernel.service.listener, - org.fortiss.tooling.kernel.service.types, - org.fortiss.tooling.kernel.utils;uses:="org.eclipse.emf.ecore, - org.eclipse.core.resources" -Automatic-Module-Name: org.fortiss.tooling.kernel +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: %pluginName +Bundle-SymbolicName: org.fortiss.tooling.kernel;singleton:=true +Bundle-Version: 2.14.0.qualifier +Bundle-ClassPath: . +Bundle-Activator: org.fortiss.tooling.kernel.ToolingKernelActivator +Bundle-Vendor: %providerName +Bundle-Localization: plugin +Require-Bundle: org.fortiss.tooling.common;visibility:=reexport;bundle-version="2.14.0", + org.eclipse.core.runtime, + org.eclipse.emf.ecore;visibility:=reexport;bundle-version="2.7.0", + org.eclipse.emf.transaction;visibility:=reexport;bundle-version="1.4.0", + org.eclipse.emf.ecore.xmi;visibility:=reexport;bundle-version="2.7.0" +Bundle-RequiredExecutionEnvironment: JavaSE-11 +Bundle-ActivationPolicy: lazy +Export-Package: org.fortiss.tooling.kernel;uses:="org.eclipse.core.runtime, + org.osgi.framework", + org.fortiss.tooling.kernel.constraint, + org.fortiss.tooling.kernel.extension, + org.fortiss.tooling.kernel.extension.base, + org.fortiss.tooling.kernel.extension.data, + org.fortiss.tooling.kernel.extension.exception, + org.fortiss.tooling.kernel.introspection, + org.fortiss.tooling.kernel.introspection.items, + org.fortiss.tooling.kernel.model;uses:=org.eclipse.emf.ecore, + org.fortiss.tooling.kernel.model.constraints, + org.fortiss.tooling.kernel.model.constraints.impl, + org.fortiss.tooling.kernel.model.constraints.util, + org.fortiss.tooling.kernel.model.impl;uses:=org.fortiss.tooling.kernel.model, + org.fortiss.tooling.kernel.model.util;uses:="org.eclipse.emf.ecore, + org.fortiss.tooling.kernel.model, + org.eclipse.emf.common.notify.impl, + org.eclipse.emf.common.notify", + org.fortiss.tooling.kernel.service;uses:="org.eclipse.core.runtime, + org.eclipse.emf.ecore, + org.fortiss.tooling.kernel.interfaces, + org.conqat.lib.commons.collections", + org.fortiss.tooling.kernel.service.base, + org.fortiss.tooling.kernel.service.listener, + org.fortiss.tooling.kernel.service.types, + org.fortiss.tooling.kernel.utils;uses:="org.eclipse.emf.ecore, + org.eclipse.core.resources" +Automatic-Module-Name: org.fortiss.tooling.kernel +Import-Package: com.google.common.collect diff --git a/org.fortiss.tooling.kernel/src/org/fortiss/tooling/kernel/utils/.ratings b/org.fortiss.tooling.kernel/src/org/fortiss/tooling/kernel/utils/.ratings index a61917f85ff8354586341551fce429baa987647d..5d3e20c3cc769090598850cc48992c99e249db2f 100644 --- a/org.fortiss.tooling.kernel/src/org/fortiss/tooling/kernel/utils/.ratings +++ b/org.fortiss.tooling.kernel/src/org/fortiss/tooling/kernel/utils/.ratings @@ -2,7 +2,7 @@ CompositionUtils.java 34c0a191bd0fb4176c94b4d61abb5c88a679d5e8 GREEN ConstraintsUtils.java 0f8be020f2ca4bb08931c32452163c04a28e30ce GREEN EMFResourceUtils.java 979d0e1f4f66a2b3e715d2da0ebef6493f547fd7 GREEN EcoreSerializerBase.java 0a0c2969d793d2e68094c55c8f7b0a662ef6e5d5 GREEN -EcoreUtils.java 3e2e2193312cd4ce6fd0c7ea2beb7daae428d2ed GREEN +EcoreUtils.java accd567a0dcd1447d6f3e112f19fe8d53862d0cb GREEN ExtensionPointUtils.java 7ce63242b49eb9a7cd4eaadd223f5ebce1dfd75b GREEN HierarchicalNameComparator.java 6face1b673126701a0721af48ead2f9766c17d46 GREEN IdentifierUtils.java fff43dc4e84cdd89c3ece4f5d9d89aec4b0749c2 GREEN diff --git a/org.fortiss.tooling.kernel/src/org/fortiss/tooling/kernel/utils/EcoreUtils.java b/org.fortiss.tooling.kernel/src/org/fortiss/tooling/kernel/utils/EcoreUtils.java index 3e2e2193312cd4ce6fd0c7ea2beb7daae428d2ed..accd567a0dcd1447d6f3e112f19fe8d53862d0cb 100644 --- a/org.fortiss.tooling.kernel/src/org/fortiss/tooling/kernel/utils/EcoreUtils.java +++ b/org.fortiss.tooling.kernel/src/org/fortiss/tooling/kernel/utils/EcoreUtils.java @@ -15,12 +15,14 @@ +--------------------------------------------------------------------------*/ package org.fortiss.tooling.kernel.utils; +import static java.util.stream.Collectors.toList; import static org.eclipse.emf.common.notify.Notification.EVENT_TYPE_COUNT; import static org.eclipse.emf.ecore.util.EcoreUtil.getAllContents; import static org.eclipse.emf.ecore.util.EcoreUtil.getRootContainer; import static org.eclipse.emf.ecore.util.EcoreUtil.replace; import static org.eclipse.emf.ecore.util.EcoreUtil.UsageCrossReferencer.find; +import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; @@ -50,6 +52,9 @@ import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.emf.ecore.util.EcoreUtil.Copier; +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; + /** * Utility methods for dealing with <code>Ecore</code> models. These methods should be used to * define EMF operations that make the models easier to use. @@ -395,6 +400,17 @@ public class EcoreUtils { return t; } + /** + * Returns a self-contained copy of the given collection of {@link EObject}s. + * + * @return Self-contained copy of the given {@link EObject}s. + */ + @SuppressWarnings("unchecked") + public static <T extends EObject> Collection<T> copy(Collection<T> eObjects) { + Map<EObject, EObject> copyMap = copyToRefMap(eObjects); + return (Collection<T>)eObjects.stream().map(orig -> copyMap.get(orig)).collect(toList()); + } + /** * Creates a self-contained copy of the given {@link EObject}. * @@ -409,6 +425,70 @@ public class EcoreUtils { return copier; } + /** + * Creates a self-contained copy of the given collection of {@link EObject}s. References + * between elements of the given {@link EObject}s are updated to point to their copies. + * + * @param originalEObjects + * collection of {@link EObject}s to copy. + * @return The method returns a {@link Map} that relates each original sub-element (including + * the given {@link EObject}) to the corresponding copy. + */ + public static Map<EObject, EObject> + copyToRefMap(Collection<? extends EObject> originalEObjects) { + BiMap<EObject, EObject> copyRefMap = HashBiMap.create(); + + // First, create all copies such that bi-references between the input object can be + // resolved in a second step: This step requires the existence of all copied elements. + for(EObject originalElement : originalEObjects) { + Map<EObject, EObject> currentCopyMap = copyToRefMap(originalElement); + copyRefMap.putAll(currentCopyMap); + } + + for(EObject eObject : copyRefMap.values()) { + Collection<EReference> refToOrigList = eObject.eClass().getEAllReferences(); + for(EReference refToOrig : refToOrigList) { + if(refToOrig.isContainment()) { + // Containments are handled by the copy operation itself. + continue; + } + if(refToOrig.isMany()) { + @SuppressWarnings("unchecked") EList<EObject> origList = + (EList<EObject>)eObject.eGet(refToOrig, false); + List<EObject> origCacheList = new ArrayList<>(origList); + // WARNING! WARNING! WARNING! + // We MUST add the copied elements to the list first, such that the + // reference list is not nulled by EMF in the parent element. This + // happens if the list contains only a single element: Removing this + // element empties the list and triggers a removal of the list from + // its container via the EMF notification mechanism. + origCacheList.forEach(e -> { + EObject eObj = copyRefMap.get(e); + if(eObj != null) { + origList.add(eObj); + origList.remove(e); + } + }); + } else { + EObject origRef = (EObject)eObject.eGet(refToOrig); + if(origRef == null && refToOrig.getEOpposite() != null) { + // Note: For bi-references between the input elements we must use the + // inverse view of the bi-copy-map since the bi-references are nulled at + // both ends. + EObject origEObj = copyRefMap.inverse().get(eObject); + origRef = (EObject)origEObj.eGet(refToOrig); + } + EObject copyRef = copyRefMap.get(origRef); + if(copyRef != null) { + eObject.eSet(refToOrig, copyRef); + } + } + } + } + + return copyRefMap; + } + /** Map to cache potentially expensive lookups in {@link #getEClassForClass(Class)}. */ private static Map<Class<?>, EClass> clazz2EClassCache = new HashMap<Class<?>, EClass>();