1 package org
.argeo
.slc
.client
.ui
.editors
;
3 import java
.util
.ArrayList
;
4 import java
.util
.Iterator
;
6 import java
.util
.SortedSet
;
7 import java
.util
.TreeSet
;
10 import javax
.jcr
.Node
;
11 import javax
.jcr
.NodeIterator
;
12 import javax
.jcr
.Property
;
13 import javax
.jcr
.RepositoryException
;
14 import javax
.jcr
.Session
;
15 import javax
.jcr
.nodetype
.NodeType
;
16 import javax
.jcr
.observation
.Event
;
17 import javax
.jcr
.observation
.EventListener
;
18 import javax
.jcr
.observation
.ObservationManager
;
19 import javax
.jcr
.query
.Query
;
20 import javax
.jcr
.query
.QueryManager
;
22 import org
.argeo
.ArgeoException
;
23 import org
.argeo
.eclipse
.ui
.jcr
.AsyncUiEventListener
;
24 import org
.argeo
.jcr
.JcrUtils
;
25 import org
.argeo
.slc
.SlcException
;
26 import org
.argeo
.slc
.client
.ui
.SlcImages
;
27 import org
.argeo
.slc
.core
.execution
.PrimitiveUtils
;
28 import org
.argeo
.slc
.execution
.ExecutionProcess
;
29 import org
.argeo
.slc
.jcr
.SlcJcrUtils
;
30 import org
.argeo
.slc
.jcr
.SlcNames
;
31 import org
.argeo
.slc
.jcr
.SlcTypes
;
32 import org
.eclipse
.jface
.viewers
.CellEditor
;
33 import org
.eclipse
.jface
.viewers
.ColumnLabelProvider
;
34 import org
.eclipse
.jface
.viewers
.ColumnViewer
;
35 import org
.eclipse
.jface
.viewers
.EditingSupport
;
36 import org
.eclipse
.jface
.viewers
.ISelectionChangedListener
;
37 import org
.eclipse
.jface
.viewers
.IStructuredContentProvider
;
38 import org
.eclipse
.jface
.viewers
.IStructuredSelection
;
39 import org
.eclipse
.jface
.viewers
.ITreeContentProvider
;
40 import org
.eclipse
.jface
.viewers
.SelectionChangedEvent
;
41 import org
.eclipse
.jface
.viewers
.StructuredSelection
;
42 import org
.eclipse
.jface
.viewers
.TableViewer
;
43 import org
.eclipse
.jface
.viewers
.TableViewerColumn
;
44 import org
.eclipse
.jface
.viewers
.TextCellEditor
;
45 import org
.eclipse
.jface
.viewers
.TreeViewer
;
46 import org
.eclipse
.jface
.viewers
.Viewer
;
47 import org
.eclipse
.jface
.viewers
.ViewerDropAdapter
;
48 import org
.eclipse
.swt
.SWT
;
49 import org
.eclipse
.swt
.custom
.SashForm
;
50 import org
.eclipse
.swt
.dnd
.DND
;
51 import org
.eclipse
.swt
.dnd
.TextTransfer
;
52 import org
.eclipse
.swt
.dnd
.Transfer
;
53 import org
.eclipse
.swt
.dnd
.TransferData
;
54 import org
.eclipse
.swt
.events
.SelectionEvent
;
55 import org
.eclipse
.swt
.events
.SelectionListener
;
56 import org
.eclipse
.swt
.graphics
.Image
;
57 import org
.eclipse
.swt
.layout
.FillLayout
;
58 import org
.eclipse
.swt
.layout
.GridData
;
59 import org
.eclipse
.swt
.layout
.GridLayout
;
60 import org
.eclipse
.swt
.layout
.RowData
;
61 import org
.eclipse
.swt
.layout
.RowLayout
;
62 import org
.eclipse
.swt
.widgets
.Button
;
63 import org
.eclipse
.swt
.widgets
.Composite
;
64 import org
.eclipse
.swt
.widgets
.Label
;
65 import org
.eclipse
.swt
.widgets
.Table
;
66 import org
.eclipse
.ui
.IWorkbenchPage
;
67 import org
.eclipse
.ui
.PlatformUI
;
68 import org
.eclipse
.ui
.forms
.AbstractFormPart
;
69 import org
.eclipse
.ui
.forms
.IManagedForm
;
70 import org
.eclipse
.ui
.forms
.editor
.FormPage
;
71 import org
.eclipse
.ui
.forms
.widgets
.FormToolkit
;
72 import org
.eclipse
.ui
.forms
.widgets
.ScrolledForm
;
74 public class ProcessBuilderPage
extends FormPage
implements SlcNames
{
75 public final static String ID
= "processBuilderPage";
76 // private final static Log log =
77 // LogFactory.getLog(ProcessBuilderPage.class);
79 private Node processNode
;
81 private TreeViewer flowsViewer
;
82 private TableViewer valuesViewer
;
83 private Label statusLabel
;
85 private Button remove
;
88 private AbstractFormPart formPart
;
89 private EventListener statusObserver
;
91 public ProcessBuilderPage(ProcessEditor editor
, Node processNode
) {
92 super(editor
, ID
, "Definition");
93 this.processNode
= processNode
;
97 protected void createFormContent(IManagedForm mf
) {
99 ScrolledForm form
= mf
.getForm();
100 form
.setExpandHorizontal(true);
101 form
.setExpandVertical(true);
102 form
.setText("Process " + processNode
.getName());
103 GridLayout mainLayout
= new GridLayout(1, true);
104 form
.getBody().setLayout(mainLayout
);
106 createControls(form
.getBody());
107 createBuilder(form
.getBody());
110 formPart
= new AbstractFormPart() {
113 getManagedForm().addPart(formPart
);
116 statusObserver
= new AsyncUiEventListener(form
.getDisplay()) {
117 protected void onEventInUiThread(List
<Event
> events
) {
121 ObservationManager observationManager
= processNode
.getSession()
122 .getWorkspace().getObservationManager();
123 observationManager
.addEventListener(statusObserver
,
124 Event
.PROPERTY_CHANGED
, processNode
.getPath(), true, null,
130 } catch (RepositoryException e
) {
131 throw new ArgeoException("Cannot create form content", e
);
135 protected void createControls(Composite parent
) {
136 FormToolkit tk
= getManagedForm().getToolkit();
138 Composite controls
= tk
.createComposite(parent
);
139 controls
.setLayout(new RowLayout());
140 controls
.setLayoutData(new GridData(SWT
.FILL
, SWT
.FILL
, true, false));
142 run
= tk
.createButton(controls
, null, SWT
.PUSH
);
143 run
.setToolTipText("Run");
144 run
.setImage(SlcImages
.LAUNCH
);
145 run
.addSelectionListener(new SelectionListener() {
146 public void widgetSelected(SelectionEvent e
) {
147 if (isFinished(getProcessStatus())) {
150 ((ProcessEditor
) getEditor()).process();
154 public void widgetDefaultSelected(SelectionEvent e
) {
159 remove
= tk
.createButton(controls
, null, SWT
.PUSH
);
160 remove
.setImage(SlcImages
.REMOVE_ONE
);
161 remove
.setToolTipText("Remove selected flows");
162 remove
.addSelectionListener(new SelectionListener() {
163 public void widgetSelected(SelectionEvent e
) {
164 removeSelectedFlows();
167 public void widgetDefaultSelected(SelectionEvent e
) {
172 clear
= tk
.createButton(controls
, null, SWT
.PUSH
);
173 clear
.setImage(SlcImages
.REMOVE_ALL
);
174 clear
.setToolTipText("Clear all flows");
175 clear
.addSelectionListener(new SelectionListener() {
176 public void widgetSelected(SelectionEvent e
) {
180 public void widgetDefaultSelected(SelectionEvent e
) {
185 Composite statusComposite
= tk
.createComposite(controls
);
186 RowData rowData
= new RowData();
189 statusComposite
.setLayoutData(rowData
);
190 statusComposite
.setLayout(new FillLayout());
191 statusLabel
= tk
.createLabel(statusComposite
, getProcessStatus());
193 // make sure all controls are in line with status
197 protected void createBuilder(Composite parent
) {
198 FormToolkit tk
= getManagedForm().getToolkit();
199 SashForm sashForm
= new SashForm(parent
, SWT
.HORIZONTAL
);
200 sashForm
.setSashWidth(4);
201 GridData sahFormGd
= new GridData(SWT
.FILL
, SWT
.FILL
, true, true);
202 sahFormGd
.widthHint
= 400;
203 sashForm
.setLayoutData(sahFormGd
);
205 Composite flowsComposite
= tk
.createComposite(sashForm
);
206 flowsComposite
.setLayout(new GridLayout(1, false));
208 flowsViewer
= new TreeViewer(flowsComposite
);
209 flowsViewer
.getTree().setLayoutData(
210 new GridData(SWT
.FILL
, SWT
.FILL
, true, true));
211 flowsViewer
.setLabelProvider(new FlowsLabelProvider());
212 flowsViewer
.setContentProvider(new FlowsContentProvider());
213 flowsViewer
.addSelectionChangedListener(new FlowsSelectionListener());
215 int operations
= DND
.DROP_COPY
| DND
.DROP_MOVE
;
216 Transfer
[] tt
= new Transfer
[] { TextTransfer
.getInstance() };
217 flowsViewer
.addDropSupport(operations
, tt
, new FlowsDropListener(
220 flowsViewer
.setInput(getEditorSite());
221 flowsViewer
.setInput(processNode
);
223 Composite valuesComposite
= tk
.createComposite(sashForm
);
224 valuesComposite
.setLayout(new GridLayout(1, false));
226 valuesViewer
= new TableViewer(valuesComposite
);
227 GridData valuedGd
= new GridData(SWT
.FILL
, SWT
.FILL
, true, true);
228 // valuedGd.widthHint = 200;
229 valuesViewer
.getTable().setLayoutData(valuedGd
);
230 valuesViewer
.setContentProvider(new ValuesContentProvider());
231 initializeValuesViewer(valuesViewer
);
232 sashForm
.setWeights(getWeights());
233 valuesViewer
.setInput(getEditorSite());
236 /** Creates the columns of the values viewer */
237 protected void initializeValuesViewer(TableViewer viewer
) {
238 String
[] titles
= { "Name", "Value" };
239 int[] bounds
= { 200, 100 };
241 for (int i
= 0; i
< titles
.length
; i
++) {
242 TableViewerColumn column
= new TableViewerColumn(viewer
, SWT
.NONE
);
243 column
.getColumn().setText(titles
[i
]);
244 column
.getColumn().setWidth(bounds
[i
]);
245 column
.getColumn().setResizable(true);
246 column
.getColumn().setMoveable(true);
248 column
.setLabelProvider(new ColumnLabelProvider() {
249 public String
getText(Object element
) {
251 Node specAttrNode
= (Node
) element
;
252 return specAttrNode
.getName();
253 } catch (RepositoryException e
) {
254 throw new SlcException("Cannot get value", e
);
259 column
.setLabelProvider(new ColumnLabelProvider() {
260 public String
getText(Object element
) {
261 Object obj
= getAttributeSpecValue((Node
) element
);
262 return obj
!= null ? obj
.toString() : "";
265 column
.setEditingSupport(new ValuesEditingSupport(viewer
));
269 Table table
= viewer
.getTable();
270 table
.setHeaderVisible(false);
271 table
.setLinesVisible(true);
274 protected int[] getWeights() {
275 return new int[] { 50, 50 };
281 /** Opens a new editor with a copy of this process */
282 protected void relaunch() {
284 Node duplicatedNode
= duplicateProcess();
285 IWorkbenchPage activePage
= PlatformUI
.getWorkbench()
286 .getActiveWorkbenchWindow().getActivePage();
287 activePage
.openEditor(
288 new ProcessEditorInput(duplicatedNode
.getPath()),
290 getEditor().close(false);
291 } catch (Exception e1
) {
292 throw new SlcException("Cannot relaunch " + processNode
, e1
);
296 /** Duplicates the process */
297 protected Node
duplicateProcess() {
299 Session session
= processNode
.getSession();
300 String uuid
= UUID
.randomUUID().toString();
301 String destPath
= SlcJcrUtils
.createExecutionProcessPath(uuid
);
302 Node newNode
= JcrUtils
.mkdirs(session
, destPath
,
303 SlcTypes
.SLC_PROCESS
);
304 JcrUtils
.copy(processNode
, newNode
);
305 // session.getWorkspace().copy(processNode.getPath(), destPath);
306 // Node newNode = session.getNode(destPath);
307 // make sure that we kept the mixins
308 // newNode.addMixin(NodeType.MIX_CREATED);
309 // newNode.addMixin(NodeType.MIX_LAST_MODIFIED);
310 newNode
.setProperty(SLC_UUID
, uuid
);
311 newNode
.setProperty(SLC_STATUS
, ExecutionProcess
.INITIALIZED
);
314 } catch (RepositoryException e
) {
315 throw new SlcException("Cannot duplicate process", e
);
319 /** Reflects a status change */
320 protected void statusChanged() {
321 String status
= getProcessStatus();
322 statusLabel
.setText(status
);
323 Boolean isEditable
= isEditable(status
);
324 run
.setEnabled(isEditable
);
325 remove
.setEnabled(isEditable
);
326 clear
.setEnabled(isEditable
);
327 // flowsViewer.getTree().setEnabled(isEditable);
328 if (status
.equals(ExecutionProcess
.COMPLETED
)
329 || status
.equals(ExecutionProcess
.ERROR
)) {
330 run
.setEnabled(true);
331 run
.setImage(SlcImages
.RELAUNCH
);
332 run
.setToolTipText("Relaunch");
336 /** Adds initial flows from the editor input if any */
337 protected void addInitialFlows() {
338 for (String path
: ((ProcessEditorInput
) getEditorInput())
339 .getInitialFlowPaths()) {
348 * the path of the flow
350 protected void addFlow(String path
) {
352 Node flowNode
= processNode
.getSession().getNode(path
);
353 Node realizedFlowNode
= processNode
.getNode(SLC_FLOW
).addNode(
355 realizedFlowNode
.addMixin(SlcTypes
.SLC_REALIZED_FLOW
);
356 Node address
= realizedFlowNode
.addNode(SLC_ADDRESS
,
357 NodeType
.NT_ADDRESS
);
358 address
.setProperty(Property
.JCR_PATH
, path
);
360 // copy spec attributes
362 if (flowNode
.hasProperty(SLC_SPEC
)) {
363 Node executionSpecNode
= flowNode
.getProperty(SLC_SPEC
)
365 specAttrsBase
= executionSpecNode
;
366 String executionSpecName
= executionSpecNode
.getProperty(
367 SLC_NAME
).getString();
368 realizedFlowNode
.setProperty(SLC_SPEC
, executionSpecName
);
370 specAttrsBase
= flowNode
;
372 specAttrs
: for (NodeIterator nit
= specAttrsBase
.getNodes(); nit
374 Node specAttrNode
= nit
.nextNode();
375 String attrName
= specAttrNode
.getName();
377 .isNodeType(SlcTypes
.SLC_EXECUTION_SPEC_ATTRIBUTE
))
379 Node realizedAttrNode
= realizedFlowNode
.addNode(specAttrNode
381 JcrUtils
.copy(specAttrNode
, realizedAttrNode
);
383 // ovveride with flow value
384 if (flowNode
.hasNode(attrName
)) {
385 // assuming this is a primitive
386 realizedAttrNode
.setProperty(SLC_VALUE
,
387 flowNode
.getNode(attrName
).getProperty(SLC_VALUE
)
392 flowsViewer
.refresh();
393 formPart
.markDirty();
394 } catch (RepositoryException e
) {
395 throw new SlcException("Cannot drop " + path
, e
);
399 @SuppressWarnings("unchecked")
400 protected void removeSelectedFlows() {
401 if (!flowsViewer
.getSelection().isEmpty()) {
402 Iterator
<Object
> it
= ((StructuredSelection
) flowsViewer
403 .getSelection()).iterator();
404 while (it
.hasNext()) {
405 Node node
= (Node
) it
.next();
408 } catch (RepositoryException e
) {
409 throw new ArgeoException("Cannot remove " + node
, e
);
412 flowsViewer
.refresh();
413 formPart
.markDirty();
417 protected void removeAllFlows() {
419 for (NodeIterator nit
= processNode
.getNode(SLC_FLOW
).getNodes(); nit
421 nit
.nextNode().remove();
423 flowsViewer
.refresh();
424 formPart
.markDirty();
425 } catch (RepositoryException e
) {
426 throw new ArgeoException("Cannot remove flows from " + processNode
,
431 public void commit(Boolean onSave
) {
433 statusLabel
.setText(getProcessStatus());
434 formPart
.commit(onSave
);
440 protected String
getProcessStatus() {
442 return processNode
.getProperty(SLC_STATUS
).getString();
443 } catch (RepositoryException e
) {
444 throw new SlcException("Cannot retrieve status for " + processNode
,
449 /** Optimization so that we don't call the node each time */
450 protected Boolean
isEditable(String status
) {
451 return status
.equals(ExecutionProcess
.NEW
)
452 || status
.equals(ExecutionProcess
.INITIALIZED
);
455 protected Boolean
isFinished(String status
) {
456 return status
.equals(ExecutionProcess
.COMPLETED
)
457 || status
.equals(ExecutionProcess
.ERROR
);
464 public void dispose() {
465 JcrUtils
.unregisterQuietly(processNode
, statusObserver
);
472 protected static Object
getAttributeSpecValue(Node specAttrNode
) {
474 if (specAttrNode
.isNodeType(SlcTypes
.SLC_PRIMITIVE_SPEC_ATTRIBUTE
)) {
475 if (!specAttrNode
.hasProperty(SLC_VALUE
))
477 String type
= specAttrNode
.getProperty(SLC_TYPE
).getString();
478 // TODO optimize based on data type?
479 Object value
= PrimitiveUtils
.convert(type
, specAttrNode
480 .getProperty(SLC_VALUE
).getString());
481 // log.debug(specAttrNode + ", type=" + type + ", value=" +
486 } catch (RepositoryException e
) {
487 throw new SlcException("Cannot get value", e
);
495 static class FlowsContentProvider
implements ITreeContentProvider
{
496 public Object
[] getElements(Object obj
) {
497 if (!(obj
instanceof Node
))
498 return new Object
[0];
501 Node node
= (Node
) obj
;
502 List
<Node
> children
= new ArrayList
<Node
>();
503 for (NodeIterator nit
= node
.getNode(SLC_FLOW
).getNodes(); nit
505 children
.add(nit
.nextNode());
506 return children
.toArray();
507 } catch (RepositoryException e
) {
508 throw new SlcException("Cannot list children of " + obj
, e
);
512 public void inputChanged(Viewer arg0
, Object arg1
, Object arg2
) {
515 public void dispose() {
518 public Object
[] getChildren(Object parentElement
) {
519 // no children for the time being
523 public Object
getParent(Object element
) {
527 public boolean hasChildren(Object element
) {
533 static class FlowsLabelProvider
extends ColumnLabelProvider
{
536 public String
getText(Object element
) {
537 Node node
= (Node
) element
;
539 if (node
.isNodeType(SlcTypes
.SLC_REALIZED_FLOW
)) {
540 if (node
.hasNode(SLC_ADDRESS
)) {
541 String path
= node
.getNode(SLC_ADDRESS
)
542 .getProperty(Property
.JCR_PATH
).getString();
543 return SlcJcrUtils
.flowExecutionModuleName(path
) + ":"
544 + SlcJcrUtils
.flowRelativePath(path
);
547 } catch (RepositoryException e
) {
548 throw new SlcException("Cannot display " + element
, e
);
550 return super.getText(element
);
554 public Image
getImage(Object element
) {
555 Node node
= (Node
) element
;
557 if (node
.isNodeType(SlcTypes
.SLC_REALIZED_FLOW
)) {
558 return SlcImages
.FLOW
;
560 } catch (RepositoryException e
) {
561 throw new SlcException("Cannot display " + element
, e
);
563 return super.getImage(element
);
568 /** Parameter view is updated each time a new line is selected */
569 class FlowsSelectionListener
implements ISelectionChangedListener
{
570 public void selectionChanged(SelectionChangedEvent evt
) {
571 if (evt
.getSelection().isEmpty()) {
572 valuesViewer
.setInput(getEditorSite());
575 Node realizedFlowNode
= (Node
) ((IStructuredSelection
) evt
576 .getSelection()).getFirstElement();
577 valuesViewer
.setInput(realizedFlowNode
);
581 /** Manages drop event. */
582 class FlowsDropListener
extends ViewerDropAdapter
{
584 public FlowsDropListener(Viewer viewer
) {
589 public boolean performDrop(Object data
) {
590 String path
= data
.toString();
592 // either a node or a whole directory was dragged
593 QueryManager qm
= processNode
.getSession().getWorkspace()
595 String statement
= "SELECT * FROM ["
596 + SlcTypes
.SLC_EXECUTION_FLOW
597 + "] WHERE ISDESCENDANTNODE(['" + path
598 + "']) OR ISSAMENODE(['" + path
+ "'])";
599 // log.debug(statement);
600 Query query
= qm
.createQuery(statement
, Query
.JCR_SQL2
);
603 SortedSet
<String
> paths
= new TreeSet
<String
>();
604 for (NodeIterator nit
= query
.execute().getNodes(); nit
606 paths
.add(nit
.nextNode().getPath());
609 for (String p
: paths
) {
613 } catch (RepositoryException e
) {
614 throw new SlcException("Cannot query flows under " + path
, e
);
619 public boolean validateDrop(Object target
, int operation
,
620 TransferData transferType
) {
621 return isEditable(getProcessStatus());
628 static class ValuesContentProvider
implements IStructuredContentProvider
{
630 public Object
[] getElements(Object inputElement
) {
631 if (!(inputElement
instanceof Node
))
632 return new Object
[0];
635 Node realizedFlowNode
= (Node
) inputElement
;
636 List
<Node
> specAttributes
= new ArrayList
<Node
>();
637 specAttrs
: for (NodeIterator nit
= realizedFlowNode
.getNodes(); nit
639 Node specAttrNode
= nit
.nextNode();
641 .isNodeType(SlcTypes
.SLC_EXECUTION_SPEC_ATTRIBUTE
))
643 specAttributes
.add(specAttrNode
);
645 return specAttributes
.toArray();
646 } catch (RepositoryException e
) {
647 throw new SlcException("Cannot get elements", e
);
651 public void dispose() {
654 public void inputChanged(Viewer viewer
, Object oldInput
, Object newInput
) {
658 class ValuesEditingSupport
extends EditingSupport
{
659 private final TableViewer tableViewer
;
661 public ValuesEditingSupport(ColumnViewer viewer
) {
663 tableViewer
= (TableViewer
) viewer
;
667 protected CellEditor
getCellEditor(Object element
) {
669 Node specAttrNode
= (Node
) element
;
671 .isNodeType(SlcTypes
.SLC_PRIMITIVE_SPEC_ATTRIBUTE
))
672 return new TextCellEditor(tableViewer
.getTable());
674 } catch (RepositoryException e
) {
675 throw new SlcException("Cannot get celle editor", e
);
680 protected boolean canEdit(Object element
) {
682 Node specAttrNode
= (Node
) element
;
683 return !(specAttrNode
.getProperty(SLC_IS_IMMUTABLE
)
684 .getBoolean() || specAttrNode
.getProperty(
685 SLC_IS_CONSTANT
).getBoolean())
687 .isNodeType(SlcTypes
.SLC_PRIMITIVE_SPEC_ATTRIBUTE
);
688 } catch (RepositoryException e
) {
689 throw new SlcException("Cannot check canEdit", e
);
694 protected Object
getValue(Object element
) {
695 Node specAttrNode
= (Node
) element
;
697 Object value
= getAttributeSpecValue(specAttrNode
);
699 throw new SlcException("Unsupported attribute " + element
);
701 .isNodeType(SlcTypes
.SLC_PRIMITIVE_SPEC_ATTRIBUTE
))
702 return value
.toString();
704 } catch (RepositoryException e
) {
705 throw new SlcException("Cannot get value for " + element
, e
);
710 protected void setValue(Object element
, Object value
) {
712 Node specAttrNode
= (Node
) element
;
714 .isNodeType(SlcTypes
.SLC_PRIMITIVE_SPEC_ATTRIBUTE
)) {
715 String type
= specAttrNode
.getProperty(SLC_TYPE
)
717 SlcJcrUtils
.setPrimitiveAsProperty(specAttrNode
, SLC_VALUE
,
719 valuesViewer
.refresh();
720 formPart
.markDirty();
722 } catch (RepositoryException e
) {
723 throw new SlcException("Cannot get celle editor", e
);