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(); + } +}