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
;
19 import org
.apache
.commons
.logging
.Log
;
20 import org
.apache
.commons
.logging
.LogFactory
;
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
, SlcTypes
{
74 public final static String ID
= "processBuilderPage";
75 private final static Log log
= LogFactory
.getLog(ProcessBuilderPage
.class);
77 private Node processNode
;
79 private TreeViewer flowsViewer
;
80 private TableViewer valuesViewer
;
81 private Label statusLabel
;
83 private Button remove
;
86 private AbstractFormPart formPart
;
87 private EventListener statusObserver
;
89 public ProcessBuilderPage(ProcessEditor editor
, Node processNode
) {
90 super(editor
, ID
, "Definition");
91 this.processNode
= processNode
;
95 protected void createFormContent(IManagedForm mf
) {
97 ScrolledForm form
= mf
.getForm();
98 form
.setExpandHorizontal(true);
99 form
.setExpandVertical(true);
100 form
.setText("Process " + processNode
.getName());
101 GridLayout mainLayout
= new GridLayout(1, true);
102 form
.getBody().setLayout(mainLayout
);
104 createControls(form
.getBody());
105 createBuilder(form
.getBody());
108 formPart
= new AbstractFormPart() {
111 getManagedForm().addPart(formPart
);
114 statusObserver
= new AsyncUiEventListener() {
115 protected void onEventInUiThread(EventIterator events
) {
119 ObservationManager observationManager
= processNode
.getSession()
120 .getWorkspace().getObservationManager();
121 observationManager
.addEventListener(statusObserver
,
122 Event
.PROPERTY_CHANGED
, processNode
.getPath(), true, null,
128 } catch (RepositoryException e
) {
129 throw new ArgeoException("Cannot create form content", e
);
133 protected void createControls(Composite parent
) {
134 FormToolkit tk
= getManagedForm().getToolkit();
136 Composite controls
= tk
.createComposite(parent
);
137 controls
.setLayout(new RowLayout());
138 controls
.setLayoutData(new GridData(SWT
.FILL
, SWT
.FILL
, true, false));
140 run
= tk
.createButton(controls
, null, SWT
.PUSH
);
141 run
.setToolTipText("Run");
142 run
.setImage(SlcImages
.LAUNCH
);
143 run
.addSelectionListener(new SelectionListener() {
144 public void widgetSelected(SelectionEvent e
) {
145 if (isFinished(getProcessStatus())) {
148 ((ProcessEditor
) getEditor()).process();
152 public void widgetDefaultSelected(SelectionEvent e
) {
157 remove
= tk
.createButton(controls
, null, SWT
.PUSH
);
158 remove
.setImage(SlcImages
.REMOVE_ONE
);
159 remove
.setToolTipText("Remove selected flows");
160 remove
.addSelectionListener(new SelectionListener() {
161 public void widgetSelected(SelectionEvent e
) {
162 removeSelectedFlows();
165 public void widgetDefaultSelected(SelectionEvent e
) {
170 clear
= tk
.createButton(controls
, null, SWT
.PUSH
);
171 clear
.setImage(SlcImages
.REMOVE_ALL
);
172 clear
.setToolTipText("Clear all flows");
173 clear
.addSelectionListener(new SelectionListener() {
174 public void widgetSelected(SelectionEvent e
) {
178 public void widgetDefaultSelected(SelectionEvent e
) {
183 Composite statusComposite
= tk
.createComposite(controls
);
184 RowData rowData
= new RowData();
187 statusComposite
.setLayoutData(rowData
);
188 statusComposite
.setLayout(new FillLayout());
189 statusLabel
= tk
.createLabel(statusComposite
, getProcessStatus());
191 // make sure all controls are in line with status
195 protected void createBuilder(Composite parent
) {
196 FormToolkit tk
= getManagedForm().getToolkit();
197 SashForm sashForm
= new SashForm(parent
, SWT
.HORIZONTAL
);
198 sashForm
.setSashWidth(4);
199 GridData sahFormGd
= new GridData(SWT
.FILL
, SWT
.FILL
, true, true);
200 sahFormGd
.widthHint
= 400;
201 sashForm
.setLayoutData(sahFormGd
);
203 Composite flowsComposite
= tk
.createComposite(sashForm
);
204 flowsComposite
.setLayout(new GridLayout(1, false));
206 flowsViewer
= new TreeViewer(flowsComposite
);
207 flowsViewer
.getTree().setLayoutData(
208 new GridData(SWT
.FILL
, SWT
.FILL
, true, true));
209 flowsViewer
.setLabelProvider(new FlowsLabelProvider());
210 flowsViewer
.setContentProvider(new FlowsContentProvider());
211 flowsViewer
.addSelectionChangedListener(new FlowsSelectionListener());
213 int operations
= DND
.DROP_COPY
| DND
.DROP_MOVE
;
214 Transfer
[] tt
= new Transfer
[] { TextTransfer
.getInstance() };
215 flowsViewer
.addDropSupport(operations
, tt
, new FlowsDropListener(
218 flowsViewer
.setInput(getEditorSite());
219 flowsViewer
.setInput(processNode
);
221 Composite valuesComposite
= tk
.createComposite(sashForm
);
222 valuesComposite
.setLayout(new GridLayout(1, false));
224 valuesViewer
= new TableViewer(valuesComposite
);
225 GridData valuedGd
= new GridData(SWT
.FILL
, SWT
.FILL
, true, true);
226 // valuedGd.widthHint = 200;
227 valuesViewer
.getTable().setLayoutData(valuedGd
);
228 valuesViewer
.setContentProvider(new ValuesContentProvider());
229 initializeValuesViewer(valuesViewer
);
230 sashForm
.setWeights(getWeights());
231 valuesViewer
.setInput(getEditorSite());
234 /** Creates the columns of the values viewer */
235 protected void initializeValuesViewer(TableViewer viewer
) {
236 String
[] titles
= { "Name", "Value" };
237 int[] bounds
= { 200, 100 };
239 for (int i
= 0; i
< titles
.length
; i
++) {
240 TableViewerColumn column
= new TableViewerColumn(viewer
, SWT
.NONE
);
241 column
.getColumn().setText(titles
[i
]);
242 column
.getColumn().setWidth(bounds
[i
]);
243 column
.getColumn().setResizable(true);
244 column
.getColumn().setMoveable(true);
246 column
.setLabelProvider(new ColumnLabelProvider() {
247 public String
getText(Object element
) {
249 Node specAttrNode
= (Node
) element
;
250 return specAttrNode
.getName();
251 } catch (RepositoryException e
) {
252 throw new SlcException("Cannot get value", e
);
257 column
.setLabelProvider(new ColumnLabelProvider() {
258 public String
getText(Object element
) {
259 Object obj
= getAttributeSpecValue((Node
) element
);
260 return obj
!= null ? obj
.toString() : "";
263 column
.setEditingSupport(new ValuesEditingSupport(viewer
));
267 Table table
= viewer
.getTable();
268 table
.setHeaderVisible(false);
269 table
.setLinesVisible(true);
272 protected int[] getWeights() {
273 return new int[] { 50, 50 };
279 /** Opens a new editor with a copy of this process */
280 protected void relaunch() {
282 Node duplicatedNode
= duplicateProcess();
283 IWorkbenchPage activePage
= PlatformUI
.getWorkbench()
284 .getActiveWorkbenchWindow().getActivePage();
285 activePage
.openEditor(
286 new ProcessEditorInput(duplicatedNode
.getPath()),
288 getEditor().close(false);
289 } catch (Exception e1
) {
290 throw new SlcException("Cannot relaunch " + processNode
, e1
);
294 /** Duplicates the process */
295 protected Node
duplicateProcess() {
297 Session session
= processNode
.getSession();
298 String uuid
= UUID
.randomUUID().toString();
299 String destPath
= SlcJcrUtils
.createExecutionProcessPath(uuid
);
300 Node newNode
= JcrUtils
.mkdirs(session
, destPath
, SLC_PROCESS
);
301 JcrUtils
.copy(processNode
, newNode
);
302 // session.getWorkspace().copy(processNode.getPath(), destPath);
303 // Node newNode = session.getNode(destPath);
304 // make sure that we kept the mixins
305 // newNode.addMixin(NodeType.MIX_CREATED);
306 // newNode.addMixin(NodeType.MIX_LAST_MODIFIED);
307 newNode
.setProperty(SLC_UUID
, uuid
);
308 newNode
.setProperty(SLC_STATUS
, ExecutionProcess
.INITIALIZED
);
311 } catch (RepositoryException e
) {
312 throw new SlcException("Cannot duplicate process", e
);
316 /** Reflects a status change */
317 protected void statusChanged() {
318 String status
= getProcessStatus();
319 statusLabel
.setText(status
);
320 Boolean isEditable
= isEditable(status
);
321 run
.setEnabled(isEditable
);
322 remove
.setEnabled(isEditable
);
323 clear
.setEnabled(isEditable
);
324 // flowsViewer.getTree().setEnabled(isEditable);
325 if (status
.equals(ExecutionProcess
.COMPLETED
)
326 || status
.equals(ExecutionProcess
.ERROR
)) {
327 run
.setEnabled(true);
328 run
.setImage(SlcImages
.RELAUNCH
);
329 run
.setToolTipText("Relaunch");
333 /** Adds initial flows from the editor input if any */
334 protected void addInitialFlows() {
335 for (String path
: ((ProcessEditorInput
) getEditorInput())
336 .getInitialFlowPaths()) {
345 * the path of the flow
347 public void addFlow(String path
) {
349 Node flowNode
= processNode
.getSession().getNode(path
);
350 Node realizedFlowNode
= processNode
.getNode(SLC_FLOW
).addNode(
352 realizedFlowNode
.addMixin(SLC_REALIZED_FLOW
);
353 Node address
= realizedFlowNode
.addNode(SLC_ADDRESS
,
354 NodeType
.NT_ADDRESS
);
355 address
.setProperty(Property
.JCR_PATH
, path
);
357 // copy spec attributes
359 if (flowNode
.hasProperty(SLC_SPEC
))
360 specAttrsBase
= flowNode
.getProperty(SLC_SPEC
).getNode();
362 specAttrsBase
= flowNode
;
364 specAttrs
: for (NodeIterator nit
= specAttrsBase
.getNodes(); nit
366 Node specAttrNode
= nit
.nextNode();
368 .isNodeType(SlcTypes
.SLC_EXECUTION_SPEC_ATTRIBUTE
))
370 Node realizedAttrNode
= realizedFlowNode
.addNode(specAttrNode
372 JcrUtils
.copy(specAttrNode
, realizedAttrNode
);
375 flowsViewer
.refresh();
376 formPart
.markDirty();
377 } catch (RepositoryException e
) {
378 throw new SlcException("Cannot drop " + path
, e
);
382 @SuppressWarnings("unchecked")
383 protected void removeSelectedFlows() {
384 if (!flowsViewer
.getSelection().isEmpty()) {
385 Iterator
<Object
> it
= ((StructuredSelection
) flowsViewer
386 .getSelection()).iterator();
387 while (it
.hasNext()) {
388 Node node
= (Node
) it
.next();
391 } catch (RepositoryException e
) {
392 throw new ArgeoException("Cannot remove " + node
, e
);
395 flowsViewer
.refresh();
396 formPart
.markDirty();
400 protected void removeAllFlows() {
402 for (NodeIterator nit
= processNode
.getNode(SLC_FLOW
).getNodes(); nit
404 nit
.nextNode().remove();
406 flowsViewer
.refresh();
407 formPart
.markDirty();
408 } catch (RepositoryException e
) {
409 throw new ArgeoException("Cannot remove flows from " + processNode
,
414 public void commit(Boolean onSave
) {
416 statusLabel
.setText(getProcessStatus());
417 formPart
.commit(onSave
);
423 protected String
getProcessStatus() {
425 return processNode
.getProperty(SLC_STATUS
).getString();
426 } catch (RepositoryException e
) {
427 throw new SlcException("Cannot retrieve status for " + processNode
,
432 /** Optimization so that we don't call the node each time */
433 protected Boolean
isEditable(String status
) {
434 return status
.equals(ExecutionProcess
.NEW
)
435 || status
.equals(ExecutionProcess
.INITIALIZED
);
438 protected Boolean
isFinished(String status
) {
439 return status
.equals(ExecutionProcess
.COMPLETED
)
440 || status
.equals(ExecutionProcess
.ERROR
);
447 public void dispose() {
448 JcrUtils
.unregisterQuietly(processNode
, statusObserver
);
455 protected static Object
getAttributeSpecValue(Node specAttrNode
) {
457 if (specAttrNode
.isNodeType(SlcTypes
.SLC_PRIMITIVE_SPEC_ATTRIBUTE
)) {
458 if (!specAttrNode
.hasProperty(SLC_VALUE
))
460 String type
= specAttrNode
.getProperty(SLC_TYPE
).getString();
461 // TODO optimize based on data type?
462 Object value
= PrimitiveUtils
.convert(type
, specAttrNode
463 .getProperty(SLC_VALUE
).getString());
464 log
.debug(specAttrNode
+ ", type=" + type
+ ", value=" + value
);
468 } catch (RepositoryException e
) {
469 throw new SlcException("Cannot get value", e
);
477 static class FlowsContentProvider
implements ITreeContentProvider
{
478 public Object
[] getElements(Object obj
) {
479 if (!(obj
instanceof Node
))
480 return new Object
[0];
483 Node node
= (Node
) obj
;
484 List
<Node
> children
= new ArrayList
<Node
>();
485 for (NodeIterator nit
= node
.getNode(SLC_FLOW
).getNodes(); nit
487 children
.add(nit
.nextNode());
488 return children
.toArray();
489 } catch (RepositoryException e
) {
490 throw new SlcException("Cannot list children of " + obj
, e
);
494 public void inputChanged(Viewer arg0
, Object arg1
, Object arg2
) {
497 public void dispose() {
500 public Object
[] getChildren(Object parentElement
) {
501 // no children for the time being
505 public Object
getParent(Object element
) {
509 public boolean hasChildren(Object element
) {
515 static class FlowsLabelProvider
extends ColumnLabelProvider
{
518 public String
getText(Object element
) {
519 Node node
= (Node
) element
;
521 if (node
.isNodeType(SLC_REALIZED_FLOW
)) {
522 if (node
.hasNode(SLC_ADDRESS
)) {
523 String path
= node
.getNode(SLC_ADDRESS
)
524 .getProperty(Property
.JCR_PATH
).getString();
525 return SlcJcrUtils
.flowExecutionModuleName(path
) + ":"
526 + SlcJcrUtils
.flowRelativePath(path
);
529 } catch (RepositoryException e
) {
530 throw new SlcException("Cannot display " + element
, e
);
532 return super.getText(element
);
536 public Image
getImage(Object element
) {
537 Node node
= (Node
) element
;
539 if (node
.isNodeType(SLC_REALIZED_FLOW
)) {
540 return SlcImages
.FLOW
;
542 } catch (RepositoryException e
) {
543 throw new SlcException("Cannot display " + element
, e
);
545 return super.getImage(element
);
550 /** Parameter view is updated each time a new line is selected */
551 class FlowsSelectionListener
implements ISelectionChangedListener
{
552 public void selectionChanged(SelectionChangedEvent evt
) {
553 if (evt
.getSelection().isEmpty()) {
554 valuesViewer
.setInput(getEditorSite());
557 Node realizedFlowNode
= (Node
) ((IStructuredSelection
) evt
558 .getSelection()).getFirstElement();
559 valuesViewer
.setInput(realizedFlowNode
);
563 class FlowsDropListener
extends ViewerDropAdapter
{
565 public FlowsDropListener(Viewer viewer
) {
570 public boolean performDrop(Object data
) {
571 String path
= data
.toString();
577 public boolean validateDrop(Object target
, int operation
,
578 TransferData transferType
) {
579 return isEditable(getProcessStatus());
586 static class ValuesContentProvider
implements IStructuredContentProvider
{
588 public Object
[] getElements(Object inputElement
) {
589 if (!(inputElement
instanceof Node
))
590 return new Object
[0];
593 Node realizedFlowNode
= (Node
) inputElement
;
594 List
<Node
> specAttributes
= new ArrayList
<Node
>();
595 specAttrs
: for (NodeIterator nit
= realizedFlowNode
.getNodes(); nit
597 Node specAttrNode
= nit
.nextNode();
599 .isNodeType(SlcTypes
.SLC_EXECUTION_SPEC_ATTRIBUTE
))
601 specAttributes
.add(specAttrNode
);
603 return specAttributes
.toArray();
604 } catch (RepositoryException e
) {
605 throw new SlcException("Cannot get elements", e
);
609 public void dispose() {
612 public void inputChanged(Viewer viewer
, Object oldInput
, Object newInput
) {
616 class ValuesEditingSupport
extends EditingSupport
{
617 private final TableViewer tableViewer
;
619 public ValuesEditingSupport(ColumnViewer viewer
) {
621 tableViewer
= (TableViewer
) viewer
;
625 protected CellEditor
getCellEditor(Object element
) {
627 Node specAttrNode
= (Node
) element
;
629 .isNodeType(SlcTypes
.SLC_PRIMITIVE_SPEC_ATTRIBUTE
))
630 return new TextCellEditor(tableViewer
.getTable());
632 } catch (RepositoryException e
) {
633 throw new SlcException("Cannot get celle editor", e
);
638 protected boolean canEdit(Object element
) {
640 Node specAttrNode
= (Node
) element
;
641 return !(specAttrNode
.getProperty(SLC_IS_IMMUTABLE
)
642 .getBoolean() || specAttrNode
.getProperty(
643 SLC_IS_CONSTANT
).getBoolean())
645 .isNodeType(SlcTypes
.SLC_PRIMITIVE_SPEC_ATTRIBUTE
);
646 } catch (RepositoryException e
) {
647 throw new SlcException("Cannot check canEdit", e
);
652 protected Object
getValue(Object element
) {
653 Node specAttrNode
= (Node
) element
;
655 Object value
= getAttributeSpecValue(specAttrNode
);
657 throw new SlcException("Unsupported attribute " + element
);
659 .isNodeType(SlcTypes
.SLC_PRIMITIVE_SPEC_ATTRIBUTE
))
660 return value
.toString();
662 } catch (RepositoryException e
) {
663 throw new SlcException("Cannot get value for " + element
, e
);
668 protected void setValue(Object element
, Object value
) {
670 Node specAttrNode
= (Node
) element
;
672 .isNodeType(SlcTypes
.SLC_PRIMITIVE_SPEC_ATTRIBUTE
)) {
673 String type
= specAttrNode
.getProperty(SLC_TYPE
)
675 SlcJcrUtils
.setPrimitiveAsProperty(specAttrNode
, SLC_VALUE
,
677 valuesViewer
.refresh();
678 formPart
.markDirty();
680 } catch (RepositoryException e
) {
681 throw new SlcException("Cannot get celle editor", e
);