1 package org
.argeo
.slc
.client
.ui
.views
;
3 import java
.util
.ArrayList
;
4 import java
.util
.Calendar
;
8 import javax
.jcr
.NodeIterator
;
9 import javax
.jcr
.Property
;
10 import javax
.jcr
.PropertyIterator
;
11 import javax
.jcr
.PropertyType
;
12 import javax
.jcr
.RepositoryException
;
13 import javax
.jcr
.Session
;
14 import javax
.jcr
.Value
;
15 import javax
.jcr
.nodetype
.NodeType
;
16 import javax
.jcr
.observation
.Event
;
17 import javax
.jcr
.observation
.EventListener
;
18 import javax
.jcr
.observation
.ObservationManager
;
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
.JcrUtils
;
24 import org
.argeo
.jcr
.UserJcrUtils
;
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
.ParentNodeFolder
;
29 import org
.argeo
.slc
.client
.ui
.model
.ResultFolder
;
30 import org
.argeo
.slc
.client
.ui
.model
.ResultParent
;
31 import org
.argeo
.slc
.client
.ui
.model
.ResultParentUtils
;
32 import org
.argeo
.slc
.client
.ui
.model
.SingleResultNode
;
33 import org
.argeo
.slc
.client
.ui
.model
.VirtualFolder
;
34 import org
.argeo
.slc
.client
.ui
.providers
.ResultTreeContentProvider
;
35 import org
.argeo
.slc
.client
.ui
.providers
.ResultTreeLabelProvider
;
36 import org
.argeo
.slc
.jcr
.SlcJcrResultUtils
;
37 import org
.argeo
.slc
.jcr
.SlcNames
;
38 import org
.argeo
.slc
.jcr
.SlcTypes
;
39 import org
.eclipse
.jface
.action
.IMenuListener
;
40 import org
.eclipse
.jface
.action
.IMenuManager
;
41 import org
.eclipse
.jface
.action
.MenuManager
;
42 import org
.eclipse
.jface
.viewers
.ColumnLabelProvider
;
43 import org
.eclipse
.jface
.viewers
.DecoratingLabelProvider
;
44 import org
.eclipse
.jface
.viewers
.ILabelDecorator
;
45 import org
.eclipse
.jface
.viewers
.ISelectionChangedListener
;
46 import org
.eclipse
.jface
.viewers
.IStructuredContentProvider
;
47 import org
.eclipse
.jface
.viewers
.IStructuredSelection
;
48 import org
.eclipse
.jface
.viewers
.SelectionChangedEvent
;
49 import org
.eclipse
.jface
.viewers
.TableViewer
;
50 import org
.eclipse
.jface
.viewers
.TableViewerColumn
;
51 import org
.eclipse
.jface
.viewers
.TreeViewer
;
52 import org
.eclipse
.jface
.viewers
.Viewer
;
53 import org
.eclipse
.jface
.viewers
.ViewerDropAdapter
;
54 import org
.eclipse
.swt
.SWT
;
55 import org
.eclipse
.swt
.custom
.SashForm
;
56 import org
.eclipse
.swt
.dnd
.DND
;
57 import org
.eclipse
.swt
.dnd
.DragSourceEvent
;
58 import org
.eclipse
.swt
.dnd
.DragSourceListener
;
59 import org
.eclipse
.swt
.dnd
.TextTransfer
;
60 import org
.eclipse
.swt
.dnd
.Transfer
;
61 import org
.eclipse
.swt
.dnd
.TransferData
;
62 import org
.eclipse
.swt
.layout
.FillLayout
;
63 import org
.eclipse
.swt
.layout
.GridData
;
64 import org
.eclipse
.swt
.layout
.GridLayout
;
65 import org
.eclipse
.swt
.widgets
.Composite
;
66 import org
.eclipse
.swt
.widgets
.Display
;
67 import org
.eclipse
.swt
.widgets
.Menu
;
68 import org
.eclipse
.ui
.ISharedImages
;
69 import org
.eclipse
.ui
.IWorkbenchWindow
;
70 import org
.eclipse
.ui
.part
.ViewPart
;
72 /** SLC generic JCR Result tree view. */
73 public class JcrResultTreeView
extends ViewPart
{
74 public final static String ID
= ClientUiPlugin
.ID
+ ".jcrResultTreeView";
76 // private final static Log log =
77 // LogFactory.getLog(JcrResultTreeView.class);
79 /* DEPENDENCY INJECTION */
80 private Session session
;
83 private TreeViewer resultTreeViewer
;
84 private TableViewer propertiesViewer
;
86 private EventListener resultsObserver
= null;
88 private final static String
[] observedNodeTypes
= {
89 SlcTypes
.SLC_TEST_RESULT
, SlcTypes
.SLC_RESULT_FOLDER
,
90 NodeType
.NT_UNSTRUCTURED
};
92 // FIXME cache to ease refresh after D&D
93 private ResultParent lastSelectedElement
;
94 private ResultParent lastSelectedElementParent
;
97 * To be overridden to adapt size of form and result frames.
99 protected int[] getWeights() {
100 return new int[] { 70, 30 };
104 public void createPartControl(Composite parent
) {
105 parent
.setLayout(new FillLayout());
107 SashForm sashForm
= new SashForm(parent
, SWT
.VERTICAL
);
108 sashForm
.setSashWidth(4);
109 sashForm
.setLayout(new FillLayout());
111 // Create the tree on top of the view
112 Composite top
= new Composite(sashForm
, SWT
.NONE
);
113 GridLayout gl
= new GridLayout(1, false);
115 resultTreeViewer
= createResultsTreeViewer(top
);
117 // Create the property viewer on the bottom
118 Composite bottom
= new Composite(sashForm
, SWT
.NONE
);
119 bottom
.setLayout(new GridLayout(1, false));
120 propertiesViewer
= createPropertiesViewer(bottom
);
122 sashForm
.setWeights(getWeights());
124 // Refresh the view to initialize it
128 // The main tree viewer
129 protected TreeViewer
createResultsTreeViewer(Composite parent
) {
130 int style
= SWT
.BORDER
| SWT
.MULTI
;
132 TreeViewer viewer
= new TreeViewer(parent
, style
);
133 viewer
.getTree().setLayoutData(
134 new GridData(SWT
.FILL
, SWT
.FILL
, true, true));
136 viewer
.setContentProvider(new ResultTreeContentProvider());
138 // Add label provider with label decorator
139 ResultTreeLabelProvider rtLblProvider
= new ResultTreeLabelProvider();
140 ILabelDecorator decorator
= ClientUiPlugin
.getDefault().getWorkbench()
141 .getDecoratorManager().getLabelDecorator();
142 viewer
.setLabelProvider(new DecoratingLabelProvider(rtLblProvider
,
144 // viewer.setLabelProvider(rtLblProvider);
145 getSite().setSelectionProvider(viewer
);
147 // add drag & drop support
148 int operations
= DND
.DROP_COPY
| DND
.DROP_MOVE
;
149 Transfer
[] tt
= new Transfer
[] { TextTransfer
.getInstance() };
150 viewer
.addDragSupport(operations
, tt
, new ViewDragListener());
151 viewer
.addDropSupport(operations
, tt
, new ViewDropListener(viewer
));
154 MenuManager menuManager
= new MenuManager();
155 Menu menu
= menuManager
.createContextMenu(viewer
.getTree());
156 menuManager
.addMenuListener(new IMenuListener() {
157 public void menuAboutToShow(IMenuManager manager
) {
158 contextMenuAboutToShow(manager
);
161 viewer
.getTree().setMenu(menu
);
162 getSite().registerContextMenu(menuManager
, viewer
);
164 // add change listener to display TestResult information in the property
166 viewer
.addSelectionChangedListener(new ISelectionChangedListener() {
167 public void selectionChanged(SelectionChangedEvent event
) {
168 if (!event
.getSelection().isEmpty()) {
169 IStructuredSelection sel
= (IStructuredSelection
) event
171 Object firstItem
= sel
.getFirstElement();
172 if (firstItem
instanceof SingleResultNode
)
174 .setInput(((SingleResultNode
) firstItem
)
177 propertiesViewer
.setInput(null);
178 lastSelectedElement
= (ResultParent
) firstItem
;
179 lastSelectedElementParent
= (ResultParent
) ((ResultParent
) firstItem
)
187 // Detailed property viewer
188 protected TableViewer
createPropertiesViewer(Composite parent
) {
189 propertiesViewer
= new TableViewer(parent
);
190 propertiesViewer
.getTable().setLayoutData(
191 new GridData(SWT
.FILL
, SWT
.FILL
, true, true));
192 propertiesViewer
.getTable().setHeaderVisible(true);
193 propertiesViewer
.setContentProvider(new PropertiesContentProvider());
194 TableViewerColumn col
= new TableViewerColumn(propertiesViewer
,
196 col
.getColumn().setText("Name");
197 col
.getColumn().setWidth(200);
198 col
.setLabelProvider(new ColumnLabelProvider() {
199 public String
getText(Object element
) {
201 return ((Property
) element
).getName();
202 } catch (RepositoryException e
) {
203 throw new ArgeoException(
204 "Unexpected exception in label provider", e
);
208 col
= new TableViewerColumn(propertiesViewer
, SWT
.NONE
);
209 col
.getColumn().setText("Value");
210 col
.getColumn().setWidth(400);
211 col
.setLabelProvider(new ColumnLabelProvider() {
212 public String
getText(Object element
) {
214 Property property
= (Property
) element
;
215 if (property
.getType() == PropertyType
.BINARY
)
217 else if (property
.isMultiple()) {
218 StringBuffer buf
= new StringBuffer("[");
219 Value
[] values
= property
.getValues();
220 for (int i
= 0; i
< values
.length
; i
++) {
223 buf
.append(values
[i
].getString());
226 return buf
.toString();
228 return property
.getValue().getString();
229 } catch (RepositoryException e
) {
230 throw new ArgeoException(
231 "Unexpected exception in label provider", e
);
235 col
= new TableViewerColumn(propertiesViewer
, SWT
.NONE
);
236 col
.getColumn().setText("Type");
237 col
.getColumn().setWidth(200);
238 col
.setLabelProvider(new ColumnLabelProvider() {
239 public String
getText(Object element
) {
241 return PropertyType
.nameFromValue(((Property
) element
)
243 } catch (RepositoryException e
) {
244 throw new ArgeoException(
245 "Unexpected exception in label provider", e
);
249 propertiesViewer
.setInput(getViewSite());
250 return propertiesViewer
;
254 public void setFocus() {
258 * refreshes the passed node and its corresponding subtree.
264 public boolean jcrRefresh(Node node
) {
265 boolean isPassed
= true;
267 if (node
.isNodeType(SlcTypes
.SLC_TEST_RESULT
)) {
268 isPassed
= node
.getNode(SlcNames
.SLC_STATUS
)
269 .getProperty(SlcNames
.SLC_SUCCESS
).getBoolean();
270 } else if (node
.isNodeType(SlcTypes
.SLC_RESULT_FOLDER
)) {
271 NodeIterator ni
= node
.getNodes();
272 // quicker but wrong : refresh will stop as soon as a failed
273 // test is found and the whole tree won't be refreshed
274 // while (isPassed && ni.hasNext()){
275 while (ni
.hasNext()) {
276 Node currChild
= ni
.nextNode();
277 isPassed
= isPassed
& jcrRefresh(currChild
);
279 if (isPassed
!= node
.getNode(SlcNames
.SLC_STATUS
)
280 .getProperty(SlcNames
.SLC_SUCCESS
).getBoolean()) {
281 node
.getNode(SlcNames
.SLC_STATUS
).setProperty(
282 SlcNames
.SLC_SUCCESS
, isPassed
);
283 node
.getSession().save();
288 } catch (RepositoryException e
) {
289 throw new SlcException("Cannot register listeners", e
);
295 * refreshes the passed resultParent and its corresponding subtree. It
296 * refreshes the whole viewer if null is passed.
298 * @param ResultParent
301 public void refresh(ResultParent resultParent
) {
302 if (resultParent
== null) {
303 resultTreeViewer
.setInput(initializeResultTree());
304 if (resultsObserver
== null) {
306 ObservationManager observationManager
= session
307 .getWorkspace().getObservationManager();
308 resultsObserver
= new ResultObserver(resultTreeViewer
309 .getTree().getDisplay());
310 observationManager
.addEventListener(resultsObserver
,
311 Event
.NODE_ADDED
| Event
.NODE_REMOVED
, UserJcrUtils
312 .getUserHome(session
).getPath(), true,
313 null, observedNodeTypes
, false);
314 } catch (RepositoryException e
) {
315 throw new SlcException("Cannot register listeners", e
);
320 // FIXME implement refresh for a specific ResultParent object.
321 if (resultParent
instanceof ResultFolder
) {
322 ResultFolder currFolder
= (ResultFolder
) resultParent
;
323 jcrRefresh(currFolder
.getNode());
324 currFolder
.forceFullRefresh();
325 resultTreeViewer
.refresh(lastSelectedElement
);
330 private ResultParent
[] initializeResultTree() {
332 if (session
.nodeExists(SlcJcrResultUtils
333 .getSlcResultsBasePath(session
))) {
334 ResultParent
[] roots
= new ResultParent
[5];
337 roots
[0] = new ParentNodeFolder(null,
338 SlcJcrResultUtils
.getMyResultParentNode(session
),
342 Calendar cal
= Calendar
.getInstance();
343 String relPath
= JcrUtils
.dateAsPath(cal
);
344 List
<String
> datePathes
= new ArrayList
<String
>();
345 datePathes
.add(relPath
);
346 roots
[1] = new VirtualFolder(null,
347 ResultParentUtils
.getResultsForDates(session
,
348 datePathes
), "Today");
351 cal
= Calendar
.getInstance();
352 cal
.add(Calendar
.DAY_OF_YEAR
, -1);
353 relPath
= JcrUtils
.dateAsPath(cal
);
354 datePathes
= new ArrayList
<String
>();
355 datePathes
.add(relPath
);
356 roots
[2] = new VirtualFolder(null,
357 ResultParentUtils
.getResultsForDates(session
,
358 datePathes
), "Yesterday");
361 cal
= Calendar
.getInstance();
362 datePathes
= new ArrayList
<String
>();
364 for (int i
= 0; i
< 7; i
++) {
365 cal
.add(Calendar
.DAY_OF_YEAR
, -i
);
366 relPath
= JcrUtils
.dateAsPath(cal
);
367 datePathes
.add(relPath
);
369 roots
[3] = new VirtualFolder(null,
370 ResultParentUtils
.getResultsForDates(session
,
371 datePathes
), "Last 7 days");
374 Node otherResultsPar
= session
.getNode(SlcJcrResultUtils
375 .getSlcResultsBasePath(session
));
376 roots
[4] = new ParentNodeFolder(null, otherResultsPar
,
380 // no test has yet been processed, we leave the viewer blank
382 } catch (RepositoryException re
) {
383 throw new ArgeoException(
384 "Unexpected error while initializing ResultTree.", re
);
388 // Manage context menu
390 * Defines the commands that will pop up in the context menu.
392 protected void contextMenuAboutToShow(IMenuManager menuManager
) {
393 IWorkbenchWindow window
= ClientUiPlugin
.getDefault().getWorkbench()
394 .getActiveWorkbenchWindow();
396 // Building conditions
397 IStructuredSelection selection
= (IStructuredSelection
) resultTreeViewer
399 boolean isMyResultFolder
= false;
400 if (selection
.size() == 1) {
401 Object obj
= selection
.getFirstElement();
403 if (obj
instanceof ResultFolder
404 && (((ResultFolder
) obj
).getNode())
405 .isNodeType(SlcTypes
.SLC_RESULT_FOLDER
))
406 isMyResultFolder
= true;
407 else if (obj
instanceof ParentNodeFolder
408 && (((ParentNodeFolder
) obj
).getNode().getPath()
409 .startsWith(SlcJcrResultUtils
410 .getMyResultsBasePath(session
))))
411 isMyResultFolder
= true;
412 } catch (RepositoryException re
) {
413 throw new SlcException(
414 "unexpected error while building condition for context menu",
419 CommandUtils
.refreshCommand(menuManager
, window
, AddResultFolder
.ID
,
420 AddResultFolder
.DEFAULT_LABEL
,
421 ClientUiPlugin
.getDefault().getWorkbench().getSharedImages()
422 .getImageDescriptor(ISharedImages
.IMG_OBJ_ADD
),
427 class ViewDragListener
implements DragSourceListener
{
429 public void dragStart(DragSourceEvent event
) {
430 // Check if the drag action should start.
432 IStructuredSelection selection
= (IStructuredSelection
) resultTreeViewer
434 boolean doIt
= false;
435 // only one node at a time for the time being.
436 if (selection
.size() == 1) {
437 Object obj
= selection
.getFirstElement();
438 if (obj
instanceof SingleResultNode
) {
439 Node tNode
= ((SingleResultNode
) obj
).getNode();
441 if (tNode
.getPrimaryNodeType().isNodeType(
442 SlcTypes
.SLC_TEST_RESULT
)
444 .startsWith(SlcJcrResultUtils
445 .getSlcResultsBasePath(session
))))
447 } catch (RepositoryException re
) {
448 throw new SlcException(
449 "unexpected error while validating drag source",
457 public void dragSetData(DragSourceEvent event
) {
458 IStructuredSelection selection
= (IStructuredSelection
) resultTreeViewer
460 Object obj
= selection
.getFirstElement();
461 if (obj
instanceof SingleResultNode
) {
462 Node first
= ((SingleResultNode
) obj
).getNode();
464 event
.data
= first
.getIdentifier();
465 } catch (RepositoryException re
) {
466 throw new SlcException(
467 "unexpected error while setting data", re
);
472 public void dragFinished(DragSourceEvent event
) {
473 // implement here tree refresh in case of a move.
477 // Implementation of the Drop Listener
478 protected class ViewDropListener
extends ViewerDropAdapter
{
480 private Node currParentNode
= null;
482 public ViewDropListener(Viewer viewer
) {
487 public boolean validateDrop(Object target
, int operation
,
488 TransferData transferType
) {
489 boolean validDrop
= false;
491 // We can only drop under myResults
492 Node targetParentNode
= null;
493 if (target
instanceof ResultFolder
) {
494 targetParentNode
= ((ResultFolder
) target
).getNode();
495 } else if (target
instanceof ParentNodeFolder
) {
496 if ((((ParentNodeFolder
) target
).getNode().getPath()
497 .startsWith(SlcJcrResultUtils
498 .getMyResultsBasePath(session
))))
499 targetParentNode
= ((ParentNodeFolder
) target
)
501 } else if (target
instanceof SingleResultNode
) {
502 Node currNode
= ((SingleResultNode
) target
).getNode();
508 .getMyResultsBasePath(session
)))
509 targetParentNode
= currNode
.getParent();
511 if (targetParentNode
!= null) {
512 currParentNode
= targetParentNode
;
515 lastSelectedElement
= (ResultParent
) target
;
516 lastSelectedElementParent
= (ResultParent
) ((ResultParent
) target
)
519 } catch (RepositoryException re
) {
520 throw new SlcException(
521 "unexpected error while validating drop target", re
);
527 public boolean performDrop(Object data
) {
530 Node source
= session
.getNodeByIdentifier((String
) data
);
531 Node target
= currParentNode
.addNode(source
.getName(), source
532 .getPrimaryNodeType().getName());
533 JcrUtils
.copy(source
, target
);
535 .updatePassedStatus(target
,
536 target
.getNode(SlcNames
.SLC_STATUS
)
537 .getProperty(SlcNames
.SLC_SUCCESS
)
539 target
.getSession().save();
540 } catch (RepositoryException re
) {
541 throw new SlcException(
542 "unexpected error while copying dropped node", re
);
548 class ResultObserver
extends AsyncUiEventListener
{
550 public ResultObserver(Display display
) {
555 protected Boolean
willProcessInUiThread(List
<Event
> events
)
556 throws RepositoryException
{
557 // unfiltered for the time being
559 // for (Event event : events) {
560 // getLog().debug("Received event " + event);
561 // int eventType = event.getType();
562 // if (eventType == Event.NODE_REMOVED)
564 // String path = event.getPath();
565 // int index = path.lastIndexOf('/');
566 // String propertyName = path.substring(index + 1);
567 // if (propertyName.equals(SlcNames.SLC_COMPLETED)
568 // || propertyName.equals(SlcNames.SLC_UUID)) {
575 protected void onEventInUiThread(List
<Event
> events
)
576 throws RepositoryException
{
578 for (Event event
: events
) {
579 getLog().debug("Received event " + event
);
580 int eventType
= event
.getType();
581 if (eventType
== Event
.NODE_REMOVED
) {
582 String path
= event
.getPath();
583 int index
= path
.lastIndexOf('/');
584 String parPath
= path
.substring(0, index
+ 1);
585 if (session
.nodeExists(parPath
)) {
586 Node currNode
= session
.getNode(parPath
);
587 if (currNode
.isNodeType(NodeType
.NT_UNSTRUCTURED
)) {
589 jcrRefresh(currNode
);
590 resultTreeViewer
.refresh(true);
591 resultTreeViewer
.expandToLevel(
592 lastSelectedElementParent
, 1);
596 } else if (eventType
== Event
.NODE_ADDED
) {
597 String path
= event
.getPath();
598 if (session
.nodeExists(path
)) {
599 Node currNode
= session
.getNode(path
);
600 if (currNode
.isNodeType(SlcTypes
.SLC_DIFF_RESULT
)
602 .isNodeType(SlcTypes
.SLC_RESULT_FOLDER
)) {
604 resultTreeViewer
.expandToLevel(lastSelectedElement
,
609 // String path = event.getPath();
610 // int index = path.lastIndexOf('/');
611 // String propertyName = path.substring(index + 1);
612 // if (propertyName.equals(SlcNames.SLC_COMPLETED)
613 // || propertyName.equals(SlcNames.SLC_UUID)) {
617 // FIXME implement correct behaviour. treeViewer selection is
618 // disposed by the drag & drop.
619 // resultTreeViewer.refresh();
621 // log.warn("Implement refresh.");
625 class PropertiesContentProvider
implements IStructuredContentProvider
{
626 // private JcrItemsComparator itemComparator = new JcrItemsComparator();
628 public void dispose() {
631 public void inputChanged(Viewer viewer
, Object oldInput
, Object newInput
) {
634 public Object
[] getElements(Object inputElement
) {
636 if (inputElement
instanceof Node
) {
637 List
<Property
> props
= new ArrayList
<Property
>();
638 PropertyIterator pit
= ((Node
) inputElement
)
640 while (pit
.hasNext())
641 props
.add(pit
.nextProperty());
642 return props
.toArray();
644 return new Object
[] {};
645 } catch (RepositoryException e
) {
646 throw new ArgeoException("Cannot get element for "
652 /* DEPENDENCY INJECTION */
653 public void setSession(Session session
) {
654 this.session
= session
;