From 972554ac3756ee8f8209d95d579afd51d6e1f096 Mon Sep 17 00:00:00 2001 From: dontyouo Date: Fri, 21 Apr 2023 13:44:09 +0300 Subject: [PATCH 1/7] added simple detection of a cycle consisting of objects of class Decision --- plugins/ru.runa.gpd/plugin.properties | 12 ++-- plugins/ru.runa.gpd/plugin.xml | 7 +++ plugins/ru.runa.gpd/plugin_ru.properties | 4 ++ .../ru/runa/gpd/algorithms/CycleDetector.java | 61 +++++++++++++++++++ .../gpd/ui/action/CycleDetectorAction.java | 60 ++++++++++++++++++ 5 files changed, 140 insertions(+), 4 deletions(-) create mode 100644 plugins/ru.runa.gpd/src/ru/runa/gpd/algorithms/CycleDetector.java create mode 100644 plugins/ru.runa.gpd/src/ru/runa/gpd/ui/action/CycleDetectorAction.java diff --git a/plugins/ru.runa.gpd/plugin.properties b/plugins/ru.runa.gpd/plugin.properties index 1ff98f071..d23531fd4 100644 --- a/plugins/ru.runa.gpd/plugin.properties +++ b/plugins/ru.runa.gpd/plugin.properties @@ -45,6 +45,7 @@ label.menu.help=Help label.menu.moreDetails=More details label.menu.helpContents=Help Contents label.menu.helpAbout=About +label.menu.cycleDetector=Check for "instantaneous" cycles label.menu.preferences=Preferences label.preference.diagram=Diagram settings label.menu.mapping=Mapping @@ -928,7 +929,7 @@ model.validation.startState.formScriptIsNotUsableInSubprocess = Form script will model.validation.startState.swimlaneIsNotUsableInEmbeddedSubprocess = Swimlane will never used in start state of embedded subprocess model.validation.multiTask.discriminator.empty = Discriminator variable is not defined model.validation.multiTask.discriminator.executors.classcasterror = Tasks can be assigned only by List(Executor) variable -model.validation.multiTask.discriminator.unmapped = +model.validation.multiTask.discriminator.unmapped = model.validation.multiTask.variable.duplicate.name = Variable \"{0}\" defined twice in mapping model.validation.multiTask.variable.duplicate.mappedName = Variable \"{0}\" defined twice in mapping model.validation.multiTask.variable.doesNotExist = Variable \"{0}\" does not exist @@ -1134,9 +1135,9 @@ mail.smtp.port.help = Mail server port mail.smtp.auth = Use authentication mail.smtp.auth.help = Mail server requires authentication mail.user = User name -mail.user.help = +mail.user.help = mail.password = Password -mail.password.help = +mail.password.help = mail.debug = Debug mail.debug.help = Write protocol communication to log mail.subject = Subject @@ -1144,7 +1145,7 @@ mail.subject.help = Message subject mail.to = To mail.to.help = Recipient's e-mail mail.cc = Cc -mail.cc.help = +mail.cc.help = mail.bodyInlined = Use body from form pref.notation.defaultNotation = Default notation @@ -1359,6 +1360,9 @@ ru.runa.wfe.extension.orgfunction.ExecutorsFromListFunction=Executors from list CheckingTokensAction.SituationExist.Message=The situation with infinitely increasing tokens can be exist. The problem found in object {0}. CheckingTokensAction.SituationNotExist.Message=The situation with infinitely increasing tokens can not be exist. +CycleDetectorAction.CycleExist.Message=The cycle can be exist. +CycleDetectorAction.CycleNotExist.Message=The cycle can not be exist. + VariableWizard.create = Create variable VariableWizard.edit = Edit variable VariableNamePage.title = Set variable name diff --git a/plugins/ru.runa.gpd/plugin.xml b/plugins/ru.runa.gpd/plugin.xml index 8d01409da..be35fce46 100644 --- a/plugins/ru.runa.gpd/plugin.xml +++ b/plugins/ru.runa.gpd/plugin.xml @@ -3046,6 +3046,13 @@ menubarPath="file/command" style="push"> + + visited; + private Queue queue; + + public CycleDetector() { + visited = new HashSet<>(); + queue = new PriorityQueue<>(); + } + + + public boolean hasCycle(Node startNode) { + visited.add(startNode); + queue.add(startNode); + while (!queue.isEmpty()) { + Node currentNode = queue.poll(); + if (currentNode instanceof Decision) { + boolean isCycle = traverseDecision((Decision) currentNode, new HashSet<>()); + if (isCycle) { + return true; + } + } + for (AbstractTransition outgoingNode : currentNode.getLeavingTransitions()) { + if (!visited.contains(outgoingNode.getTarget())) { + visited.add(outgoingNode.getTarget()); + queue.add(outgoingNode.getTarget()); + } + } + } + return false; + } + + private boolean traverseDecision(Decision decision, Set visited) { + visited.add(decision); + for (AbstractTransition outgoingNode : decision.getLeavingTransitions()) { + if (outgoingNode.getTarget() instanceof Decision) { + Decision nextDecision = (Decision) outgoingNode.getTarget(); + if (visited.contains(nextDecision)) { + return true; + } + boolean isCycle = traverseDecision(nextDecision, visited); + if (isCycle) { + return true; + } + } + } + visited.remove(decision); + return false; + } +} \ No newline at end of file diff --git a/plugins/ru.runa.gpd/src/ru/runa/gpd/ui/action/CycleDetectorAction.java b/plugins/ru.runa.gpd/src/ru/runa/gpd/ui/action/CycleDetectorAction.java new file mode 100644 index 000000000..038c26ba9 --- /dev/null +++ b/plugins/ru.runa.gpd/src/ru/runa/gpd/ui/action/CycleDetectorAction.java @@ -0,0 +1,60 @@ +package ru.runa.gpd.ui.action; + +import java.util.List; + +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.part.FileEditorInput; + +import ru.runa.gpd.Localization; +import ru.runa.gpd.ProcessCache; +import ru.runa.gpd.editor.ProcessEditorBase; +import ru.runa.gpd.lang.Language; +import ru.runa.gpd.lang.model.Node; +import ru.runa.gpd.lang.model.StartState; +import ru.runa.gpd.lang.model.ProcessDefinition; +import ru.runa.gpd.lang.model.Transition; +import ru.runa.gpd.lang.model.bpmn.ExclusiveGateway; +import ru.runa.gpd.ui.custom.Dialogs; +import ru.runa.gpd.algorithms.CycleDetector; + +public class CycleDetectorAction extends BaseActionDelegate { + + @Override + public void run(IAction action) { + IEditorPart editorPart = getActiveEditor(); + if (editorPart != null) { + IEditorInput editorInput = editorPart.getEditorInput(); + if (editorInput instanceof FileEditorInput) { + ProcessDefinition definition = ProcessCache.getProcessDefinition( + ((FileEditorInput) editorInput).getFile()); + StartState startNode = definition.getChildren(StartState.class).get(0); + + CycleDetector cycleDetector = new CycleDetector(); + boolean isCycle = cycleDetector.hasCycle(startNode); + + if (isCycle) { + Dialogs.warning(Localization.getString("CycleDetectorAction.CycleExist.Message")); + } else { + Dialogs.information(Localization.getString("CycleDetectorAction.CycleNotExist.Message")); + } + } + } + } + + @Override + public void selectionChanged (IAction action, ISelection selection){ + ProcessEditorBase editor = getActiveDesignerEditor(); + List nodes = null; + if (editor != null) { + nodes = editor.getDefinition().getChildren(ExclusiveGateway.class); + } + action.setEnabled(nodes != null && !nodes.isEmpty()); + } + + private IEditorPart[] getDirtyEditors () { + return window.getActivePage().getDirtyEditors(); + } + } From 0009d09dd14aba524f5da09e6570229662e61e4b Mon Sep 17 00:00:00 2001 From: dontyouo Date: Fri, 21 Apr 2023 13:54:56 +0300 Subject: [PATCH 2/7] added simple detection of a cycle consisting of objects of class Decision, fixed merge conflict with GraphElement --- .../ru/runa/gpd/lang/model/GraphElement.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/plugins/ru.runa.gpd/src/ru/runa/gpd/lang/model/GraphElement.java b/plugins/ru.runa.gpd/src/ru/runa/gpd/lang/model/GraphElement.java index 28aa997e7..6687533cf 100644 --- a/plugins/ru.runa.gpd/src/ru/runa/gpd/lang/model/GraphElement.java +++ b/plugins/ru.runa.gpd/src/ru/runa/gpd/lang/model/GraphElement.java @@ -49,7 +49,7 @@ @SuppressWarnings("unchecked") public abstract class GraphElement extends EventSupport - implements IPropertySource, PropertyNames, IActionFilter, VariableContainer, ProcessDefinitionAware { + implements IPropertySource, PropertyNames, IActionFilter, VariableContainer, ProcessDefinitionAware { private PropertyChangeListener delegatedListener; private GraphElement parent; private GraphElement uiParentContainer; @@ -129,7 +129,7 @@ public void setConstraint(Rectangle newConstraint) { if (this.constraint != null && this instanceof IBoundaryEventContainer) { for (GraphElement element : children) { if (element.getConstraint() != null && element instanceof IBoundaryEventCapable - && ((IBoundaryEventCapable) element).isBoundaryEvent()) { + && ((IBoundaryEventCapable) element).isBoundaryEvent()) { ((IBoundaryEventCapable) element).updateBoundaryEventConstraint(); } } @@ -173,14 +173,14 @@ public void validate(List errors, IFile definitionFile) { List docxErrors = Lists.newArrayList(); List docxErrorSources = Lists.newArrayList(); DocxDialogEnhancement.checkScriptTaskParametersWithDocxTemplate(delegable, - EmbeddedFileUtils.getProcessFileName(embeddedDocxTemplateFileName), docxErrors, docxErrorSources, null); + EmbeddedFileUtils.getProcessFileName(embeddedDocxTemplateFileName), docxErrors, docxErrorSources, null); if (docxErrors.size() > 0 && docxErrors.size() == docxErrorSources.size()) { ListIterator iterator = docxErrors.listIterator(); ListIterator iterator2 = docxErrorSources.listIterator(); while (iterator.hasNext()) { Delegable delegable2 = iterator2.next(); errors.add(ValidationError.createError(delegable2 instanceof GraphElement ? (GraphElement) delegable2 : this, - iterator.next())); + iterator.next())); } } } @@ -260,7 +260,7 @@ public void changeChildIndex(GraphElement child, GraphElement insertBefore) { firePropertyChange(PROPERTY_CHILDREN_CHANGED, old, before + 1); } } - + public List getChildren(Class type) { return getChildren(type, null); } @@ -411,19 +411,19 @@ public final IPropertyDescriptor[] getPropertyDescriptors() { } if (this instanceof Describable) { descriptors - .add(new DescribablePropertyDescriptor(PROPERTY_DESCRIPTION, Localization.getString("property.description"), (Describable) this)); + .add(new DescribablePropertyDescriptor(PROPERTY_DESCRIPTION, Localization.getString("property.description"), (Describable) this)); } if (isDelegable()) { Delegable delegable = (Delegable) this; descriptors.add(new DelegableClassPropertyDescriptor(PROPERTY_CLASS, Localization.getString("property.delegation.class"), delegable)); descriptors.add(new DelegableConfPropertyDescriptor(PROPERTY_CONFIGURATION, (Delegable) this, - Localization.getString("property.delegation.configuration"))); + Localization.getString("property.delegation.configuration"))); } if (this instanceof ITimed && getProcessDefinition().getLanguage() == Language.JPDL) { Timer timer = ((ITimed) this).getTimer(); if (timer != null) { descriptors.add(new DurationPropertyDescriptor(PROPERTY_TIMER_DELAY, timer.getProcessDefinition(), timer.getDelay(), - Localization.getString("property.duration"))); + Localization.getString("property.duration"))); descriptors.add(new TimerActionPropertyDescriptor(PROPERTY_TIMER_ACTION, Localization.getString("Timer.action"), timer)); } } @@ -539,4 +539,4 @@ public String getLabel() { return id; } -} +} \ No newline at end of file From 63c90560e34f9c4902305c55da97ff4f99a334cc Mon Sep 17 00:00:00 2001 From: dontyouo Date: Fri, 21 Apr 2023 14:13:38 +0300 Subject: [PATCH 3/7] added check if there is an StartState in the process before checking for cycles --- .../src/ru/runa/gpd/ui/action/CycleDetectorAction.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/plugins/ru.runa.gpd/src/ru/runa/gpd/ui/action/CycleDetectorAction.java b/plugins/ru.runa.gpd/src/ru/runa/gpd/ui/action/CycleDetectorAction.java index 038c26ba9..10cbb2f2a 100644 --- a/plugins/ru.runa.gpd/src/ru/runa/gpd/ui/action/CycleDetectorAction.java +++ b/plugins/ru.runa.gpd/src/ru/runa/gpd/ui/action/CycleDetectorAction.java @@ -47,11 +47,17 @@ public void run(IAction action) { @Override public void selectionChanged (IAction action, ISelection selection){ ProcessEditorBase editor = getActiveDesignerEditor(); + + // add to check if StartState exist in process + List startNodes = null; + if (editor != null) { + startNodes = editor.getDefinition().getChildren(StartState.class); + } List nodes = null; if (editor != null) { nodes = editor.getDefinition().getChildren(ExclusiveGateway.class); } - action.setEnabled(nodes != null && !nodes.isEmpty()); + action.setEnabled(nodes != null && startNodes!=null && !nodes.isEmpty() && !startNodes.isEmpty()); } private IEditorPart[] getDirtyEditors () { From 9cd25c370d497137bc15165d6838bb1cac9c35f5 Mon Sep 17 00:00:00 2001 From: dontyouo Date: Sat, 22 Apr 2023 10:47:30 +0300 Subject: [PATCH 4/7] added a sub-item to the menu for checking processes --- plugins/ru.runa.gpd/plugin.properties | 1 + plugins/ru.runa.gpd/plugin.xml | 9 ++++++++- plugins/ru.runa.gpd/plugin_ru.properties | 1 + 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/plugins/ru.runa.gpd/plugin.properties b/plugins/ru.runa.gpd/plugin.properties index d23531fd4..cebfd6bc3 100644 --- a/plugins/ru.runa.gpd/plugin.properties +++ b/plugins/ru.runa.gpd/plugin.properties @@ -46,6 +46,7 @@ label.menu.moreDetails=More details label.menu.helpContents=Help Contents label.menu.helpAbout=About label.menu.cycleDetector=Check for "instantaneous" cycles +label.menu.checkProcess=Check process label.menu.preferences=Preferences label.preference.diagram=Diagram settings label.menu.mapping=Mapping diff --git a/plugins/ru.runa.gpd/plugin.xml b/plugins/ru.runa.gpd/plugin.xml index be35fce46..04307edc9 100644 --- a/plugins/ru.runa.gpd/plugin.xml +++ b/plugins/ru.runa.gpd/plugin.xml @@ -2689,6 +2689,13 @@ + + + + Date: Sat, 22 Apr 2023 12:18:14 +0300 Subject: [PATCH 5/7] added cycle check for ScriptState objects along with Decision --- .../ru/runa/gpd/algorithms/CycleDetector.java | 22 +++++---- .../gpd/ui/action/CycleDetectorAction.java | 47 +++++++++++-------- 2 files changed, 39 insertions(+), 30 deletions(-) diff --git a/plugins/ru.runa.gpd/src/ru/runa/gpd/algorithms/CycleDetector.java b/plugins/ru.runa.gpd/src/ru/runa/gpd/algorithms/CycleDetector.java index 09fcc9bfe..6bfd9d972 100644 --- a/plugins/ru.runa.gpd/src/ru/runa/gpd/algorithms/CycleDetector.java +++ b/plugins/ru.runa.gpd/src/ru/runa/gpd/algorithms/CycleDetector.java @@ -7,6 +7,7 @@ import ru.runa.gpd.lang.model.Node; import ru.runa.gpd.lang.model.AbstractTransition; import ru.runa.gpd.lang.model.Decision; +import ru.runa.gpd.lang.model.bpmn.ScriptTask; public class CycleDetector { @@ -25,8 +26,8 @@ public boolean hasCycle(Node startNode) { queue.add(startNode); while (!queue.isEmpty()) { Node currentNode = queue.poll(); - if (currentNode instanceof Decision) { - boolean isCycle = traverseDecision((Decision) currentNode, new HashSet<>()); + if (currentNode instanceof Decision || currentNode instanceof ScriptTask) { + boolean isCycle = traverseNode(currentNode, new HashSet<>()); if (isCycle) { return true; } @@ -41,21 +42,22 @@ public boolean hasCycle(Node startNode) { return false; } - private boolean traverseDecision(Decision decision, Set visited) { - visited.add(decision); - for (AbstractTransition outgoingNode : decision.getLeavingTransitions()) { - if (outgoingNode.getTarget() instanceof Decision) { - Decision nextDecision = (Decision) outgoingNode.getTarget(); - if (visited.contains(nextDecision)) { + private boolean traverseNode(Node node, Set visited) { + visited.add(node); + for (AbstractTransition outgoingNode : node.getLeavingTransitions()) { + if (outgoingNode.getTarget() instanceof Decision + || outgoingNode.getTarget() instanceof ScriptTask) { + Node nextNode = outgoingNode.getTarget(); + if (visited.contains(nextNode)) { return true; } - boolean isCycle = traverseDecision(nextDecision, visited); + boolean isCycle = traverseNode(nextNode, visited); if (isCycle) { return true; } } } - visited.remove(decision); + visited.remove(node); return false; } } \ No newline at end of file diff --git a/plugins/ru.runa.gpd/src/ru/runa/gpd/ui/action/CycleDetectorAction.java b/plugins/ru.runa.gpd/src/ru/runa/gpd/ui/action/CycleDetectorAction.java index 10cbb2f2a..94350b353 100644 --- a/plugins/ru.runa.gpd/src/ru/runa/gpd/ui/action/CycleDetectorAction.java +++ b/plugins/ru.runa.gpd/src/ru/runa/gpd/ui/action/CycleDetectorAction.java @@ -11,11 +11,9 @@ import ru.runa.gpd.Localization; import ru.runa.gpd.ProcessCache; import ru.runa.gpd.editor.ProcessEditorBase; -import ru.runa.gpd.lang.Language; -import ru.runa.gpd.lang.model.Node; +import ru.runa.gpd.lang.model.bpmn.ScriptTask; import ru.runa.gpd.lang.model.StartState; import ru.runa.gpd.lang.model.ProcessDefinition; -import ru.runa.gpd.lang.model.Transition; import ru.runa.gpd.lang.model.bpmn.ExclusiveGateway; import ru.runa.gpd.ui.custom.Dialogs; import ru.runa.gpd.algorithms.CycleDetector; @@ -36,31 +34,40 @@ public void run(IAction action) { boolean isCycle = cycleDetector.hasCycle(startNode); if (isCycle) { - Dialogs.warning(Localization.getString("CycleDetectorAction.CycleExist.Message")); + Dialogs.warning( + Localization.getString("CycleDetectorAction.CycleExist.Message")); } else { - Dialogs.information(Localization.getString("CycleDetectorAction.CycleNotExist.Message")); + Dialogs.information( + Localization.getString("CycleDetectorAction.CycleNotExist.Message")); } } } } - @Override - public void selectionChanged (IAction action, ISelection selection){ - ProcessEditorBase editor = getActiveDesignerEditor(); + @Override + public void selectionChanged(IAction action, ISelection selection) { + ProcessEditorBase editor = getActiveDesignerEditor(); - // add to check if StartState exist in process - List startNodes = null; - if (editor != null) { - startNodes = editor.getDefinition().getChildren(StartState.class); - } - List nodes = null; - if (editor != null) { - nodes = editor.getDefinition().getChildren(ExclusiveGateway.class); - } - action.setEnabled(nodes != null && startNodes!=null && !nodes.isEmpty() && !startNodes.isEmpty()); + List startNodes = null; + if (editor != null) { + startNodes = editor.getDefinition().getChildren(StartState.class); + } + List nodes = null; + if (editor != null) { + nodes = editor.getDefinition().getChildren(ExclusiveGateway.class); } - private IEditorPart[] getDirtyEditors () { - return window.getActivePage().getDirtyEditors(); + List scriptNodes = null; + if (editor != null) { + scriptNodes = editor.getDefinition().getChildren(ScriptTask.class); } + + action.setEnabled( + startNodes != null && !startNodes.isEmpty() && (nodes != null && !nodes.isEmpty() + || scriptNodes != null && !scriptNodes.isEmpty())); + } + + private IEditorPart[] getDirtyEditors() { + return window.getActivePage().getDirtyEditors(); } +} From 1b96bad85c034481c666c0e59a9633ad9eda47e7 Mon Sep 17 00:00:00 2001 From: dontyouo Date: Sat, 22 Apr 2023 12:40:24 +0300 Subject: [PATCH 6/7] moved CheckUnlimitedToken button to checkProcess menubar --- plugins/ru.runa.gpd/plugin.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/ru.runa.gpd/plugin.xml b/plugins/ru.runa.gpd/plugin.xml index 04307edc9..5b4ce2c8a 100644 --- a/plugins/ru.runa.gpd/plugin.xml +++ b/plugins/ru.runa.gpd/plugin.xml @@ -3050,7 +3050,7 @@ class="ru.runa.gpd.ui.action.CheckUnlimitedTokenAction" id="ru.runa.gpd.checkUnlimitedToken" label="%label.menu.checkingTokens" - menubarPath="file/command" + menubarPath="file/checkProcess/unlimitedToken" style="push"> Date: Mon, 24 Apr 2023 16:11:58 +0300 Subject: [PATCH 7/7] added support for detecting cycles with the participation of a ParallelGateway --- .../ru/runa/gpd/algorithms/CycleDetector.java | 38 +++++++++++-------- .../gpd/ui/action/CycleDetectorAction.java | 32 +++++++--------- 2 files changed, 37 insertions(+), 33 deletions(-) diff --git a/plugins/ru.runa.gpd/src/ru/runa/gpd/algorithms/CycleDetector.java b/plugins/ru.runa.gpd/src/ru/runa/gpd/algorithms/CycleDetector.java index 6bfd9d972..00d2bb2b5 100644 --- a/plugins/ru.runa.gpd/src/ru/runa/gpd/algorithms/CycleDetector.java +++ b/plugins/ru.runa.gpd/src/ru/runa/gpd/algorithms/CycleDetector.java @@ -8,6 +8,7 @@ import ru.runa.gpd.lang.model.AbstractTransition; import ru.runa.gpd.lang.model.Decision; import ru.runa.gpd.lang.model.bpmn.ScriptTask; +import ru.runa.gpd.lang.model.bpmn.ParallelGateway; public class CycleDetector { @@ -26,8 +27,9 @@ public boolean hasCycle(Node startNode) { queue.add(startNode); while (!queue.isEmpty()) { Node currentNode = queue.poll(); - if (currentNode instanceof Decision || currentNode instanceof ScriptTask) { - boolean isCycle = traverseNode(currentNode, new HashSet<>()); + if (currentNode instanceof Decision || currentNode instanceof ScriptTask + || currentNode instanceof ParallelGateway) { + boolean isCycle = traverseNode(currentNode); if (isCycle) { return true; } @@ -42,22 +44,28 @@ public boolean hasCycle(Node startNode) { return false; } - private boolean traverseNode(Node node, Set visited) { - visited.add(node); - for (AbstractTransition outgoingNode : node.getLeavingTransitions()) { - if (outgoingNode.getTarget() instanceof Decision - || outgoingNode.getTarget() instanceof ScriptTask) { - Node nextNode = outgoingNode.getTarget(); - if (visited.contains(nextNode)) { - return true; - } - boolean isCycle = traverseNode(nextNode, visited); - if (isCycle) { - return true; + private boolean traverseNode(Node startNode) { + Queue queue = new PriorityQueue<>(); + Set visited = new HashSet<>(); + queue.add(startNode); + while (!queue.isEmpty()) { + Node node = queue.poll(); + if (visited.contains(node)) { + return true; + } + visited.add(node); + for (AbstractTransition outgoingNode : node.getLeavingTransitions()) { + if (outgoingNode.getTarget() instanceof Decision + || outgoingNode.getTarget() instanceof ScriptTask + || outgoingNode.getTarget() instanceof ParallelGateway) { + queue.add(outgoingNode.getTarget()); + } else { + if (node instanceof ParallelGateway) { + return false; + } } } } - visited.remove(node); return false; } } \ No newline at end of file diff --git a/plugins/ru.runa.gpd/src/ru/runa/gpd/ui/action/CycleDetectorAction.java b/plugins/ru.runa.gpd/src/ru/runa/gpd/ui/action/CycleDetectorAction.java index 94350b353..fea9e2319 100644 --- a/plugins/ru.runa.gpd/src/ru/runa/gpd/ui/action/CycleDetectorAction.java +++ b/plugins/ru.runa.gpd/src/ru/runa/gpd/ui/action/CycleDetectorAction.java @@ -13,6 +13,7 @@ import ru.runa.gpd.editor.ProcessEditorBase; import ru.runa.gpd.lang.model.bpmn.ScriptTask; import ru.runa.gpd.lang.model.StartState; +import ru.runa.gpd.lang.model.Node; import ru.runa.gpd.lang.model.ProcessDefinition; import ru.runa.gpd.lang.model.bpmn.ExclusiveGateway; import ru.runa.gpd.ui.custom.Dialogs; @@ -46,28 +47,23 @@ public void run(IAction action) { @Override public void selectionChanged(IAction action, ISelection selection) { - ProcessEditorBase editor = getActiveDesignerEditor(); - - List startNodes = null; - if (editor != null) { - startNodes = editor.getDefinition().getChildren(StartState.class); - } - List nodes = null; - if (editor != null) { - nodes = editor.getDefinition().getChildren(ExclusiveGateway.class); - } - - List scriptNodes = null; - if (editor != null) { - scriptNodes = editor.getDefinition().getChildren(ScriptTask.class); - } - action.setEnabled( - startNodes != null && !startNodes.isEmpty() && (nodes != null && !nodes.isEmpty() - || scriptNodes != null && !scriptNodes.isEmpty())); + hasElements(StartState.class) && (hasElements(ExclusiveGateway.class) || hasElements( + ScriptTask.class))); } private IEditorPart[] getDirtyEditors() { return window.getActivePage().getDirtyEditors(); } + + private boolean hasElements(Class type) { + ProcessEditorBase editor = getActiveDesignerEditor(); + + List nodes = null; + if (editor != null) { + nodes = editor.getDefinition().getChildren(type); + } + + return nodes != null && !nodes.isEmpty(); + } }