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
.EventIterator
;
18 import javax
.jcr
.observation
.EventListener
;
19 import javax
.jcr
.observation
.ObservationManager
;
20 import javax
.jcr
.query
.Query
;
21 import javax
.jcr
.query
.QueryManager
;
23 import org
.argeo
.ArgeoException
;
24 import org
.argeo
.eclipse
.ui
.jcr
.AsyncUiEventListener
;
25 import org
.argeo
.jcr
.JcrUtils
;
26 import org
.argeo
.slc
.SlcException
;
27 import org
.argeo
.slc
.client
.ui
.SlcImages
;
28 import org
.argeo
.slc
.core
.execution
.PrimitiveUtils
;
29 import org
.argeo
.slc
.execution
.ExecutionProcess
;
30 import org
.argeo
.slc
.jcr
.SlcJcrUtils
;
31 import org
.argeo
.slc
.jcr
.SlcNames
;
32 import org
.argeo
.slc
.jcr
.SlcTypes
;
33 import org
.eclipse
.jface
.viewers
.CellEditor
;
34 import org
.eclipse
.jface
.viewers
.ColumnLabelProvider
;
35 import org
.eclipse
.jface
.viewers
.ColumnViewer
;
36 import org
.eclipse
.jface
.viewers
.EditingSupport
;
37 import org
.eclipse
.jface
.viewers
.ISelectionChangedListener
;
38 import org
.eclipse
.jface
.viewers
.IStructuredContentProvider
;
39 import org
.eclipse
.jface
.viewers
.IStructuredSelection
;
40 import org
.eclipse
.jface
.viewers
.ITreeContentProvider
;
41 import org
.eclipse
.jface
.viewers
.SelectionChangedEvent
;
42 import org
.eclipse
.jface
.viewers
.StructuredSelection
;
43 import org
.eclipse
.jface
.viewers
.TableViewer
;
44 import org
.eclipse
.jface
.viewers
.TableViewerColumn
;
45 import org
.eclipse
.jface
.viewers
.TextCellEditor
;
46 import org
.eclipse
.jface
.viewers
.TreeViewer
;
47 import org
.eclipse
.jface
.viewers
.Viewer
;
48 import org
.eclipse
.jface
.viewers
.ViewerDropAdapter
;
49 import org
.eclipse
.swt
.SWT
;
50 import org
.eclipse
.swt
.custom
.SashForm
;
51 import org
.eclipse
.swt
.dnd
.DND
;
52 import org
.eclipse
.swt
.dnd
.TextTransfer
;
53 import org
.eclipse
.swt
.dnd
.Transfer
;
54 import org
.eclipse
.swt
.dnd
.TransferData
;
55 import org
.eclipse
.swt
.events
.SelectionEvent
;
56 import org
.eclipse
.swt
.events
.SelectionListener
;
57 import org
.eclipse
.swt
.graphics
.Image
;
58 import org
.eclipse
.swt
.layout
.FillLayout
;
59 import org
.eclipse
.swt
.layout
.GridData
;
60 import org
.eclipse
.swt
.layout
.GridLayout
;
61 import org
.eclipse
.swt
.layout
.RowData
;
62 import org
.eclipse
.swt
.layout
.RowLayout
;
63 import org
.eclipse
.swt
.widgets
.Button
;
64 import org
.eclipse
.swt
.widgets
.Composite
;
65 import org
.eclipse
.swt
.widgets
.Label
;
66 import org
.eclipse
.swt
.widgets
.Table
;
67 import org
.eclipse
.ui
.IWorkbenchPage
;
68 import org
.eclipse
.ui
.PlatformUI
;
69 import org
.eclipse
.ui
.forms
.AbstractFormPart
;
70 import org
.eclipse
.ui
.forms
.IManagedForm
;
71 import org
.eclipse
.ui
.forms
.editor
.FormPage
;
72 import org
.eclipse
.ui
.forms
.widgets
.FormToolkit
;
73 import org
.eclipse
.ui
.forms
.widgets
.ScrolledForm
;
75 public class ProcessBuilderPage
extends FormPage
implements SlcNames
{
76 public final static String ID
= "processBuilderPage";
77 // private final static Log log =
78 // LogFactory.getLog(ProcessBuilderPage.class);
80 private Node processNode
;
82 private TreeViewer flowsViewer
;
83 private TableViewer valuesViewer
;
84 private Label statusLabel
;
86 private Button remove
;
89 private AbstractFormPart formPart
;
90 private EventListener statusObserver
;
92 public ProcessBuilderPage(ProcessEditor editor
, Node processNode
) {
93 super(editor
, ID
, "Definition");
94 this.processNode
= processNode
;
98 protected void createFormContent(IManagedForm mf
) {
100 ScrolledForm form
= mf
.getForm();
101 form
.setExpandHorizontal(true);
102 form
.setExpandVertical(true);
103 form
.setText("Process " + processNode
.getName());
104 GridLayout mainLayout
= new GridLayout(1, true);
105 form
.getBody().setLayout(mainLayout
);
107 createControls(form
.getBody());
108 createBuilder(form
.getBody());
111 formPart
= new AbstractFormPart() {
114 getManagedForm().addPart(formPart
);
117 statusObserver
= new AsyncUiEventListener() {
118 protected void onEventInUiThread(EventIterator events
) {
122 ObservationManager observationManager
= processNode
.getSession()
123 .getWorkspace().getObservationManager();
124 observationManager
.addEventListener(statusObserver
,
125 Event
.PROPERTY_CHANGED
, processNode
.getPath(), true, null,
131 } catch (RepositoryException e
) {
132 throw new ArgeoException("Cannot create form content", e
);
136 protected void createControls(Composite parent
) {
137 FormToolkit tk
= getManagedForm().getToolkit();
139 Composite controls
= tk
.createComposite(parent
);
140 controls
.setLayout(new RowLayout());
141 controls
.setLayoutData(new GridData(SWT
.FILL
, SWT
.FILL
, true, false));
143 run
= tk
.createButton(controls
, null, SWT
.PUSH
);
144 run
.setToolTipText("Run");
145 run
.setImage(SlcImages
.LAUNCH
);
146 run
.addSelectionListener(new SelectionListener() {
147 public void widgetSelected(SelectionEvent e
) {
148 if (isFinished(getProcessStatus())) {
151 ((ProcessEditor
) getEditor()).process();
155 public void widgetDefaultSelected(SelectionEvent e
) {
160 remove
= tk
.createButton(controls
, null, SWT
.PUSH
);
161 remove
.setImage(SlcImages
.REMOVE_ONE
);
162 remove
.setToolTipText("Remove selected flows");
163 remove
.addSelectionListener(new SelectionListener() {
164 public void widgetSelected(SelectionEvent e
) {
165 removeSelectedFlows();
168 public void widgetDefaultSelected(SelectionEvent e
) {
173 clear
= tk
.createButton(controls
, null, SWT
.PUSH
);
174 clear
.setImage(SlcImages
.REMOVE_ALL
);
175 clear
.setToolTipText("Clear all flows");
176 clear
.addSelectionListener(new SelectionListener() {
177 public void widgetSelected(SelectionEvent e
) {
181 public void widgetDefaultSelected(SelectionEvent e
) {
186 Composite statusComposite
= tk
.createComposite(controls
);
187 RowData rowData
= new RowData();
190 statusComposite
.setLayoutData(rowData
);
191 statusComposite
.setLayout(new FillLayout());
192 statusLabel
= tk
.createLabel(statusComposite
, getProcessStatus());
194 // make sure all controls are in line with status
198 protected void createBuilder(Composite parent
) {
199 FormToolkit tk
= getManagedForm().getToolkit();
200 SashForm sashForm
= new SashForm(parent
, SWT
.HORIZONTAL
);
201 sashForm
.setSashWidth(4);
202 GridData sahFormGd
= new GridData(SWT
.FILL
, SWT
.FILL
, true, true);
203 sahFormGd
.widthHint
= 400;
204 sashForm
.setLayoutData(sahFormGd
);
206 Composite flowsComposite
= tk
.createComposite(sashForm
);
207 flowsComposite
.setLayout(new GridLayout(1, false));
209 flowsViewer
= new TreeViewer(flowsComposite
);
210 flowsViewer
.getTree().setLayoutData(
211 new GridData(SWT
.FILL
, SWT
.FILL
, true, true));
212 flowsViewer
.setLabelProvider(new FlowsLabelProvider());
213 flowsViewer
.setContentProvider(new FlowsContentProvider());
214 flowsViewer
.addSelectionChangedListener(new FlowsSelectionListener());
216 int operations
= DND
.DROP_COPY
| DND
.DROP_MOVE
;
217 Transfer
[] tt
= new Transfer
[] { TextTransfer
.getInstance() };
218 flowsViewer
.addDropSupport(operations
, tt
, new FlowsDropListener(
221 flowsViewer
.setInput(getEditorSite());
222 flowsViewer
.setInput(processNode
);
224 Composite valuesComposite
= tk
.createComposite(sashForm
);
225 valuesComposite
.setLayout(new GridLayout(1, false));
227 valuesViewer
= new TableViewer(valuesComposite
);
228 GridData valuedGd
= new GridData(SWT
.FILL
, SWT
.FILL
, true, true);
229 // valuedGd.widthHint = 200;
230 valuesViewer
.getTable().setLayoutData(valuedGd
);
231 valuesViewer
.setContentProvider(new ValuesContentProvider());
232 initializeValuesViewer(valuesViewer
);
233 sashForm
.setWeights(getWeights());
234 valuesViewer
.setInput(getEditorSite());
237 /** Creates the columns of the values viewer */
238 protected void initializeValuesViewer(TableViewer viewer
) {
239 String
[] titles
= { "Name", "Value" };
240 int[] bounds
= { 200, 100 };
242 for (int i
= 0; i
< titles
.length
; i
++) {
243 TableViewerColumn column
= new TableViewerColumn(viewer
, SWT
.NONE
);
244 column
.getColumn().setText(titles
[i
]);
245 column
.getColumn().setWidth(bounds
[i
]);
246 column
.getColumn().setResizable(true);
247 column
.getColumn().setMoveable(true);
249 column
.setLabelProvider(new ColumnLabelProvider() {
250 public String
getText(Object element
) {
252 Node specAttrNode
= (Node
) element
;
253 return specAttrNode
.getName();
254 } catch (RepositoryException e
) {
255 throw new SlcException("Cannot get value", e
);
260 column
.setLabelProvider(new ColumnLabelProvider() {
261 public String
getText(Object element
) {
262 Object obj
= getAttributeSpecValue((Node
) element
);
263 return obj
!= null ? obj
.toString() : "";
266 column
.setEditingSupport(new ValuesEditingSupport(viewer
));
270 Table table
= viewer
.getTable();
271 table
.setHeaderVisible(false);
272 table
.setLinesVisible(true);
275 protected int[] getWeights() {
276 return new int[] { 50, 50 };
282 /** Opens a new editor with a copy of this process */
283 protected void relaunch() {
285 Node duplicatedNode
= duplicateProcess();
286 IWorkbenchPage activePage
= PlatformUI
.getWorkbench()
287 .getActiveWorkbenchWindow().getActivePage();
288 activePage
.openEditor(
289 new ProcessEditorInput(duplicatedNode
.getPath()),
291 getEditor().close(false);
292 } catch (Exception e1
) {
293 throw new SlcException("Cannot relaunch " + processNode
, e1
);
297 /** Duplicates the process */
298 protected Node
duplicateProcess() {
300 Session session
= processNode
.getSession();
301 String uuid
= UUID
.randomUUID().toString();
302 String destPath
= SlcJcrUtils
.createExecutionProcessPath(uuid
);
303 Node newNode
= JcrUtils
.mkdirs(session
, destPath
,
304 SlcTypes
.SLC_PROCESS
);
305 JcrUtils
.copy(processNode
, newNode
);
306 // session.getWorkspace().copy(processNode.getPath(), destPath);
307 // Node newNode = session.getNode(destPath);
308 // make sure that we kept the mixins
309 // newNode.addMixin(NodeType.MIX_CREATED);
310 // newNode.addMixin(NodeType.MIX_LAST_MODIFIED);
311 newNode
.setProperty(SLC_UUID
, uuid
);
312 newNode
.setProperty(SLC_STATUS
, ExecutionProcess
.INITIALIZED
);
315 } catch (RepositoryException e
) {
316 throw new SlcException("Cannot duplicate process", e
);
320 /** Reflects a status change */
321 protected void statusChanged() {
322 String status
= getProcessStatus();
323 statusLabel
.setText(status
);
324 Boolean isEditable
= isEditable(status
);
325 run
.setEnabled(isEditable
);
326 remove
.setEnabled(isEditable
);
327 clear
.setEnabled(isEditable
);
328 // flowsViewer.getTree().setEnabled(isEditable);
329 if (status
.equals(ExecutionProcess
.COMPLETED
)
330 || status
.equals(ExecutionProcess
.ERROR
)) {
331 run
.setEnabled(true);
332 run
.setImage(SlcImages
.RELAUNCH
);
333 run
.setToolTipText("Relaunch");
337 /** Adds initial flows from the editor input if any */
338 protected void addInitialFlows() {
339 for (String path
: ((ProcessEditorInput
) getEditorInput())
340 .getInitialFlowPaths()) {
349 * the path of the flow
351 protected void addFlow(String path
) {
353 Node flowNode
= processNode
.getSession().getNode(path
);
354 Node realizedFlowNode
= processNode
.getNode(SLC_FLOW
).addNode(
356 realizedFlowNode
.addMixin(SlcTypes
.SLC_REALIZED_FLOW
);
357 Node address
= realizedFlowNode
.addNode(SLC_ADDRESS
,
358 NodeType
.NT_ADDRESS
);
359 address
.setProperty(Property
.JCR_PATH
, path
);
361 // copy spec attributes
363 if (flowNode
.hasProperty(SLC_SPEC
)) {
364 Node executionSpecNode
= flowNode
.getProperty(SLC_SPEC
)
366 specAttrsBase
= executionSpecNode
;
367 String executionSpecName
= executionSpecNode
.getProperty(
368 SLC_NAME
).getString();
369 realizedFlowNode
.setProperty(SLC_SPEC
, executionSpecName
);
371 specAttrsBase
= flowNode
;
373 specAttrs
: for (NodeIterator nit
= specAttrsBase
.getNodes(); nit
375 Node specAttrNode
= nit
.nextNode();
376 String attrName
= specAttrNode
.getName();
378 .isNodeType(SlcTypes
.SLC_EXECUTION_SPEC_ATTRIBUTE
))
380 Node realizedAttrNode
= realizedFlowNode
.addNode(specAttrNode
382 JcrUtils
.copy(specAttrNode
, realizedAttrNode
);
384 // ovveride with flow value
385 if (flowNode
.hasNode(attrName
)) {
386 // assuming this is a primitive
387 realizedAttrNode
.setProperty(SLC_VALUE
,
388 flowNode
.getNode(attrName
).getProperty(SLC_VALUE
)
393 flowsViewer
.refresh();
394 formPart
.markDirty();
395 } catch (RepositoryException e
) {
396 throw new SlcException("Cannot drop " + path
, e
);
400 @SuppressWarnings("unchecked")
401 protected void removeSelectedFlows() {
402 if (!flowsViewer
.getSelection().isEmpty()) {
403 Iterator
<Object
> it
= ((StructuredSelection
) flowsViewer
404 .getSelection()).iterator();
405 while (it
.hasNext()) {
406 Node node
= (Node
) it
.next();
409 } catch (RepositoryException e
) {
410 throw new ArgeoException("Cannot remove " + node
, e
);
413 flowsViewer
.refresh();
414 formPart
.markDirty();
418 protected void removeAllFlows() {
420 for (NodeIterator nit
= processNode
.getNode(SLC_FLOW
).getNodes(); nit
422 nit
.nextNode().remove();
424 flowsViewer
.refresh();
425 formPart
.markDirty();
426 } catch (RepositoryException e
) {
427 throw new ArgeoException("Cannot remove flows from " + processNode
,
432 public void commit(Boolean onSave
) {
434 statusLabel
.setText(getProcessStatus());
435 formPart
.commit(onSave
);
441 protected String
getProcessStatus() {
443 return processNode
.getProperty(SLC_STATUS
).getString();
444 } catch (RepositoryException e
) {
445 throw new SlcException("Cannot retrieve status for " + processNode
,
450 /** Optimization so that we don't call the node each time */
451 protected Boolean
isEditable(String status
) {
452 return status
.equals(ExecutionProcess
.NEW
)
453 || status
.equals(ExecutionProcess
.INITIALIZED
);
456 protected Boolean
isFinished(String status
) {
457 return status
.equals(ExecutionProcess
.COMPLETED
)
458 || status
.equals(ExecutionProcess
.ERROR
);
465 public void dispose() {
466 JcrUtils
.unregisterQuietly(processNode
, statusObserver
);
473 protected static Object
getAttributeSpecValue(Node specAttrNode
) {
475 if (specAttrNode
.isNodeType(SlcTypes
.SLC_PRIMITIVE_SPEC_ATTRIBUTE
)) {
476 if (!specAttrNode
.hasProperty(SLC_VALUE
))
478 String type
= specAttrNode
.getProperty(SLC_TYPE
).getString();
479 // TODO optimize based on data type?
480 Object value
= PrimitiveUtils
.convert(type
, specAttrNode
481 .getProperty(SLC_VALUE
).getString());
482 // log.debug(specAttrNode + ", type=" + type + ", value=" +
487 } catch (RepositoryException e
) {
488 throw new SlcException("Cannot get value", e
);
496 static class FlowsContentProvider
implements ITreeContentProvider
{
497 public Object
[] getElements(Object obj
) {
498 if (!(obj
instanceof Node
))
499 return new Object
[0];
502 Node node
= (Node
) obj
;
503 List
<Node
> children
= new ArrayList
<Node
>();
504 for (NodeIterator nit
= node
.getNode(SLC_FLOW
).getNodes(); nit
506 children
.add(nit
.nextNode());
507 return children
.toArray();
508 } catch (RepositoryException e
) {
509 throw new SlcException("Cannot list children of " + obj
, e
);
513 public void inputChanged(Viewer arg0
, Object arg1
, Object arg2
) {
516 public void dispose() {
519 public Object
[] getChildren(Object parentElement
) {
520 // no children for the time being
524 public Object
getParent(Object element
) {
528 public boolean hasChildren(Object element
) {
534 static class FlowsLabelProvider
extends ColumnLabelProvider
{
537 public String
getText(Object element
) {
538 Node node
= (Node
) element
;
540 if (node
.isNodeType(SlcTypes
.SLC_REALIZED_FLOW
)) {
541 if (node
.hasNode(SLC_ADDRESS
)) {
542 String path
= node
.getNode(SLC_ADDRESS
)
543 .getProperty(Property
.JCR_PATH
).getString();
544 return SlcJcrUtils
.flowExecutionModuleName(path
) + ":"
545 + SlcJcrUtils
.flowRelativePath(path
);
548 } catch (RepositoryException e
) {
549 throw new SlcException("Cannot display " + element
, e
);
551 return super.getText(element
);
555 public Image
getImage(Object element
) {
556 Node node
= (Node
) element
;
558 if (node
.isNodeType(SlcTypes
.SLC_REALIZED_FLOW
)) {
559 return SlcImages
.FLOW
;
561 } catch (RepositoryException e
) {
562 throw new SlcException("Cannot display " + element
, e
);
564 return super.getImage(element
);
569 /** Parameter view is updated each time a new line is selected */
570 class FlowsSelectionListener
implements ISelectionChangedListener
{
571 public void selectionChanged(SelectionChangedEvent evt
) {
572 if (evt
.getSelection().isEmpty()) {
573 valuesViewer
.setInput(getEditorSite());
576 Node realizedFlowNode
= (Node
) ((IStructuredSelection
) evt
577 .getSelection()).getFirstElement();
578 valuesViewer
.setInput(realizedFlowNode
);
582 /** Manages drop event. */
583 class FlowsDropListener
extends ViewerDropAdapter
{
585 public FlowsDropListener(Viewer viewer
) {
590 public boolean performDrop(Object data
) {
591 String path
= data
.toString();
593 // either a node or a whole directory was dragged
594 QueryManager qm
= processNode
.getSession().getWorkspace()
596 String statement
= "SELECT * FROM ["
597 + SlcTypes
.SLC_EXECUTION_FLOW
598 + "] WHERE ISDESCENDANTNODE(['" + path
599 + "']) OR ISSAMENODE(['" + path
+ "'])";
600 // log.debug(statement);
601 Query query
= qm
.createQuery(statement
, Query
.JCR_SQL2
);
604 SortedSet
<String
> paths
= new TreeSet
<String
>();
605 for (NodeIterator nit
= query
.execute().getNodes(); nit
607 paths
.add(nit
.nextNode().getPath());
610 for (String p
: paths
) {
614 } catch (RepositoryException e
) {
615 throw new SlcException("Cannot query flows under " + path
, e
);
620 public boolean validateDrop(Object target
, int operation
,
621 TransferData transferType
) {
622 return isEditable(getProcessStatus());
629 static class ValuesContentProvider
implements IStructuredContentProvider
{
631 public Object
[] getElements(Object inputElement
) {
632 if (!(inputElement
instanceof Node
))
633 return new Object
[0];
636 Node realizedFlowNode
= (Node
) inputElement
;
637 List
<Node
> specAttributes
= new ArrayList
<Node
>();
638 specAttrs
: for (NodeIterator nit
= realizedFlowNode
.getNodes(); nit
640 Node specAttrNode
= nit
.nextNode();
642 .isNodeType(SlcTypes
.SLC_EXECUTION_SPEC_ATTRIBUTE
))
644 specAttributes
.add(specAttrNode
);
646 return specAttributes
.toArray();
647 } catch (RepositoryException e
) {
648 throw new SlcException("Cannot get elements", e
);
652 public void dispose() {
655 public void inputChanged(Viewer viewer
, Object oldInput
, Object newInput
) {
659 class ValuesEditingSupport
extends EditingSupport
{
660 private final TableViewer tableViewer
;
662 public ValuesEditingSupport(ColumnViewer viewer
) {
664 tableViewer
= (TableViewer
) viewer
;
668 protected CellEditor
getCellEditor(Object element
) {
670 Node specAttrNode
= (Node
) element
;
672 .isNodeType(SlcTypes
.SLC_PRIMITIVE_SPEC_ATTRIBUTE
))
673 return new TextCellEditor(tableViewer
.getTable());
675 } catch (RepositoryException e
) {
676 throw new SlcException("Cannot get celle editor", e
);
681 protected boolean canEdit(Object element
) {
683 Node specAttrNode
= (Node
) element
;
684 return !(specAttrNode
.getProperty(SLC_IS_IMMUTABLE
)
685 .getBoolean() || specAttrNode
.getProperty(
686 SLC_IS_CONSTANT
).getBoolean())
688 .isNodeType(SlcTypes
.SLC_PRIMITIVE_SPEC_ATTRIBUTE
);
689 } catch (RepositoryException e
) {
690 throw new SlcException("Cannot check canEdit", e
);
695 protected Object
getValue(Object element
) {
696 Node specAttrNode
= (Node
) element
;
698 Object value
= getAttributeSpecValue(specAttrNode
);
700 throw new SlcException("Unsupported attribute " + element
);
702 .isNodeType(SlcTypes
.SLC_PRIMITIVE_SPEC_ATTRIBUTE
))
703 return value
.toString();
705 } catch (RepositoryException e
) {
706 throw new SlcException("Cannot get value for " + element
, e
);
711 protected void setValue(Object element
, Object value
) {
713 Node specAttrNode
= (Node
) element
;
715 .isNodeType(SlcTypes
.SLC_PRIMITIVE_SPEC_ATTRIBUTE
)) {
716 String type
= specAttrNode
.getProperty(SLC_TYPE
)
718 SlcJcrUtils
.setPrimitiveAsProperty(specAttrNode
, SLC_VALUE
,
720 valuesViewer
.refresh();
721 formPart
.markDirty();
723 } catch (RepositoryException e
) {
724 throw new SlcException("Cannot get celle editor", e
);