1 package org
.argeo
.slc
.client
.ui
.views
;
3 import java
.util
.ArrayList
;
7 import javax
.jcr
.NodeIterator
;
8 import javax
.jcr
.Property
;
9 import javax
.jcr
.PropertyIterator
;
10 import javax
.jcr
.PropertyType
;
11 import javax
.jcr
.RepositoryException
;
12 import javax
.jcr
.Session
;
13 import javax
.jcr
.Value
;
14 import javax
.jcr
.observation
.Event
;
15 import javax
.jcr
.observation
.EventListener
;
16 import javax
.jcr
.observation
.ObservationManager
;
18 import org
.apache
.commons
.logging
.Log
;
19 import org
.apache
.commons
.logging
.LogFactory
;
20 import org
.argeo
.ArgeoException
;
21 import org
.argeo
.eclipse
.ui
.jcr
.AsyncUiEventListener
;
22 import org
.argeo
.eclipse
.ui
.utils
.CommandUtils
;
23 import org
.argeo
.jcr
.ArgeoJcrUtils
;
24 import org
.argeo
.jcr
.JcrUtils
;
25 import org
.argeo
.slc
.SlcException
;
26 import org
.argeo
.slc
.client
.ui
.ClientUiPlugin
;
27 import org
.argeo
.slc
.client
.ui
.commands
.AddResultFolder
;
28 import org
.argeo
.slc
.client
.ui
.model
.ResultFolder
;
29 import org
.argeo
.slc
.client
.ui
.model
.ResultParent
;
30 import org
.argeo
.slc
.client
.ui
.model
.SimpleNodeFolder
;
31 import org
.argeo
.slc
.client
.ui
.model
.SingleResultNode
;
32 import org
.argeo
.slc
.client
.ui
.providers
.ResultTreeContentProvider
;
33 import org
.argeo
.slc
.client
.ui
.providers
.ResultTreeLabelProvider
;
34 import org
.argeo
.slc
.jcr
.SlcJcrResultUtils
;
35 import org
.argeo
.slc
.jcr
.SlcNames
;
36 import org
.argeo
.slc
.jcr
.SlcTypes
;
37 import org
.eclipse
.jface
.action
.IMenuListener
;
38 import org
.eclipse
.jface
.action
.IMenuManager
;
39 import org
.eclipse
.jface
.action
.MenuManager
;
40 import org
.eclipse
.jface
.viewers
.ColumnLabelProvider
;
41 import org
.eclipse
.jface
.viewers
.DecoratingLabelProvider
;
42 import org
.eclipse
.jface
.viewers
.ILabelDecorator
;
43 import org
.eclipse
.jface
.viewers
.ISelectionChangedListener
;
44 import org
.eclipse
.jface
.viewers
.IStructuredContentProvider
;
45 import org
.eclipse
.jface
.viewers
.IStructuredSelection
;
46 import org
.eclipse
.jface
.viewers
.SelectionChangedEvent
;
47 import org
.eclipse
.jface
.viewers
.TableViewer
;
48 import org
.eclipse
.jface
.viewers
.TableViewerColumn
;
49 import org
.eclipse
.jface
.viewers
.TreeViewer
;
50 import org
.eclipse
.jface
.viewers
.Viewer
;
51 import org
.eclipse
.jface
.viewers
.ViewerDropAdapter
;
52 import org
.eclipse
.swt
.SWT
;
53 import org
.eclipse
.swt
.custom
.SashForm
;
54 import org
.eclipse
.swt
.dnd
.DND
;
55 import org
.eclipse
.swt
.dnd
.DragSourceEvent
;
56 import org
.eclipse
.swt
.dnd
.DragSourceListener
;
57 import org
.eclipse
.swt
.dnd
.TextTransfer
;
58 import org
.eclipse
.swt
.dnd
.Transfer
;
59 import org
.eclipse
.swt
.dnd
.TransferData
;
60 import org
.eclipse
.swt
.layout
.FillLayout
;
61 import org
.eclipse
.swt
.layout
.GridData
;
62 import org
.eclipse
.swt
.layout
.GridLayout
;
63 import org
.eclipse
.swt
.widgets
.Composite
;
64 import org
.eclipse
.swt
.widgets
.Display
;
65 import org
.eclipse
.swt
.widgets
.Menu
;
66 import org
.eclipse
.ui
.ISharedImages
;
67 import org
.eclipse
.ui
.IWorkbenchWindow
;
68 import org
.eclipse
.ui
.part
.ViewPart
;
70 /** SLC generic JCR Result tree view. */
71 public class JcrResultTreeView
extends ViewPart
{
72 public final static String ID
= ClientUiPlugin
.ID
+ ".jcrResultTreeView";
74 private final static Log log
= LogFactory
.getLog(JcrResultTreeView
.class);
76 /* DEPENDENCY INJECTION */
77 private Session session
;
80 private TreeViewer resultTreeViewer
;
81 private TableViewer propertiesViewer
;
83 private EventListener resultsObserver
= null;
85 private final static String
[] observedNodeTypes
= { SlcTypes
.SLC_TEST_RESULT
};
88 * To be overridden to adapt size of form and result frames.
90 protected int[] getWeights() {
91 return new int[] { 70, 30 };
95 public void createPartControl(Composite parent
) {
96 parent
.setLayout(new FillLayout());
98 SashForm sashForm
= new SashForm(parent
, SWT
.VERTICAL
);
99 sashForm
.setSashWidth(4);
100 sashForm
.setLayout(new FillLayout());
102 // Create the tree on top of the view
103 Composite top
= new Composite(sashForm
, SWT
.NONE
);
104 GridLayout gl
= new GridLayout(1, false);
106 resultTreeViewer
= createResultsTreeViewer(top
);
108 // Create the property viewer on the bottom
109 Composite bottom
= new Composite(sashForm
, SWT
.NONE
);
110 bottom
.setLayout(new GridLayout(1, false));
111 propertiesViewer
= createPropertiesViewer(bottom
);
113 sashForm
.setWeights(getWeights());
115 // Refresh the view to initialize it
119 ObservationManager observationManager
= session
.getWorkspace()
120 .getObservationManager();
121 // FIXME Will not be notified if empty result is deleted
122 if (ArgeoJcrUtils
.getUserHome(session
) != null) {
123 resultsObserver
= new ResultObserver(resultTreeViewer
.getTree()
125 observationManager
.addEventListener(resultsObserver
,
126 Event
.PROPERTY_ADDED
| Event
.NODE_REMOVED
, ArgeoJcrUtils
127 .getUserHome(session
).getPath(), true, null,
128 observedNodeTypes
, false);
130 } catch (RepositoryException e
) {
131 throw new SlcException("Cannot register listeners", e
);
136 // The main tree viewer
137 protected TreeViewer
createResultsTreeViewer(Composite parent
) {
138 int style
= SWT
.BORDER
| SWT
.MULTI
;
140 TreeViewer viewer
= new TreeViewer(parent
, style
);
141 viewer
.getTree().setLayoutData(
142 new GridData(SWT
.FILL
, SWT
.FILL
, true, true));
144 viewer
.setContentProvider(new ResultTreeContentProvider());
146 // Add label provider with label decorator
147 ResultTreeLabelProvider rtLblProvider
= new ResultTreeLabelProvider();
148 ILabelDecorator decorator
= ClientUiPlugin
.getDefault().getWorkbench()
149 .getDecoratorManager().getLabelDecorator();
150 viewer
.setLabelProvider(new DecoratingLabelProvider(rtLblProvider
,
152 // viewer.setLabelProvider(rtLblProvider);
153 getSite().setSelectionProvider(viewer
);
155 // add drag & drop support
156 int operations
= DND
.DROP_COPY
| DND
.DROP_MOVE
;
157 Transfer
[] tt
= new Transfer
[] { TextTransfer
.getInstance() };
158 viewer
.addDragSupport(operations
, tt
, new ViewDragListener());
159 viewer
.addDropSupport(operations
, tt
, new ViewDropListener(viewer
));
162 MenuManager menuManager
= new MenuManager();
163 Menu menu
= menuManager
.createContextMenu(viewer
.getTree());
164 menuManager
.addMenuListener(new IMenuListener() {
165 public void menuAboutToShow(IMenuManager manager
) {
166 contextMenuAboutToShow(manager
);
169 viewer
.getTree().setMenu(menu
);
170 getSite().registerContextMenu(menuManager
, viewer
);
172 // add change listener to display TestResult information in the property
174 viewer
.addSelectionChangedListener(new ISelectionChangedListener() {
175 public void selectionChanged(SelectionChangedEvent event
) {
176 if (!event
.getSelection().isEmpty()) {
177 IStructuredSelection sel
= (IStructuredSelection
) event
179 Object firstItem
= sel
.getFirstElement();
180 if (firstItem
instanceof SingleResultNode
)
182 .setInput(((SingleResultNode
) firstItem
)
185 propertiesViewer
.setInput(null);
193 // Detailed property viewer
194 protected TableViewer
createPropertiesViewer(Composite parent
) {
195 propertiesViewer
= new TableViewer(parent
);
196 propertiesViewer
.getTable().setLayoutData(
197 new GridData(SWT
.FILL
, SWT
.FILL
, true, true));
198 propertiesViewer
.getTable().setHeaderVisible(true);
199 propertiesViewer
.setContentProvider(new PropertiesContentProvider());
200 TableViewerColumn col
= new TableViewerColumn(propertiesViewer
,
202 col
.getColumn().setText("Name");
203 col
.getColumn().setWidth(200);
204 col
.setLabelProvider(new ColumnLabelProvider() {
205 public String
getText(Object element
) {
207 return ((Property
) element
).getName();
208 } catch (RepositoryException e
) {
209 throw new ArgeoException(
210 "Unexpected exception in label provider", e
);
214 col
= new TableViewerColumn(propertiesViewer
, SWT
.NONE
);
215 col
.getColumn().setText("Value");
216 col
.getColumn().setWidth(400);
217 col
.setLabelProvider(new ColumnLabelProvider() {
218 public String
getText(Object element
) {
220 Property property
= (Property
) element
;
221 if (property
.getType() == PropertyType
.BINARY
)
223 else if (property
.isMultiple()) {
224 StringBuffer buf
= new StringBuffer("[");
225 Value
[] values
= property
.getValues();
226 for (int i
= 0; i
< values
.length
; i
++) {
229 buf
.append(values
[i
].getString());
232 return buf
.toString();
234 return property
.getValue().getString();
235 } catch (RepositoryException e
) {
236 throw new ArgeoException(
237 "Unexpected exception in label provider", e
);
241 col
= new TableViewerColumn(propertiesViewer
, SWT
.NONE
);
242 col
.getColumn().setText("Type");
243 col
.getColumn().setWidth(200);
244 col
.setLabelProvider(new ColumnLabelProvider() {
245 public String
getText(Object element
) {
247 return PropertyType
.nameFromValue(((Property
) element
)
249 } catch (RepositoryException e
) {
250 throw new ArgeoException(
251 "Unexpected exception in label provider", e
);
255 propertiesViewer
.setInput(getViewSite());
256 return propertiesViewer
;
260 public void setFocus() {
264 * refreshes the passed node and its corresponding subtree.
270 public boolean jcrRefresh(Node node
) {
271 boolean isPassed
= true;
273 if (node
.isNodeType(SlcTypes
.SLC_TEST_RESULT
)) {
274 isPassed
= node
.getNode(SlcNames
.SLC_STATUS
)
275 .getProperty(SlcNames
.SLC_SUCCESS
).getBoolean();
276 } else if (node
.isNodeType(SlcTypes
.SLC_RESULT_FOLDER
)) {
277 NodeIterator ni
= node
.getNodes();
278 // quicker but wrong : refresh will stop as soon as a failed
279 // test is found and the whole tree won't be refreshed
280 // while (isPassed && ni.hasNext()){
281 while (ni
.hasNext()) {
282 Node currChild
= ni
.nextNode();
283 isPassed
= isPassed
& jcrRefresh(currChild
);
285 if (isPassed
!= node
.getNode(SlcNames
.SLC_STATUS
)
286 .getProperty(SlcNames
.SLC_SUCCESS
).getBoolean()) {
287 node
.getNode(SlcNames
.SLC_STATUS
).setProperty(
288 SlcNames
.SLC_SUCCESS
, isPassed
);
289 node
.getSession().save();
294 } catch (RepositoryException e
) {
295 throw new SlcException("Cannot register listeners", e
);
301 * refreshes the passed resultParent and its corresponding subtree. It
302 * refreshes the whole viewer if null is passed.
304 * @param ResultParent
307 public void refresh(ResultParent resultParent
) {
308 if (resultParent
== null) {
309 resultTreeViewer
.setInput(initializeResultTree());
310 if (resultsObserver
== null) {
311 // force initialization of the resultsObserver, only useful
312 // if the current view has been displayed before a single
315 ObservationManager observationManager
= session
316 .getWorkspace().getObservationManager();
317 resultsObserver
= new ResultObserver(resultTreeViewer
318 .getTree().getDisplay());
319 observationManager
.addEventListener(resultsObserver
,
320 Event
.PROPERTY_ADDED
| Event
.NODE_REMOVED
, ArgeoJcrUtils
321 .getUserHome(session
).getPath(), true,
322 null, observedNodeTypes
, false);
323 } catch (RepositoryException e
) {
324 throw new SlcException("Cannot register listeners", e
);
329 // FIXME implement refresh for a specific ResultParent object.
330 if (resultParent
instanceof ResultFolder
) {
331 ResultFolder currFolder
= (ResultFolder
) resultParent
;
332 jcrRefresh(currFolder
.getNode());
333 currFolder
.forceFullRefresh();
338 private ResultParent
[] initializeResultTree() {
339 ResultParent
[] roots
= new ResultParent
[2];
341 roots
[0] = new ResultFolder(null,
342 SlcJcrResultUtils
.getMyResultParentNode(session
),
344 Node otherResultsPar
= session
.getNode(SlcJcrResultUtils
345 .getSlcResultsBasePath(session
));
346 roots
[1] = new SimpleNodeFolder(null, otherResultsPar
,
349 } catch (RepositoryException re
) {
350 throw new ArgeoException(
351 "Unexpected error while initializing ResultTree.", re
);
355 // Manage context menu
357 * Defines the commands that will pop up in the context menu.
359 protected void contextMenuAboutToShow(IMenuManager menuManager
) {
360 IWorkbenchWindow window
= ClientUiPlugin
.getDefault().getWorkbench()
361 .getActiveWorkbenchWindow();
363 // Building conditions
364 IStructuredSelection selection
= (IStructuredSelection
) resultTreeViewer
366 boolean isMyResultFolder
= false;
367 if (selection
.size() == 1) {
368 Object obj
= selection
.getFirstElement();
370 Node targetParentNode
= null;
371 if (obj
instanceof ResultFolder
) {
372 targetParentNode
= ((ResultFolder
) obj
).getNode();
374 if (targetParentNode
.isNodeType(SlcTypes
.SLC_RESULT_FOLDER
))
375 isMyResultFolder
= true;
377 } catch (RepositoryException re
) {
378 throw new SlcException(
379 "unexpected error while building condition for context menu",
384 CommandUtils
.refreshCommand(menuManager
, window
, AddResultFolder
.ID
,
385 AddResultFolder
.DEFAULT_LABEL
,
386 ClientUiPlugin
.getDefault().getWorkbench().getSharedImages()
387 .getImageDescriptor(ISharedImages
.IMG_OBJ_ADD
),
392 class ViewDragListener
implements DragSourceListener
{
394 public void dragStart(DragSourceEvent event
) {
395 // Check if the drag action should start.
397 IStructuredSelection selection
= (IStructuredSelection
) resultTreeViewer
399 boolean doIt
= false;
400 // only one node at a time for the time being.
401 if (selection
.size() == 1) {
402 Object obj
= selection
.getFirstElement();
403 if (obj
instanceof SingleResultNode
) {
404 Node tNode
= ((SingleResultNode
) obj
).getNode();
406 if (tNode
.getPrimaryNodeType().isNodeType(
407 SlcTypes
.SLC_TEST_RESULT
)
409 .startsWith(SlcJcrResultUtils
410 .getSlcResultsBasePath(session
))))
412 } catch (RepositoryException re
) {
413 throw new SlcException(
414 "unexpected error while validating drag source",
422 public void dragSetData(DragSourceEvent event
) {
423 IStructuredSelection selection
= (IStructuredSelection
) resultTreeViewer
425 Object obj
= selection
.getFirstElement();
426 if (obj
instanceof SingleResultNode
) {
427 Node first
= ((SingleResultNode
) obj
).getNode();
429 event
.data
= first
.getIdentifier();
430 } catch (RepositoryException re
) {
431 throw new SlcException(
432 "unexpected error while setting data", re
);
437 public void dragFinished(DragSourceEvent event
) {
438 // implement here tree refresh in case of a move.
442 // Implementation of the Drop Listener
443 protected class ViewDropListener
extends ViewerDropAdapter
{
445 private Node currParentNode
= null;
447 public ViewDropListener(Viewer viewer
) {
452 public boolean validateDrop(Object target
, int operation
,
453 TransferData transferType
) {
454 boolean validDrop
= false;
456 // We can only drop under myResults
457 Node targetParentNode
= null;
458 if (target
instanceof ResultFolder
) {
459 Node currNode
= ((ResultFolder
) target
).getNode();
461 if (currNode
.isNodeType(SlcTypes
.SLC_RESULT_FOLDER
)) {
462 targetParentNode
= currNode
;
464 } else if (target
instanceof SingleResultNode
) {
465 Node currNode
= ((SingleResultNode
) target
).getNode();
466 if (currNode
.getParent().isNodeType(
467 SlcTypes
.SLC_RESULT_FOLDER
))
468 targetParentNode
= currNode
.getParent();
470 if (targetParentNode
!= null) {
471 currParentNode
= targetParentNode
;
474 } catch (RepositoryException re
) {
475 throw new SlcException(
476 "unexpected error while validating drop target", re
);
482 public boolean performDrop(Object data
) {
485 Node source
= session
.getNodeByIdentifier((String
) data
);
486 Node target
= currParentNode
.addNode(source
.getName(), source
487 .getPrimaryNodeType().getName());
488 JcrUtils
.copy(source
, target
);
489 updatePassedStatus(target
, target
.getNode(SlcNames
.SLC_STATUS
)
490 .getProperty(SlcNames
.SLC_SUCCESS
).getBoolean());
491 target
.getSession().save();
492 } catch (RepositoryException re
) {
493 throw new SlcException(
494 "unexpected error while copying dropped node", re
);
500 * recursively update passed status of the parent ResultFolder and its
507 private void updatePassedStatus(Node node
, boolean passed
) {
509 Node pNode
= node
.getParent();
510 boolean pStatus
= pNode
.getNode(SlcNames
.SLC_STATUS
)
511 .getProperty(SlcNames
.SLC_SUCCESS
).getBoolean();
512 if (pStatus
== passed
)
516 // error we only update status of the result folder and its
518 pNode
.getNode(SlcNames
.SLC_STATUS
).setProperty(
519 SlcNames
.SLC_SUCCESS
, passed
);
520 updatePassedStatus(pNode
, passed
);
522 // success we must first check if all siblings have also
523 // successfully completed
524 boolean success
= true;
525 NodeIterator ni
= pNode
.getNodes();
526 children
: while (ni
.hasNext()) {
527 Node currNode
= ni
.nextNode();
528 if ((currNode
.isNodeType(SlcTypes
.SLC_DIFF_RESULT
) || currNode
529 .isNodeType(SlcTypes
.SLC_RESULT_FOLDER
))
530 && !currNode
.getNode(SlcNames
.SLC_STATUS
)
531 .getProperty(SlcNames
.SLC_SUCCESS
)
538 pNode
.getNode(SlcNames
.SLC_STATUS
).setProperty(
539 SlcNames
.SLC_SUCCESS
, passed
);
540 updatePassedStatus(pNode
, passed
);
542 // one of the siblings had also the failed status so
543 // above tree remains unchanged.
547 } catch (RepositoryException e
) {
548 throw new SlcException("Cannot register listeners", e
);
553 class ResultObserver
extends AsyncUiEventListener
{
555 public ResultObserver(Display display
) {
560 protected Boolean
willProcessInUiThread(List
<Event
> events
)
561 throws RepositoryException
{
562 for (Event event
: events
) {
563 // getLog().debug("Received event " + event);
564 int eventType
= event
.getType();
565 if (eventType
== Event
.NODE_REMOVED
)
567 String path
= event
.getPath();
568 int index
= path
.lastIndexOf('/');
569 String propertyName
= path
.substring(index
+ 1);
570 if (propertyName
.equals(SlcNames
.SLC_COMPLETED
)
571 || propertyName
.equals(SlcNames
.SLC_UUID
)) {
578 protected void onEventInUiThread(List
<Event
> events
)
579 throws RepositoryException
{
580 // FIXME implement correct behaviour. treeViewer selection is
581 // disposed by the drag & drop.
582 // resultTreeViewer.refresh();
584 // log.warn("Implement refresh.");
589 class PropertiesContentProvider
implements IStructuredContentProvider
{
590 // private JcrItemsComparator itemComparator = new JcrItemsComparator();
592 public void dispose() {
595 public void inputChanged(Viewer viewer
, Object oldInput
, Object newInput
) {
598 public Object
[] getElements(Object inputElement
) {
600 if (inputElement
instanceof Node
) {
601 List
<Property
> props
= new ArrayList
<Property
>();
602 PropertyIterator pit
= ((Node
) inputElement
)
604 while (pit
.hasNext())
605 props
.add(pit
.nextProperty());
606 return props
.toArray();
608 return new Object
[] {};
609 } catch (RepositoryException e
) {
610 throw new ArgeoException("Cannot get element for "
616 /* DEPENDENCY INJECTION */
617 public void setSession(Session session
) {
618 this.session
= session
;