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
.NodeIterator
;
11 import javax
.jcr
.Property
;
12 import javax
.jcr
.RepositoryException
;
13 import javax
.jcr
.nodetype
.NodeType
;
14 import javax
.jcr
.observation
.Event
;
15 import javax
.jcr
.observation
.EventListener
;
16 import javax
.jcr
.observation
.ObservationManager
;
17 import javax
.jcr
.query
.Query
;
18 import javax
.jcr
.query
.QueryManager
;
20 import org
.argeo
.ArgeoException
;
21 import org
.argeo
.eclipse
.ui
.jcr
.AsyncUiEventListener
;
22 import org
.argeo
.jcr
.JcrUtils
;
23 import org
.argeo
.slc
.SlcException
;
24 import org
.argeo
.slc
.client
.ui
.SlcImages
;
25 import org
.argeo
.slc
.core
.execution
.PrimitiveUtils
;
26 import org
.argeo
.slc
.execution
.ExecutionProcess
;
27 import org
.argeo
.slc
.jcr
.SlcJcrUtils
;
28 import org
.argeo
.slc
.jcr
.SlcNames
;
29 import org
.argeo
.slc
.jcr
.SlcTypes
;
30 import org
.eclipse
.jface
.viewers
.CellEditor
;
31 import org
.eclipse
.jface
.viewers
.ColumnLabelProvider
;
32 import org
.eclipse
.jface
.viewers
.ColumnViewer
;
33 import org
.eclipse
.jface
.viewers
.EditingSupport
;
34 import org
.eclipse
.jface
.viewers
.ISelectionChangedListener
;
35 import org
.eclipse
.jface
.viewers
.IStructuredContentProvider
;
36 import org
.eclipse
.jface
.viewers
.IStructuredSelection
;
37 import org
.eclipse
.jface
.viewers
.ITreeContentProvider
;
38 import org
.eclipse
.jface
.viewers
.SelectionChangedEvent
;
39 import org
.eclipse
.jface
.viewers
.StructuredSelection
;
40 import org
.eclipse
.jface
.viewers
.TableViewer
;
41 import org
.eclipse
.jface
.viewers
.TableViewerColumn
;
42 import org
.eclipse
.jface
.viewers
.TextCellEditor
;
43 import org
.eclipse
.jface
.viewers
.TreeViewer
;
44 import org
.eclipse
.jface
.viewers
.Viewer
;
45 import org
.eclipse
.jface
.viewers
.ViewerDropAdapter
;
46 import org
.eclipse
.swt
.SWT
;
47 import org
.eclipse
.swt
.custom
.SashForm
;
48 import org
.eclipse
.swt
.dnd
.DND
;
49 import org
.eclipse
.swt
.dnd
.TextTransfer
;
50 import org
.eclipse
.swt
.dnd
.Transfer
;
51 import org
.eclipse
.swt
.dnd
.TransferData
;
52 import org
.eclipse
.swt
.events
.SelectionEvent
;
53 import org
.eclipse
.swt
.events
.SelectionListener
;
54 import org
.eclipse
.swt
.graphics
.Image
;
55 import org
.eclipse
.swt
.layout
.FillLayout
;
56 import org
.eclipse
.swt
.layout
.GridData
;
57 import org
.eclipse
.swt
.layout
.GridLayout
;
58 import org
.eclipse
.swt
.layout
.RowData
;
59 import org
.eclipse
.swt
.layout
.RowLayout
;
60 import org
.eclipse
.swt
.widgets
.Button
;
61 import org
.eclipse
.swt
.widgets
.Composite
;
62 import org
.eclipse
.swt
.widgets
.Label
;
63 import org
.eclipse
.swt
.widgets
.Table
;
64 import org
.eclipse
.ui
.forms
.AbstractFormPart
;
65 import org
.eclipse
.ui
.forms
.IManagedForm
;
66 import org
.eclipse
.ui
.forms
.editor
.FormPage
;
67 import org
.eclipse
.ui
.forms
.widgets
.FormToolkit
;
68 import org
.eclipse
.ui
.forms
.widgets
.ScrolledForm
;
70 /** Definition of the process. */
71 public class ProcessBuilderPage
extends FormPage
implements SlcNames
{
72 public final static String ID
= "processBuilderPage";
73 // private final static Log log =
74 // LogFactory.getLog(ProcessBuilderPage.class);
76 private Node processNode
;
78 private TreeViewer flowsViewer
;
79 private TableViewer valuesViewer
;
80 private Label statusLabel
;
82 private Button remove
;
85 private AbstractFormPart formPart
;
86 private EventListener statusObserver
;
88 public ProcessBuilderPage(ProcessEditor editor
, Node processNode
) {
89 super(editor
, ID
, "Definition");
90 this.processNode
= processNode
;
94 protected void createFormContent(IManagedForm mf
) {
96 ScrolledForm form
= mf
.getForm();
97 form
.setExpandHorizontal(true);
98 form
.setExpandVertical(true);
99 form
.setText("Process " + processNode
.getName());
100 GridLayout mainLayout
= new GridLayout(1, true);
101 form
.getBody().setLayout(mainLayout
);
103 createControls(form
.getBody());
104 createBuilder(form
.getBody());
107 formPart
= new AbstractFormPart() {
110 getManagedForm().addPart(formPart
);
113 statusObserver
= new AsyncUiEventListener(form
.getDisplay()) {
114 protected void onEventInUiThread(List
<Event
> events
) {
118 ObservationManager observationManager
= processNode
.getSession()
119 .getWorkspace().getObservationManager();
120 observationManager
.addEventListener(statusObserver
,
121 Event
.PROPERTY_CHANGED
, processNode
.getPath(), true, null,
124 // make sure all controls are in line with status
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())) {
148 ((ProcessEditor
) getEditor()).relaunch();
149 } else if (isRunning(getProcessStatus())) {
150 ((ProcessEditor
) getEditor()).kill();
152 ((ProcessEditor
) getEditor()).process();
156 public void widgetDefaultSelected(SelectionEvent e
) {
161 remove
= tk
.createButton(controls
, null, SWT
.PUSH
);
162 remove
.setImage(SlcImages
.REMOVE_ONE
);
163 remove
.setToolTipText("Remove selected flows");
164 remove
.addSelectionListener(new SelectionListener() {
165 public void widgetSelected(SelectionEvent e
) {
166 removeSelectedFlows();
169 public void widgetDefaultSelected(SelectionEvent e
) {
174 clear
= tk
.createButton(controls
, null, SWT
.PUSH
);
175 clear
.setImage(SlcImages
.REMOVE_ALL
);
176 clear
.setToolTipText("Clear all flows");
177 clear
.addSelectionListener(new SelectionListener() {
178 public void widgetSelected(SelectionEvent e
) {
182 public void widgetDefaultSelected(SelectionEvent e
) {
187 Composite statusComposite
= tk
.createComposite(controls
);
188 RowData rowData
= new RowData();
191 statusComposite
.setLayoutData(rowData
);
192 statusComposite
.setLayout(new FillLayout());
193 statusLabel
= tk
.createLabel(statusComposite
, getProcessStatus());
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 /** Reflects a status change */
282 protected void statusChanged() {
283 String status
= getProcessStatus();
284 statusLabel
.setText(status
);
285 Boolean isEditable
= isEditable(status
);
286 run
.setEnabled(status
.equals(ExecutionProcess
.RUNNING
) || isEditable
);
287 remove
.setEnabled(isEditable
);
288 clear
.setEnabled(isEditable
);
289 // flowsViewer.getTree().setEnabled(isEditable);
290 if (status
.equals(ExecutionProcess
.RUNNING
)) {
291 run
.setEnabled(true);
292 run
.setImage(SlcImages
.KILL
);
293 run
.setToolTipText("Kill");
294 } else if (isFinished(status
)) {
295 run
.setEnabled(true);
296 run
.setImage(SlcImages
.RELAUNCH
);
297 run
.setToolTipText("Relaunch");
300 if (flowsViewer
!= null)
301 flowsViewer
.refresh();
304 /** Adds initial flows from the editor input if any */
305 protected void addInitialFlows() {
306 for (String path
: ((ProcessEditorInput
) getEditorInput())
307 .getInitialFlowPaths()) {
316 * the path of the flow
318 protected void addFlow(String path
) {
320 Node flowNode
= processNode
.getSession().getNode(path
);
321 Node realizedFlowNode
= processNode
.getNode(SLC_FLOW
).addNode(
323 realizedFlowNode
.addMixin(SlcTypes
.SLC_REALIZED_FLOW
);
324 Node address
= realizedFlowNode
.addNode(SLC_ADDRESS
,
325 NodeType
.NT_ADDRESS
);
326 address
.setProperty(Property
.JCR_PATH
, path
);
328 // copy spec attributes
330 if (flowNode
.hasProperty(SLC_SPEC
)) {
331 Node executionSpecNode
= flowNode
.getProperty(SLC_SPEC
)
333 specAttrsBase
= executionSpecNode
;
334 String executionSpecName
= executionSpecNode
.getProperty(
335 SLC_NAME
).getString();
336 realizedFlowNode
.setProperty(SLC_SPEC
, executionSpecName
);
338 specAttrsBase
= flowNode
;
340 specAttrs
: for (NodeIterator nit
= specAttrsBase
.getNodes(); nit
342 Node specAttrNode
= nit
.nextNode();
343 String attrName
= specAttrNode
.getName();
345 .isNodeType(SlcTypes
.SLC_EXECUTION_SPEC_ATTRIBUTE
))
347 Node realizedAttrNode
= realizedFlowNode
.addNode(specAttrNode
349 JcrUtils
.copy(specAttrNode
, realizedAttrNode
);
351 // ovveride with flow value
352 if (flowNode
.hasNode(attrName
)) {
353 // assuming this is a primitive
354 realizedAttrNode
.setProperty(SLC_VALUE
,
355 flowNode
.getNode(attrName
).getProperty(SLC_VALUE
)
360 flowsViewer
.refresh();
361 formPart
.markDirty();
362 } catch (RepositoryException e
) {
363 throw new SlcException("Cannot drop " + path
, e
);
367 @SuppressWarnings("unchecked")
368 protected void removeSelectedFlows() {
369 if (!flowsViewer
.getSelection().isEmpty()) {
370 Iterator
<Object
> it
= ((StructuredSelection
) flowsViewer
371 .getSelection()).iterator();
372 while (it
.hasNext()) {
373 Node node
= (Node
) it
.next();
376 } catch (RepositoryException e
) {
377 throw new ArgeoException("Cannot remove " + node
, e
);
380 flowsViewer
.refresh();
381 formPart
.markDirty();
385 protected void removeAllFlows() {
387 for (NodeIterator nit
= processNode
.getNode(SLC_FLOW
).getNodes(); nit
389 nit
.nextNode().remove();
391 flowsViewer
.refresh();
392 formPart
.markDirty();
393 } catch (RepositoryException e
) {
394 throw new ArgeoException("Cannot remove flows from " + processNode
,
399 public void commit(Boolean onSave
) {
401 statusLabel
.setText(getProcessStatus());
402 formPart
.commit(onSave
);
408 protected String
getProcessStatus() {
410 return processNode
.getProperty(SLC_STATUS
).getString();
411 } catch (RepositoryException e
) {
412 throw new SlcException("Cannot retrieve status for " + processNode
,
417 /** Optimization so that we don't call the node each time */
418 protected static Boolean
isEditable(String status
) {
419 return status
.equals(ExecutionProcess
.NEW
)
420 || status
.equals(ExecutionProcess
.INITIALIZED
);
423 protected static Boolean
isFinished(String status
) {
424 return status
.equals(ExecutionProcess
.COMPLETED
)
425 || status
.equals(ExecutionProcess
.ERROR
)
426 || status
.equals(ExecutionProcess
.KILLED
);
429 protected static Boolean
isRunning(String status
) {
430 return status
.equals(ExecutionProcess
.RUNNING
);
437 public void dispose() {
438 JcrUtils
.unregisterQuietly(processNode
, statusObserver
);
445 protected static Object
getAttributeSpecValue(Node specAttrNode
) {
447 if (specAttrNode
.isNodeType(SlcTypes
.SLC_PRIMITIVE_SPEC_ATTRIBUTE
)) {
448 if (!specAttrNode
.hasProperty(SLC_VALUE
))
450 String type
= specAttrNode
.getProperty(SLC_TYPE
).getString();
451 // TODO optimize based on data type?
452 Object value
= PrimitiveUtils
.convert(type
, specAttrNode
453 .getProperty(SLC_VALUE
).getString());
454 // log.debug(specAttrNode + ", type=" + type + ", value=" +
459 } catch (RepositoryException e
) {
460 throw new SlcException("Cannot get value", e
);
468 static class FlowsContentProvider
implements ITreeContentProvider
{
469 public Object
[] getElements(Object obj
) {
470 if (!(obj
instanceof Node
))
471 return new Object
[0];
474 Node node
= (Node
) obj
;
475 List
<Node
> children
= new ArrayList
<Node
>();
476 for (NodeIterator nit
= node
.getNode(SLC_FLOW
).getNodes(); nit
478 children
.add(nit
.nextNode());
479 return children
.toArray();
480 } catch (RepositoryException e
) {
481 throw new SlcException("Cannot list children of " + obj
, e
);
485 public void inputChanged(Viewer arg0
, Object arg1
, Object arg2
) {
488 public void dispose() {
491 public Object
[] getChildren(Object parentElement
) {
492 // no children for the time being
496 public Object
getParent(Object element
) {
500 public boolean hasChildren(Object element
) {
506 static class FlowsLabelProvider
extends ColumnLabelProvider
{
509 public String
getText(Object element
) {
510 Node node
= (Node
) element
;
512 if (node
.isNodeType(SlcTypes
.SLC_REALIZED_FLOW
)) {
513 if (node
.hasNode(SLC_ADDRESS
)) {
514 String path
= node
.getNode(SLC_ADDRESS
)
515 .getProperty(Property
.JCR_PATH
).getString();
516 return SlcJcrUtils
.flowExecutionModuleName(path
) + ":"
517 + SlcJcrUtils
.flowRelativePath(path
);
520 } catch (RepositoryException e
) {
521 throw new SlcException("Cannot display " + element
, e
);
523 return super.getText(element
);
527 public Image
getImage(Object element
) {
528 Node node
= (Node
) element
;
530 if (node
.isNodeType(SlcTypes
.SLC_REALIZED_FLOW
)) {
531 if (node
.hasProperty(SLC_STATUS
)) {
532 String status
= node
.getProperty(SLC_STATUS
)
534 // TODO: factorize with process view ?
535 if (status
.equals(ExecutionProcess
.RUNNING
))
536 return SlcImages
.PROCESS_RUNNING
;
537 else if (status
.equals(ExecutionProcess
.ERROR
)
538 || status
.equals(ExecutionProcess
.KILLED
))
539 return SlcImages
.PROCESS_ERROR
;
540 else if (status
.equals(ExecutionProcess
.COMPLETED
))
541 return SlcImages
.PROCESS_COMPLETED
;
543 return SlcImages
.FLOW
;
545 } catch (RepositoryException e
) {
546 throw new SlcException("Cannot display " + element
, e
);
548 return super.getImage(element
);
553 /** Parameter view is updated each time a new line is selected */
554 class FlowsSelectionListener
implements ISelectionChangedListener
{
555 public void selectionChanged(SelectionChangedEvent evt
) {
556 if (evt
.getSelection().isEmpty()) {
557 valuesViewer
.setInput(getEditorSite());
560 Node realizedFlowNode
= (Node
) ((IStructuredSelection
) evt
561 .getSelection()).getFirstElement();
562 valuesViewer
.setInput(realizedFlowNode
);
566 /** Manages drop event. */
567 class FlowsDropListener
extends ViewerDropAdapter
{
569 public FlowsDropListener(Viewer viewer
) {
574 public boolean performDrop(Object data
) {
575 String path
= data
.toString();
577 // either a node or a whole directory was dragged
578 QueryManager qm
= processNode
.getSession().getWorkspace()
580 String statement
= "SELECT * FROM ["
581 + SlcTypes
.SLC_EXECUTION_FLOW
582 + "] WHERE ISDESCENDANTNODE(['" + path
583 + "']) OR ISSAMENODE(['" + path
+ "'])";
584 // log.debug(statement);
585 Query query
= qm
.createQuery(statement
, Query
.JCR_SQL2
);
588 SortedSet
<String
> paths
= new TreeSet
<String
>();
589 for (NodeIterator nit
= query
.execute().getNodes(); nit
591 paths
.add(nit
.nextNode().getPath());
594 for (String p
: paths
) {
598 } catch (RepositoryException e
) {
599 throw new SlcException("Cannot query flows under " + path
, e
);
604 public boolean validateDrop(Object target
, int operation
,
605 TransferData transferType
) {
606 return isEditable(getProcessStatus());
613 static class ValuesContentProvider
implements IStructuredContentProvider
{
615 public Object
[] getElements(Object inputElement
) {
616 if (!(inputElement
instanceof Node
))
617 return new Object
[0];
620 Node realizedFlowNode
= (Node
) inputElement
;
621 List
<Node
> specAttributes
= new ArrayList
<Node
>();
622 specAttrs
: for (NodeIterator nit
= realizedFlowNode
.getNodes(); nit
624 Node specAttrNode
= nit
.nextNode();
626 .isNodeType(SlcTypes
.SLC_EXECUTION_SPEC_ATTRIBUTE
))
628 specAttributes
.add(specAttrNode
);
630 return specAttributes
.toArray();
631 } catch (RepositoryException e
) {
632 throw new SlcException("Cannot get elements", e
);
636 public void dispose() {
639 public void inputChanged(Viewer viewer
, Object oldInput
, Object newInput
) {
643 class ValuesEditingSupport
extends EditingSupport
{
644 private final TableViewer tableViewer
;
646 public ValuesEditingSupport(ColumnViewer viewer
) {
648 tableViewer
= (TableViewer
) viewer
;
652 protected CellEditor
getCellEditor(Object element
) {
654 Node specAttrNode
= (Node
) element
;
656 .isNodeType(SlcTypes
.SLC_PRIMITIVE_SPEC_ATTRIBUTE
))
657 return new TextCellEditor(tableViewer
.getTable());
659 } catch (RepositoryException e
) {
660 throw new SlcException("Cannot get celle editor", e
);
665 protected boolean canEdit(Object element
) {
667 Node specAttrNode
= (Node
) element
;
668 return !(specAttrNode
.getProperty(SLC_IS_IMMUTABLE
)
669 .getBoolean() || specAttrNode
.getProperty(
670 SLC_IS_CONSTANT
).getBoolean())
672 .isNodeType(SlcTypes
.SLC_PRIMITIVE_SPEC_ATTRIBUTE
);
673 } catch (RepositoryException e
) {
674 throw new SlcException("Cannot check canEdit", e
);
679 protected Object
getValue(Object element
) {
680 Node specAttrNode
= (Node
) element
;
682 Object value
= getAttributeSpecValue(specAttrNode
);
684 throw new SlcException("Unsupported attribute " + element
);
686 .isNodeType(SlcTypes
.SLC_PRIMITIVE_SPEC_ATTRIBUTE
))
687 return value
.toString();
689 } catch (RepositoryException e
) {
690 throw new SlcException("Cannot get value for " + element
, e
);
695 protected void setValue(Object element
, Object value
) {
697 Node specAttrNode
= (Node
) element
;
699 .isNodeType(SlcTypes
.SLC_PRIMITIVE_SPEC_ATTRIBUTE
)) {
700 String type
= specAttrNode
.getProperty(SLC_TYPE
)
702 SlcJcrUtils
.setPrimitiveAsProperty(specAttrNode
, SLC_VALUE
,
704 valuesViewer
.refresh();
705 formPart
.markDirty();
707 } catch (RepositoryException e
) {
708 throw new SlcException("Cannot get celle editor", e
);