1 package org
.argeo
.slc
.client
.ui
.editors
;
3 import java
.util
.ArrayList
;
4 import java
.util
.Iterator
;
9 import javax
.jcr
.NodeIterator
;
10 import javax
.jcr
.Property
;
11 import javax
.jcr
.RepositoryException
;
12 import javax
.jcr
.Session
;
13 import javax
.jcr
.nodetype
.NodeType
;
14 import javax
.jcr
.observation
.Event
;
15 import javax
.jcr
.observation
.EventIterator
;
16 import javax
.jcr
.observation
.EventListener
;
17 import javax
.jcr
.observation
.ObservationManager
;
18 import javax
.jcr
.query
.Query
;
19 import javax
.jcr
.query
.QueryManager
;
21 import org
.argeo
.ArgeoException
;
22 import org
.argeo
.eclipse
.ui
.jcr
.AsyncUiEventListener
;
23 import org
.argeo
.jcr
.JcrUtils
;
24 import org
.argeo
.slc
.SlcException
;
25 import org
.argeo
.slc
.client
.ui
.SlcImages
;
26 import org
.argeo
.slc
.core
.execution
.PrimitiveUtils
;
27 import org
.argeo
.slc
.execution
.ExecutionProcess
;
28 import org
.argeo
.slc
.jcr
.SlcJcrUtils
;
29 import org
.argeo
.slc
.jcr
.SlcNames
;
30 import org
.argeo
.slc
.jcr
.SlcTypes
;
31 import org
.eclipse
.jface
.viewers
.CellEditor
;
32 import org
.eclipse
.jface
.viewers
.ColumnLabelProvider
;
33 import org
.eclipse
.jface
.viewers
.ColumnViewer
;
34 import org
.eclipse
.jface
.viewers
.EditingSupport
;
35 import org
.eclipse
.jface
.viewers
.ISelectionChangedListener
;
36 import org
.eclipse
.jface
.viewers
.IStructuredContentProvider
;
37 import org
.eclipse
.jface
.viewers
.IStructuredSelection
;
38 import org
.eclipse
.jface
.viewers
.ITreeContentProvider
;
39 import org
.eclipse
.jface
.viewers
.SelectionChangedEvent
;
40 import org
.eclipse
.jface
.viewers
.StructuredSelection
;
41 import org
.eclipse
.jface
.viewers
.TableViewer
;
42 import org
.eclipse
.jface
.viewers
.TableViewerColumn
;
43 import org
.eclipse
.jface
.viewers
.TextCellEditor
;
44 import org
.eclipse
.jface
.viewers
.TreeViewer
;
45 import org
.eclipse
.jface
.viewers
.Viewer
;
46 import org
.eclipse
.jface
.viewers
.ViewerDropAdapter
;
47 import org
.eclipse
.swt
.SWT
;
48 import org
.eclipse
.swt
.custom
.SashForm
;
49 import org
.eclipse
.swt
.dnd
.DND
;
50 import org
.eclipse
.swt
.dnd
.TextTransfer
;
51 import org
.eclipse
.swt
.dnd
.Transfer
;
52 import org
.eclipse
.swt
.dnd
.TransferData
;
53 import org
.eclipse
.swt
.events
.SelectionEvent
;
54 import org
.eclipse
.swt
.events
.SelectionListener
;
55 import org
.eclipse
.swt
.graphics
.Image
;
56 import org
.eclipse
.swt
.layout
.FillLayout
;
57 import org
.eclipse
.swt
.layout
.GridData
;
58 import org
.eclipse
.swt
.layout
.GridLayout
;
59 import org
.eclipse
.swt
.layout
.RowData
;
60 import org
.eclipse
.swt
.layout
.RowLayout
;
61 import org
.eclipse
.swt
.widgets
.Button
;
62 import org
.eclipse
.swt
.widgets
.Composite
;
63 import org
.eclipse
.swt
.widgets
.Label
;
64 import org
.eclipse
.swt
.widgets
.Table
;
65 import org
.eclipse
.ui
.IWorkbenchPage
;
66 import org
.eclipse
.ui
.PlatformUI
;
67 import org
.eclipse
.ui
.forms
.AbstractFormPart
;
68 import org
.eclipse
.ui
.forms
.IManagedForm
;
69 import org
.eclipse
.ui
.forms
.editor
.FormPage
;
70 import org
.eclipse
.ui
.forms
.widgets
.FormToolkit
;
71 import org
.eclipse
.ui
.forms
.widgets
.ScrolledForm
;
73 public class ProcessBuilderPage
extends FormPage
implements SlcNames
{
74 public final static String ID
= "processBuilderPage";
75 // private final static Log log =
76 // LogFactory.getLog(ProcessBuilderPage.class);
78 private Node processNode
;
80 private TreeViewer flowsViewer
;
81 private TableViewer valuesViewer
;
82 private Label statusLabel
;
84 private Button remove
;
87 private AbstractFormPart formPart
;
88 private EventListener statusObserver
;
90 public ProcessBuilderPage(ProcessEditor editor
, Node processNode
) {
91 super(editor
, ID
, "Definition");
92 this.processNode
= processNode
;
96 protected void createFormContent(IManagedForm mf
) {
98 ScrolledForm form
= mf
.getForm();
99 form
.setExpandHorizontal(true);
100 form
.setExpandVertical(true);
101 form
.setText("Process " + processNode
.getName());
102 GridLayout mainLayout
= new GridLayout(1, true);
103 form
.getBody().setLayout(mainLayout
);
105 createControls(form
.getBody());
106 createBuilder(form
.getBody());
109 formPart
= new AbstractFormPart() {
112 getManagedForm().addPart(formPart
);
115 statusObserver
= new AsyncUiEventListener() {
116 protected void onEventInUiThread(EventIterator events
) {
120 ObservationManager observationManager
= processNode
.getSession()
121 .getWorkspace().getObservationManager();
122 observationManager
.addEventListener(statusObserver
,
123 Event
.PROPERTY_CHANGED
, processNode
.getPath(), true, null,
129 } catch (RepositoryException e
) {
130 throw new ArgeoException("Cannot create form content", e
);
134 protected void createControls(Composite parent
) {
135 FormToolkit tk
= getManagedForm().getToolkit();
137 Composite controls
= tk
.createComposite(parent
);
138 controls
.setLayout(new RowLayout());
139 controls
.setLayoutData(new GridData(SWT
.FILL
, SWT
.FILL
, true, false));
141 run
= tk
.createButton(controls
, null, SWT
.PUSH
);
142 run
.setToolTipText("Run");
143 run
.setImage(SlcImages
.LAUNCH
);
144 run
.addSelectionListener(new SelectionListener() {
145 public void widgetSelected(SelectionEvent e
) {
146 if (isFinished(getProcessStatus())) {
149 ((ProcessEditor
) getEditor()).process();
153 public void widgetDefaultSelected(SelectionEvent e
) {
158 remove
= tk
.createButton(controls
, null, SWT
.PUSH
);
159 remove
.setImage(SlcImages
.REMOVE_ONE
);
160 remove
.setToolTipText("Remove selected flows");
161 remove
.addSelectionListener(new SelectionListener() {
162 public void widgetSelected(SelectionEvent e
) {
163 removeSelectedFlows();
166 public void widgetDefaultSelected(SelectionEvent e
) {
171 clear
= tk
.createButton(controls
, null, SWT
.PUSH
);
172 clear
.setImage(SlcImages
.REMOVE_ALL
);
173 clear
.setToolTipText("Clear all flows");
174 clear
.addSelectionListener(new SelectionListener() {
175 public void widgetSelected(SelectionEvent e
) {
179 public void widgetDefaultSelected(SelectionEvent e
) {
184 Composite statusComposite
= tk
.createComposite(controls
);
185 RowData rowData
= new RowData();
188 statusComposite
.setLayoutData(rowData
);
189 statusComposite
.setLayout(new FillLayout());
190 statusLabel
= tk
.createLabel(statusComposite
, getProcessStatus());
192 // make sure all controls are in line with status
196 protected void createBuilder(Composite parent
) {
197 FormToolkit tk
= getManagedForm().getToolkit();
198 SashForm sashForm
= new SashForm(parent
, SWT
.HORIZONTAL
);
199 sashForm
.setSashWidth(4);
200 GridData sahFormGd
= new GridData(SWT
.FILL
, SWT
.FILL
, true, true);
201 sahFormGd
.widthHint
= 400;
202 sashForm
.setLayoutData(sahFormGd
);
204 Composite flowsComposite
= tk
.createComposite(sashForm
);
205 flowsComposite
.setLayout(new GridLayout(1, false));
207 flowsViewer
= new TreeViewer(flowsComposite
);
208 flowsViewer
.getTree().setLayoutData(
209 new GridData(SWT
.FILL
, SWT
.FILL
, true, true));
210 flowsViewer
.setLabelProvider(new FlowsLabelProvider());
211 flowsViewer
.setContentProvider(new FlowsContentProvider());
212 flowsViewer
.addSelectionChangedListener(new FlowsSelectionListener());
214 int operations
= DND
.DROP_COPY
| DND
.DROP_MOVE
;
215 Transfer
[] tt
= new Transfer
[] { TextTransfer
.getInstance() };
216 flowsViewer
.addDropSupport(operations
, tt
, new FlowsDropListener(
219 flowsViewer
.setInput(getEditorSite());
220 flowsViewer
.setInput(processNode
);
222 Composite valuesComposite
= tk
.createComposite(sashForm
);
223 valuesComposite
.setLayout(new GridLayout(1, false));
225 valuesViewer
= new TableViewer(valuesComposite
);
226 GridData valuedGd
= new GridData(SWT
.FILL
, SWT
.FILL
, true, true);
227 // valuedGd.widthHint = 200;
228 valuesViewer
.getTable().setLayoutData(valuedGd
);
229 valuesViewer
.setContentProvider(new ValuesContentProvider());
230 initializeValuesViewer(valuesViewer
);
231 sashForm
.setWeights(getWeights());
232 valuesViewer
.setInput(getEditorSite());
235 /** Creates the columns of the values viewer */
236 protected void initializeValuesViewer(TableViewer viewer
) {
237 String
[] titles
= { "Name", "Value" };
238 int[] bounds
= { 200, 100 };
240 for (int i
= 0; i
< titles
.length
; i
++) {
241 TableViewerColumn column
= new TableViewerColumn(viewer
, SWT
.NONE
);
242 column
.getColumn().setText(titles
[i
]);
243 column
.getColumn().setWidth(bounds
[i
]);
244 column
.getColumn().setResizable(true);
245 column
.getColumn().setMoveable(true);
247 column
.setLabelProvider(new ColumnLabelProvider() {
248 public String
getText(Object element
) {
250 Node specAttrNode
= (Node
) element
;
251 return specAttrNode
.getName();
252 } catch (RepositoryException e
) {
253 throw new SlcException("Cannot get value", e
);
258 column
.setLabelProvider(new ColumnLabelProvider() {
259 public String
getText(Object element
) {
260 Object obj
= getAttributeSpecValue((Node
) element
);
261 return obj
!= null ? obj
.toString() : "";
264 column
.setEditingSupport(new ValuesEditingSupport(viewer
));
268 Table table
= viewer
.getTable();
269 table
.setHeaderVisible(false);
270 table
.setLinesVisible(true);
273 protected int[] getWeights() {
274 return new int[] { 50, 50 };
280 /** Opens a new editor with a copy of this process */
281 protected void relaunch() {
283 Node duplicatedNode
= duplicateProcess();
284 IWorkbenchPage activePage
= PlatformUI
.getWorkbench()
285 .getActiveWorkbenchWindow().getActivePage();
286 activePage
.openEditor(
287 new ProcessEditorInput(duplicatedNode
.getPath()),
289 getEditor().close(false);
290 } catch (Exception e1
) {
291 throw new SlcException("Cannot relaunch " + processNode
, e1
);
295 /** Duplicates the process */
296 protected Node
duplicateProcess() {
298 Session session
= processNode
.getSession();
299 String uuid
= UUID
.randomUUID().toString();
300 String destPath
= SlcJcrUtils
.createExecutionProcessPath(uuid
);
301 Node newNode
= JcrUtils
.mkdirs(session
, destPath
,
302 SlcTypes
.SLC_PROCESS
);
303 JcrUtils
.copy(processNode
, newNode
);
304 // session.getWorkspace().copy(processNode.getPath(), destPath);
305 // Node newNode = session.getNode(destPath);
306 // make sure that we kept the mixins
307 // newNode.addMixin(NodeType.MIX_CREATED);
308 // newNode.addMixin(NodeType.MIX_LAST_MODIFIED);
309 newNode
.setProperty(SLC_UUID
, uuid
);
310 newNode
.setProperty(SLC_STATUS
, ExecutionProcess
.INITIALIZED
);
313 } catch (RepositoryException e
) {
314 throw new SlcException("Cannot duplicate process", e
);
318 /** Reflects a status change */
319 protected void statusChanged() {
320 String status
= getProcessStatus();
321 statusLabel
.setText(status
);
322 Boolean isEditable
= isEditable(status
);
323 run
.setEnabled(isEditable
);
324 remove
.setEnabled(isEditable
);
325 clear
.setEnabled(isEditable
);
326 // flowsViewer.getTree().setEnabled(isEditable);
327 if (status
.equals(ExecutionProcess
.COMPLETED
)
328 || status
.equals(ExecutionProcess
.ERROR
)) {
329 run
.setEnabled(true);
330 run
.setImage(SlcImages
.RELAUNCH
);
331 run
.setToolTipText("Relaunch");
335 /** Adds initial flows from the editor input if any */
336 protected void addInitialFlows() {
337 for (String path
: ((ProcessEditorInput
) getEditorInput())
338 .getInitialFlowPaths()) {
347 * the path of the flow
349 protected void addFlow(String path
) {
351 Node flowNode
= processNode
.getSession().getNode(path
);
352 Node realizedFlowNode
= processNode
.getNode(SLC_FLOW
).addNode(
354 realizedFlowNode
.addMixin(SlcTypes
.SLC_REALIZED_FLOW
);
355 Node address
= realizedFlowNode
.addNode(SLC_ADDRESS
,
356 NodeType
.NT_ADDRESS
);
357 address
.setProperty(Property
.JCR_PATH
, path
);
359 // copy spec attributes
361 if (flowNode
.hasProperty(SLC_SPEC
)) {
362 Node executionSpecNode
= flowNode
.getProperty(SLC_SPEC
)
364 specAttrsBase
= executionSpecNode
;
365 String executionSpecName
= executionSpecNode
.getProperty(
366 SLC_NAME
).getString();
367 realizedFlowNode
.setProperty(SLC_SPEC
, executionSpecName
);
369 specAttrsBase
= flowNode
;
371 specAttrs
: for (NodeIterator nit
= specAttrsBase
.getNodes(); nit
373 Node specAttrNode
= nit
.nextNode();
374 String attrName
= specAttrNode
.getName();
376 .isNodeType(SlcTypes
.SLC_EXECUTION_SPEC_ATTRIBUTE
))
378 Node realizedAttrNode
= realizedFlowNode
.addNode(specAttrNode
380 JcrUtils
.copy(specAttrNode
, realizedAttrNode
);
382 // ovveride with flow value
383 if (flowNode
.hasNode(attrName
)) {
384 // assuming this is a primitive
385 realizedAttrNode
.setProperty(SLC_VALUE
,
386 flowNode
.getNode(attrName
).getProperty(SLC_VALUE
)
391 flowsViewer
.refresh();
392 formPart
.markDirty();
393 } catch (RepositoryException e
) {
394 throw new SlcException("Cannot drop " + path
, e
);
398 @SuppressWarnings("unchecked")
399 protected void removeSelectedFlows() {
400 if (!flowsViewer
.getSelection().isEmpty()) {
401 Iterator
<Object
> it
= ((StructuredSelection
) flowsViewer
402 .getSelection()).iterator();
403 while (it
.hasNext()) {
404 Node node
= (Node
) it
.next();
407 } catch (RepositoryException e
) {
408 throw new ArgeoException("Cannot remove " + node
, e
);
411 flowsViewer
.refresh();
412 formPart
.markDirty();
416 protected void removeAllFlows() {
418 for (NodeIterator nit
= processNode
.getNode(SLC_FLOW
).getNodes(); nit
420 nit
.nextNode().remove();
422 flowsViewer
.refresh();
423 formPart
.markDirty();
424 } catch (RepositoryException e
) {
425 throw new ArgeoException("Cannot remove flows from " + processNode
,
430 public void commit(Boolean onSave
) {
432 statusLabel
.setText(getProcessStatus());
433 formPart
.commit(onSave
);
439 protected String
getProcessStatus() {
441 return processNode
.getProperty(SLC_STATUS
).getString();
442 } catch (RepositoryException e
) {
443 throw new SlcException("Cannot retrieve status for " + processNode
,
448 /** Optimization so that we don't call the node each time */
449 protected Boolean
isEditable(String status
) {
450 return status
.equals(ExecutionProcess
.NEW
)
451 || status
.equals(ExecutionProcess
.INITIALIZED
);
454 protected Boolean
isFinished(String status
) {
455 return status
.equals(ExecutionProcess
.COMPLETED
)
456 || status
.equals(ExecutionProcess
.ERROR
);
463 public void dispose() {
464 JcrUtils
.unregisterQuietly(processNode
, statusObserver
);
471 protected static Object
getAttributeSpecValue(Node specAttrNode
) {
473 if (specAttrNode
.isNodeType(SlcTypes
.SLC_PRIMITIVE_SPEC_ATTRIBUTE
)) {
474 if (!specAttrNode
.hasProperty(SLC_VALUE
))
476 String type
= specAttrNode
.getProperty(SLC_TYPE
).getString();
477 // TODO optimize based on data type?
478 Object value
= PrimitiveUtils
.convert(type
, specAttrNode
479 .getProperty(SLC_VALUE
).getString());
480 // log.debug(specAttrNode + ", type=" + type + ", value=" +
485 } catch (RepositoryException e
) {
486 throw new SlcException("Cannot get value", e
);
494 static class FlowsContentProvider
implements ITreeContentProvider
{
495 public Object
[] getElements(Object obj
) {
496 if (!(obj
instanceof Node
))
497 return new Object
[0];
500 Node node
= (Node
) obj
;
501 List
<Node
> children
= new ArrayList
<Node
>();
502 for (NodeIterator nit
= node
.getNode(SLC_FLOW
).getNodes(); nit
504 children
.add(nit
.nextNode());
505 return children
.toArray();
506 } catch (RepositoryException e
) {
507 throw new SlcException("Cannot list children of " + obj
, e
);
511 public void inputChanged(Viewer arg0
, Object arg1
, Object arg2
) {
514 public void dispose() {
517 public Object
[] getChildren(Object parentElement
) {
518 // no children for the time being
522 public Object
getParent(Object element
) {
526 public boolean hasChildren(Object element
) {
532 static class FlowsLabelProvider
extends ColumnLabelProvider
{
535 public String
getText(Object element
) {
536 Node node
= (Node
) element
;
538 if (node
.isNodeType(SlcTypes
.SLC_REALIZED_FLOW
)) {
539 if (node
.hasNode(SLC_ADDRESS
)) {
540 String path
= node
.getNode(SLC_ADDRESS
)
541 .getProperty(Property
.JCR_PATH
).getString();
542 return SlcJcrUtils
.flowExecutionModuleName(path
) + ":"
543 + SlcJcrUtils
.flowRelativePath(path
);
546 } catch (RepositoryException e
) {
547 throw new SlcException("Cannot display " + element
, e
);
549 return super.getText(element
);
553 public Image
getImage(Object element
) {
554 Node node
= (Node
) element
;
556 if (node
.isNodeType(SlcTypes
.SLC_REALIZED_FLOW
)) {
557 return SlcImages
.FLOW
;
559 } catch (RepositoryException e
) {
560 throw new SlcException("Cannot display " + element
, e
);
562 return super.getImage(element
);
567 /** Parameter view is updated each time a new line is selected */
568 class FlowsSelectionListener
implements ISelectionChangedListener
{
569 public void selectionChanged(SelectionChangedEvent evt
) {
570 if (evt
.getSelection().isEmpty()) {
571 valuesViewer
.setInput(getEditorSite());
574 Node realizedFlowNode
= (Node
) ((IStructuredSelection
) evt
575 .getSelection()).getFirstElement();
576 valuesViewer
.setInput(realizedFlowNode
);
580 /** Manages drop event. */
581 class FlowsDropListener
extends ViewerDropAdapter
{
583 public FlowsDropListener(Viewer viewer
) {
588 public boolean performDrop(Object data
) {
589 String path
= data
.toString();
591 // either a node or a whole directory was dragged
592 QueryManager qm
= processNode
.getSession().getWorkspace()
594 String statement
= "SELECT * FROM ["
595 + SlcTypes
.SLC_EXECUTION_FLOW
596 + "] WHERE ISDESCENDANTNODE(['" + path
597 + "']) OR ISSAMENODE(['" + path
+ "'])";
598 // log.debug(statement);
599 Query query
= qm
.createQuery(statement
, Query
.JCR_SQL2
);
600 for (NodeIterator nit
= query
.execute().getNodes(); nit
602 addFlow(nit
.nextNode().getPath());
605 } catch (RepositoryException e
) {
606 throw new SlcException("Cannot query flows under " + path
, e
);
611 public boolean validateDrop(Object target
, int operation
,
612 TransferData transferType
) {
613 return isEditable(getProcessStatus());
620 static class ValuesContentProvider
implements IStructuredContentProvider
{
622 public Object
[] getElements(Object inputElement
) {
623 if (!(inputElement
instanceof Node
))
624 return new Object
[0];
627 Node realizedFlowNode
= (Node
) inputElement
;
628 List
<Node
> specAttributes
= new ArrayList
<Node
>();
629 specAttrs
: for (NodeIterator nit
= realizedFlowNode
.getNodes(); nit
631 Node specAttrNode
= nit
.nextNode();
633 .isNodeType(SlcTypes
.SLC_EXECUTION_SPEC_ATTRIBUTE
))
635 specAttributes
.add(specAttrNode
);
637 return specAttributes
.toArray();
638 } catch (RepositoryException e
) {
639 throw new SlcException("Cannot get elements", e
);
643 public void dispose() {
646 public void inputChanged(Viewer viewer
, Object oldInput
, Object newInput
) {
650 class ValuesEditingSupport
extends EditingSupport
{
651 private final TableViewer tableViewer
;
653 public ValuesEditingSupport(ColumnViewer viewer
) {
655 tableViewer
= (TableViewer
) viewer
;
659 protected CellEditor
getCellEditor(Object element
) {
661 Node specAttrNode
= (Node
) element
;
663 .isNodeType(SlcTypes
.SLC_PRIMITIVE_SPEC_ATTRIBUTE
))
664 return new TextCellEditor(tableViewer
.getTable());
666 } catch (RepositoryException e
) {
667 throw new SlcException("Cannot get celle editor", e
);
672 protected boolean canEdit(Object element
) {
674 Node specAttrNode
= (Node
) element
;
675 return !(specAttrNode
.getProperty(SLC_IS_IMMUTABLE
)
676 .getBoolean() || specAttrNode
.getProperty(
677 SLC_IS_CONSTANT
).getBoolean())
679 .isNodeType(SlcTypes
.SLC_PRIMITIVE_SPEC_ATTRIBUTE
);
680 } catch (RepositoryException e
) {
681 throw new SlcException("Cannot check canEdit", e
);
686 protected Object
getValue(Object element
) {
687 Node specAttrNode
= (Node
) element
;
689 Object value
= getAttributeSpecValue(specAttrNode
);
691 throw new SlcException("Unsupported attribute " + element
);
693 .isNodeType(SlcTypes
.SLC_PRIMITIVE_SPEC_ATTRIBUTE
))
694 return value
.toString();
696 } catch (RepositoryException e
) {
697 throw new SlcException("Cannot get value for " + element
, e
);
702 protected void setValue(Object element
, Object value
) {
704 Node specAttrNode
= (Node
) element
;
706 .isNodeType(SlcTypes
.SLC_PRIMITIVE_SPEC_ATTRIBUTE
)) {
707 String type
= specAttrNode
.getProperty(SLC_TYPE
)
709 SlcJcrUtils
.setPrimitiveAsProperty(specAttrNode
, SLC_VALUE
,
711 valuesViewer
.refresh();
712 formPart
.markDirty();
714 } catch (RepositoryException e
) {
715 throw new SlcException("Cannot get celle editor", e
);