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

Plant Hierarchy inconsistency prevention when deleting subapp #1101

Open
wants to merge 4 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 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
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ Require-Bundle: org.eclipse.core.resources,
org.eclipse.ui.ide,
org.eclipse.ui.views.properties.tabbed,
org.eclipse.fordiac.ide.model.commands,
org.eclipse.gef
org.eclipse.gef,
org.eclipse.ltk.core.refactoring
Bundle-Vendor: Eclipse 4diac Project
Automatic-Module-Name: org.eclipse.fordiac.ide.hierarchymanager.ui
Bundle-RequiredExecutionEnvironment: JavaSE-21
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -278,4 +278,13 @@
point="org.eclipse.fordiac.ide.model.commands.QualNameChangeListener">
<listener class = "org.eclipse.fordiac.ide.hierarchymanager.ui.listeners.HierachyManagerUpdateListener"/>
</extension>
<extension
point="org.eclipse.ltk.core.refactoring.deleteParticipants">
<deleteParticipant
class="org.eclipse.fordiac.ide.hierarchymanager.ui.refactoring.DeleteLibraryElementParticipant"
id="org.eclipse.fordiac.ide.hierarchymanager.ui.deleteLibraryElementParticipant"
name="deletePlantElement">
<enablement></enablement>
</deleteParticipant>
</extension>
</plugin>
Original file line number Diff line number Diff line change
Expand Up @@ -31,20 +31,22 @@
import org.eclipse.fordiac.ide.hierarchymanager.model.hierarchy.util.HierarchyResourceFactoryImpl;
import org.eclipse.fordiac.ide.hierarchymanager.ui.handlers.AbstractHierarchyHandler;
import org.eclipse.fordiac.ide.hierarchymanager.ui.operations.AbstractChangeHierarchyOperation;
import org.eclipse.fordiac.ide.hierarchymanager.ui.operations.DeleteNodeOperation;
import org.eclipse.fordiac.ide.hierarchymanager.ui.operations.UpdateLeafRefOperation;
import org.eclipse.fordiac.ide.hierarchymanager.ui.util.HierarchyManagerUtil;
import org.eclipse.fordiac.ide.hierarchymanager.ui.view.PlantHierarchyView;
import org.eclipse.fordiac.ide.model.commands.QualNameChange;
import org.eclipse.fordiac.ide.model.commands.QualNameChangeListener;
import org.eclipse.fordiac.ide.model.libraryElement.INamedElement;
import org.eclipse.fordiac.ide.model.libraryElement.SubApp;
import org.eclipse.fordiac.ide.model.libraryElement.UntypedSubApp;
import org.eclipse.fordiac.ide.model.typelibrary.TypeEntry;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PlatformUI;

public class HierachyManagerUpdateListener extends QualNameChangeListener {

private static EObject loadPlantHierachy(final IProject project) {
public static EObject loadPlantHierachy(final IProject project) {
final Map<String, Object> loadOptions = new HashMap<>();
final ResourceSet hierarchyResouceSet = new ResourceSetImpl();
hierarchyResouceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put( //
Expand All @@ -61,6 +63,8 @@ private static EObject loadPlantHierachy(final IProject project) {
return PlantHierarchyView.loadHierachyForProject(project, hierarchyResouceSet, loadOptions);
}

private final HashMap<String, DeleteNodeOperation> deleteNodeOperations = new HashMap<>();

@Override
public List<AbstractOperation> constructExecutableUndoOperations(final QualNameChange change, final Object o) {
return constructOperation(change, o, true);
Expand All @@ -73,24 +77,39 @@ protected List<AbstractOperation> constructExecutableOperations(final QualNameCh
return constructOperation(qualNameChange, rootLevel, false);
}

protected static List<AbstractOperation> constructOperation(final QualNameChange qualNameChange,
final Object rootLevel, final boolean isUndo) {
protected List<AbstractOperation> constructOperation(final QualNameChange qualNameChange, final Object rootLevel,
final boolean isUndo) {

final String identifier = isUndo ? qualNameChange.newQualName() : qualNameChange.oldQualName();

final List<AbstractOperation> result = new ArrayList<>();

final List<Leaf> leafs = HierarchyManagerUtil.searchLeaf((RootLevel) rootLevel,
leafRef -> leafRef.contains(identifier));
if (leafs == null || leafs.isEmpty()) {
return Collections.emptyList(); // leaf may have been deleted in the meantime
}
if ((qualNameChange.state() == QualNameChangeState.DELETE_UNDO)
&& qualNameChange.notifier() instanceof final SubApp sub) {

final String newRef = isUndo ? qualNameChange.oldQualName() : qualNameChange.newQualName();
final DeleteNodeOperation op = deleteNodeOperations.get(qualNameChange.oldQualName());
result.add(op.createUndoOperation(sub));
} else {
final List<Leaf> leafs = HierarchyManagerUtil.searchLeaf((RootLevel) rootLevel,
leaf -> leaf.getRef().contains(identifier));

for (final Leaf leaf : leafs) {
result.add(new UpdateLeafRefOperation(leaf, newRef, identifier));
}
if (leafs == null || leafs.isEmpty()) {
return Collections.emptyList(); // leaf may have been deleted in the meantime
}

final String newRef = isUndo ? qualNameChange.oldQualName() : qualNameChange.newQualName();

for (final Leaf leaf : leafs) {
if (qualNameChange.state() == QualNameChangeState.DELETE
|| qualNameChange.state() == QualNameChangeState.DELETE_REDO) {
final DeleteNodeOperation operation = new DeleteNodeOperation(leaf);
deleteNodeOperations.put(qualNameChange.oldQualName(), operation);
result.add(operation);
} else {
result.add(new UpdateLeafRefOperation(leaf, newRef, identifier));
}
}
}
return result;
}

Expand All @@ -104,7 +123,7 @@ protected void executeOperation(final AbstractOperation op) {
AbstractHierarchyHandler.executeOperation((AbstractChangeHierarchyOperation) op);
}

private static RootLevel getPlantHierachy(final TypeEntry key) {
public static RootLevel getPlantHierachy(final TypeEntry key) {
final IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
if (page != null) {
final PlantHierarchyView view = (PlantHierarchyView) page
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.eclipse.fordiac.ide.hierarchymanager.model.hierarchy.Level;
import org.eclipse.fordiac.ide.hierarchymanager.model.hierarchy.Node;
import org.eclipse.fordiac.ide.hierarchymanager.model.hierarchy.RootLevel;
import org.eclipse.fordiac.ide.model.libraryElement.SubApp;

public class DeleteNodeOperation extends AbstractChangeHierarchyOperation {

Expand All @@ -43,12 +44,17 @@ public IStatus execute(final IProgressMonitor monitor, final IAdaptable info) th
return Status.OK_STATUS;
}

private int getParentIndex() {
final EList<? extends Node> parentContainer = getParentContainer();
if (parentContainer != null) {
return parentContainer.indexOf(node);
@Override
public IStatus undo(final IProgressMonitor monitor, final IAdaptable info) throws ExecutionException {
// for adding we can not use getParentContainer as the root node has levels and not nodes
if (parent instanceof final RootLevel root) {
root.getLevels().add(index, (Level) node);
}
return -1;
if (parent instanceof final Level level) {
level.getChildren().add(index, node);
}
saveHierarchy(parent, monitor);
return Status.OK_STATUS;
}

@Override
Expand All @@ -58,24 +64,23 @@ public IStatus redo(final IProgressMonitor monitor, final IAdaptable info) throw
return Status.OK_STATUS;
}

private void removeFromParent() {
public CreateLeafOperation createUndoOperation(final SubApp subApp) {
return new CreateLeafOperation((Level) parent, subApp);
}

private int getParentIndex() {
final EList<? extends Node> parentContainer = getParentContainer();
if (parentContainer != null && index != -1) {
parentContainer.remove(index);
if (parentContainer != null) {
return parentContainer.indexOf(node);
}
return -1;
}

@Override
public IStatus undo(final IProgressMonitor monitor, final IAdaptable info) throws ExecutionException {
// for adding we can not use getParentContainer as the root node has levels and not nodes
if (parent instanceof final RootLevel root) {
root.getLevels().add(index, (Level) node);
}
if (parent instanceof final Level level) {
level.getChildren().add(index, node);
private void removeFromParent() {
final EList<? extends Node> parentContainer = getParentContainer();
if (parentContainer != null && index != -1) {
parentContainer.remove(index);
}
saveHierarchy(parent, monitor);
return Status.OK_STATUS;
}

private EList<? extends Node> getParentContainer() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*******************************************************************************
* Copyright (c) 2025 Primetals Technologies Austria GmbH
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Qemal Alliu - initial API and implementation and/or initial documentation
*******************************************************************************/
package org.eclipse.fordiac.ide.hierarchymanager.ui.refactoring;

import java.util.List;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.fordiac.ide.hierarchymanager.model.hierarchy.Leaf;
import org.eclipse.fordiac.ide.hierarchymanager.model.hierarchy.RootLevel;
import org.eclipse.fordiac.ide.hierarchymanager.ui.listeners.HierachyManagerUpdateListener;
import org.eclipse.fordiac.ide.hierarchymanager.ui.util.HierarchyManagerUtil;
import org.eclipse.fordiac.ide.hierarchymanager.ui.view.PlantHierarchyView;
import org.eclipse.ltk.core.refactoring.Change;
import org.eclipse.ltk.core.refactoring.RefactoringStatus;
import org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext;
import org.eclipse.ltk.core.refactoring.participants.DeleteParticipant;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PlatformUI;

public class DeleteLibraryElementParticipant extends DeleteParticipant {

private RootLevel plantHierarchy;

private IFile file;

@Override
protected boolean initialize(final Object element) {

if (element instanceof final IFile file) {
this.file = file;
}

Display.getDefault().syncExec(() -> {
final IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
PlantHierarchyView view = null;

if (page != null) {
view = (PlantHierarchyView) page.findView("org.eclipse.fordiac.ide.hierarchymanager.view"); //$NON-NLS-1$

}

if (view != null) {
plantHierarchy = (RootLevel) view.getCommonViewer().getInput();
} else if (element instanceof final IFile file) {
plantHierarchy = (RootLevel) HierachyManagerUpdateListener.loadPlantHierachy(file.getProject());
}
});

return true;
}

@Override
public String getName() {
return "Delete element of plant hierarchy";
}

@Override
public RefactoringStatus checkConditions(final IProgressMonitor pm, final CheckConditionsContext context)
throws OperationCanceledException {

return new RefactoringStatus();
}

@Override
public Change createChange(final IProgressMonitor pm) throws CoreException, OperationCanceledException {
try {
pm.beginTask("Creating change...", 1); //$NON-NLS-1$

final List<Leaf> leaves = HierarchyManagerUtil.searchLeaf(plantHierarchy,
leaf -> leaf.getContainerFileName().contains(file.getName()));

if (!leaves.isEmpty()) {
return new SafePlantElementDeletionChange(plantHierarchy, leaves);
}

return null;
} finally {
pm.done();
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*******************************************************************************
* Copyright (c) 2025 Primetals Technologies Austria GmbH
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Qemal Alliu - initial API and implementation and/or initial documentation
*******************************************************************************/
package org.eclipse.fordiac.ide.hierarchymanager.ui.refactoring;

import java.util.List;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.fordiac.ide.hierarchymanager.model.hierarchy.Leaf;
import org.eclipse.fordiac.ide.hierarchymanager.model.hierarchy.RootLevel;
import org.eclipse.fordiac.ide.hierarchymanager.ui.handlers.AbstractHierarchyHandler;
import org.eclipse.fordiac.ide.hierarchymanager.ui.operations.DeleteNodeOperation;
import org.eclipse.ltk.core.refactoring.Change;
import org.eclipse.ltk.core.refactoring.RefactoringStatus;
import org.eclipse.swt.widgets.Display;

public class SafePlantElementDeletionChange extends Change {

private final RootLevel rootLevel;

private final List<Leaf> leaves;

public SafePlantElementDeletionChange(final RootLevel rootLevel, final List<Leaf> leaves) {
this.rootLevel = rootLevel;
this.leaves = leaves;
}

@Override
public String getName() {
return "Delete from Plant hierarchy";
}

@Override
public void initializeValidationData(final IProgressMonitor pm) {
// nothing to validate
}

@Override
public RefactoringStatus isValid(final IProgressMonitor pm) throws CoreException, OperationCanceledException {
return new RefactoringStatus();
}

@Override
public Change perform(final IProgressMonitor pm) throws CoreException {

for (final Leaf l : leaves) {
Display.getDefault().asyncExec(() -> {
AbstractHierarchyHandler.executeOperation(new DeleteNodeOperation(l));
});
}

return null;
}

@Override
public Object getModifiedElement() {
return rootLevel;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ public static EObject parseSubappPath(FBNetwork network, final String[] path) {

@FunctionalInterface
public interface LeafMatcher {
boolean match(String s);
boolean match(Leaf leaf);
}

public static List<Leaf> searchLeaf(final RootLevel rootLevel, final LeafMatcher matcher) {
Expand All @@ -124,7 +124,7 @@ public static List<Leaf> searchLeaf(final Level level, final List<Leaf> result,
if (node instanceof final Level l) {
searchLeaf(l, result, matcher);
}
if (node instanceof final Leaf leaf && matcher.match(leaf.getRef())) {
if (node instanceof final Leaf leaf && matcher.match(leaf)) {
result.add(leaf);
}
}
Expand Down
Loading
Loading