diff --git a/plugins/ru.runa.gpd/plugin.properties b/plugins/ru.runa.gpd/plugin.properties
index 1ff98f071..cebfd6bc3 100644
--- a/plugins/ru.runa.gpd/plugin.properties
+++ b/plugins/ru.runa.gpd/plugin.properties
@@ -45,6 +45,8 @@ 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.checkProcess=Check process
label.menu.preferences=Preferences
label.preference.diagram=Diagram settings
label.menu.mapping=Mapping
@@ -928,7 +930,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 +1136,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 +1146,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 +1361,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..5b4ce2c8a 100644
--- a/plugins/ru.runa.gpd/plugin.xml
+++ b/plugins/ru.runa.gpd/plugin.xml
@@ -2689,6 +2689,13 @@
+
+
+
+
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 || currentNode instanceof ScriptTask
+ || currentNode instanceof ParallelGateway) {
+ boolean isCycle = traverseNode(currentNode);
+ 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 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;
+ }
+ }
+ }
+ }
+ return false;
+ }
+}
\ No newline at end of file
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
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..fea9e2319
--- /dev/null
+++ b/plugins/ru.runa.gpd/src/ru/runa/gpd/ui/action/CycleDetectorAction.java
@@ -0,0 +1,69 @@
+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.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;
+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) {
+ action.setEnabled(
+ 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();
+ }
+}