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
.apache
.commons
.logging
.Log
;
22 import org
.apache
.commons
.logging
.LogFactory
;
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
= 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() {
117 protected void onEventInUiThread(EventIterator 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();
376 .isNodeType(SlcTypes
.SLC_EXECUTION_SPEC_ATTRIBUTE
))
378 Node realizedAttrNode
= realizedFlowNode
.addNode(specAttrNode
380 JcrUtils
.copy(specAttrNode
, realizedAttrNode
);
382 // for (PropertyIterator pit = realizedAttrNode.getProperties();
385 // Property p = pit.nextProperty();
386 // if (!p.isMultiple())
387 // log.debug(p.getName() + "=" + p.getValue().getString());
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
);