2 * Copyright (C) 2007-2012 Argeo GmbH
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package org
.argeo
.slc
.client
.ui
.dist
.editors
;
18 import java
.util
.ArrayList
;
19 import java
.util
.HashMap
;
20 import java
.util
.List
;
23 import javax
.jcr
.Node
;
24 import javax
.jcr
.NodeIterator
;
25 import javax
.jcr
.Property
;
26 import javax
.jcr
.PropertyType
;
27 import javax
.jcr
.RepositoryException
;
28 import javax
.jcr
.Session
;
29 import javax
.jcr
.nodetype
.NodeType
;
30 import javax
.jcr
.query
.QueryManager
;
31 import javax
.jcr
.query
.QueryResult
;
32 import javax
.jcr
.query
.qom
.Constraint
;
33 import javax
.jcr
.query
.qom
.DynamicOperand
;
34 import javax
.jcr
.query
.qom
.Ordering
;
35 import javax
.jcr
.query
.qom
.QueryObjectModel
;
36 import javax
.jcr
.query
.qom
.QueryObjectModelFactory
;
37 import javax
.jcr
.query
.qom
.Selector
;
38 import javax
.jcr
.query
.qom
.StaticOperand
;
40 import org
.argeo
.ArgeoMonitor
;
41 import org
.argeo
.eclipse
.ui
.EclipseArgeoMonitor
;
42 import org
.argeo
.eclipse
.ui
.EclipseUiUtils
;
43 import org
.argeo
.eclipse
.ui
.utils
.CommandUtils
;
44 import org
.argeo
.jcr
.JcrUtils
;
45 import org
.argeo
.slc
.SlcException
;
46 import org
.argeo
.slc
.client
.ui
.dist
.DistConstants
;
47 import org
.argeo
.slc
.client
.ui
.dist
.DistImages
;
48 import org
.argeo
.slc
.client
.ui
.dist
.DistPlugin
;
49 import org
.argeo
.slc
.client
.ui
.dist
.PrivilegedJob
;
50 import org
.argeo
.slc
.client
.ui
.dist
.commands
.DeleteArtifacts
;
51 import org
.argeo
.slc
.client
.ui
.dist
.commands
.OpenModuleEditor
;
52 import org
.argeo
.slc
.client
.ui
.dist
.utils
.NodeViewerComparator
;
53 import org
.argeo
.slc
.jcr
.SlcNames
;
54 import org
.argeo
.slc
.jcr
.SlcTypes
;
55 import org
.eclipse
.core
.runtime
.IProgressMonitor
;
56 import org
.eclipse
.core
.runtime
.IStatus
;
57 import org
.eclipse
.core
.runtime
.Status
;
58 import org
.eclipse
.jface
.action
.IMenuListener
;
59 import org
.eclipse
.jface
.action
.IMenuManager
;
60 import org
.eclipse
.jface
.action
.MenuManager
;
61 import org
.eclipse
.jface
.viewers
.ColumnLabelProvider
;
62 import org
.eclipse
.jface
.viewers
.DoubleClickEvent
;
63 import org
.eclipse
.jface
.viewers
.IDoubleClickListener
;
64 import org
.eclipse
.jface
.viewers
.IStructuredContentProvider
;
65 import org
.eclipse
.jface
.viewers
.IStructuredSelection
;
66 import org
.eclipse
.jface
.viewers
.TableViewer
;
67 import org
.eclipse
.jface
.viewers
.TableViewerColumn
;
68 import org
.eclipse
.jface
.viewers
.Viewer
;
69 import org
.eclipse
.swt
.SWT
;
70 import org
.eclipse
.swt
.events
.ModifyEvent
;
71 import org
.eclipse
.swt
.events
.ModifyListener
;
72 import org
.eclipse
.swt
.events
.SelectionAdapter
;
73 import org
.eclipse
.swt
.events
.SelectionEvent
;
74 import org
.eclipse
.swt
.events
.SelectionListener
;
75 import org
.eclipse
.swt
.layout
.FillLayout
;
76 import org
.eclipse
.swt
.layout
.GridData
;
77 import org
.eclipse
.swt
.layout
.GridLayout
;
78 import org
.eclipse
.swt
.widgets
.Button
;
79 import org
.eclipse
.swt
.widgets
.Composite
;
80 import org
.eclipse
.swt
.widgets
.Display
;
81 import org
.eclipse
.swt
.widgets
.Label
;
82 import org
.eclipse
.swt
.widgets
.Menu
;
83 import org
.eclipse
.swt
.widgets
.Table
;
84 import org
.eclipse
.swt
.widgets
.Text
;
85 import org
.eclipse
.ui
.IWorkbenchWindow
;
86 import org
.eclipse
.ui
.forms
.IManagedForm
;
87 import org
.eclipse
.ui
.forms
.editor
.FormPage
;
88 import org
.eclipse
.ui
.forms
.widgets
.FormToolkit
;
89 import org
.eclipse
.ui
.forms
.widgets
.ScrolledForm
;
91 /** Show all bundles contained in a given workspace as filter-able table */
92 public class DistWkspSearchPage
extends FormPage
implements SlcNames
{
93 // final private static Log log = LogFactory
94 // .getLog(DistributionOverviewPage.class);
96 final static String PAGE_ID
= "distributionOverviewPage";
99 private Session session
;
102 private DistWorkspaceEditor formEditor
;
103 private FormToolkit tk
;
105 private NodeViewerComparator comparator
;
106 private TableViewer viewer
;
108 // private Composite header;
109 private Text artifactTxt
;
110 private final static String FILTER_HELP_MSG
= "Search bundles in the current workspace";
112 public DistWkspSearchPage(DistWorkspaceEditor formEditor
, String title
,
114 super(formEditor
, PAGE_ID
, title
);
115 this.formEditor
= formEditor
;
116 this.session
= session
;
119 private void asynchronousRefresh() {
120 RefreshJob job
= new RefreshJob(artifactTxt
.getText(), viewer
,
121 getSite().getShell().getDisplay());
126 private class RefreshJob
extends PrivilegedJob
{
127 private TableViewer viewer
;
128 private String filter
;
129 private Display display
;
131 public RefreshJob(String filter
, TableViewer viewer
, Display display
) {
132 super("Get bundle list");
133 this.filter
= filter
;
134 this.viewer
= viewer
;
135 this.display
= display
;
139 protected IStatus
doRun(IProgressMonitor progressMonitor
) {
141 ArgeoMonitor monitor
= new EclipseArgeoMonitor(progressMonitor
);
142 monitor
.beginTask("Getting bundle list", -1);
143 final List
<Node
> result
= JcrUtils
144 .nodeIteratorToList(listBundleArtifacts(session
, filter
));
146 display
.asyncExec(new Runnable() {
148 viewer
.setInput(result
);
151 } catch (Exception e
) {
152 return new Status(IStatus
.ERROR
, DistPlugin
.ID
,
153 "Cannot get bundle list", e
);
155 return Status
.OK_STATUS
;
160 protected void createFormContent(IManagedForm managedForm
) {
161 ScrolledForm form
= managedForm
.getForm();
162 tk
= managedForm
.getToolkit();
165 GridLayout layout
= new GridLayout(1, false);
166 Composite body
= form
.getBody();
167 body
.setLayout(layout
);
169 // Meta info about current workspace
170 Composite header
= tk
.createComposite(body
);
171 header
.setLayoutData(new GridData(SWT
.FILL
, SWT
.TOP
, true, false));
172 createHeaderPart(form
, header
);
175 Composite filter
= tk
.createComposite(body
);
176 filter
.setLayoutData(new GridData(SWT
.FILL
, SWT
.TOP
, true, false));
177 createFilterPart(filter
);
180 Composite tableCmp
= tk
.createComposite(body
);
181 tableCmp
.setLayoutData(new GridData(SWT
.FILL
, SWT
.FILL
, true, true));
182 createTableViewer(tableCmp
);
185 /** Build repository request */
186 private NodeIterator
listBundleArtifacts(Session session
, String filter
)
187 throws RepositoryException
{
188 QueryManager queryManager
= session
.getWorkspace().getQueryManager();
189 QueryObjectModelFactory factory
= queryManager
.getQOMFactory();
191 final String bundleArtifactsSelector
= "bundleArtifacts";
192 Selector source
= factory
.selector(SlcTypes
.SLC_BUNDLE_ARTIFACT
,
193 bundleArtifactsSelector
);
195 // Create a dynamic operand for each property on which we want to filter
196 DynamicOperand symbNameDO
= factory
.propertyValue(
197 source
.getSelectorName(), SlcNames
.SLC_SYMBOLIC_NAME
);
198 DynamicOperand versionDO
= factory
.propertyValue(
199 source
.getSelectorName(), SlcNames
.SLC_BUNDLE_VERSION
);
200 DynamicOperand nameDO
= factory
.propertyValue(source
.getSelectorName(),
201 DistConstants
.SLC_BUNDLE_NAME
);
203 // Default Constraint: no source artifacts
204 Constraint defaultC
= factory
.not(factory
.comparison(
206 QueryObjectModelFactory
.JCR_OPERATOR_LIKE
,
207 factory
.literal(session
.getValueFactory().createValue(
210 // Build constraints based the textArea content
211 if (filter
!= null && !"".equals(filter
.trim())) {
213 String
[] strs
= filter
.trim().split(" ");
214 for (String token
: strs
) {
215 token
= token
.replace('*', '%');
216 StaticOperand so
= factory
.literal(session
.getValueFactory()
217 .createValue("%" + token
+ "%"));
219 Constraint currC
= factory
.comparison(symbNameDO
,
220 QueryObjectModelFactory
.JCR_OPERATOR_LIKE
, so
);
221 currC
= factory
.or(currC
, factory
.comparison(versionDO
,
222 QueryObjectModelFactory
.JCR_OPERATOR_LIKE
, so
));
223 currC
= factory
.or(currC
, factory
.comparison(nameDO
,
224 QueryObjectModelFactory
.JCR_OPERATOR_LIKE
, so
));
226 defaultC
= factory
.and(defaultC
, currC
);
230 Ordering order
= factory
.descending(factory
.propertyValue(
231 bundleArtifactsSelector
, SlcNames
.SLC_BUNDLE_VERSION
));
232 Ordering order2
= factory
.ascending(factory
.propertyValue(
233 bundleArtifactsSelector
, SlcNames
.SLC_SYMBOLIC_NAME
));
234 Ordering
[] orderings
= { order
, order2
};
236 QueryObjectModel query
= factory
.createQuery(source
, defaultC
,
239 QueryResult result
= query
.execute();
240 return result
.getNodes();
244 private void createHeaderPart(ScrolledForm form
, Composite parent
) {
245 GridLayout layout
= new GridLayout(4, false);
246 layout
.marginWidth
= layout
.marginHeight
= layout
.verticalSpacing
= 0;
247 layout
.horizontalSpacing
= 5;
248 parent
.setLayout(layout
);
250 String wkspName
= ((DistWkspEditorInput
) getEditorInput())
252 // wkspName = "Workspace " + wkspName;
253 form
.setText(wkspName
);
255 // form.setMessage("Choose in the below list "
256 // + "the categories that can be used as base for "
257 // + "modular distributions maintained via the current workspace",
258 // IMessageProvider.NONE);
260 String repoAlias
= "";
261 Node repoNode
= ((DistWorkspaceEditor
) getEditor()).getRepoNode();
262 if (repoNode
!= null)
264 repoAlias
= repoNode
.isNodeType(NodeType
.MIX_TITLE
) ? repoNode
265 .getProperty(Property
.JCR_TITLE
).getString() : repoNode
267 } catch (RepositoryException e1
) {
268 throw new SlcException("Unable to get repository alias ", e1
);
273 createLT(parent
, "Repository alias", repoAlias
);
274 createLT(parent
, "URI",
275 ((DistWkspEditorInput
) getEditorInput()).getUri());
278 private Text
createLT(Composite parent
, String labelValue
, String textValue
) {
279 Label label
= tk
.createLabel(parent
, labelValue
, SWT
.RIGHT
);
280 label
.setFont(EclipseUiUtils
.getBoldFont(parent
));
281 label
.setLayoutData(new GridData(SWT
.RIGHT
, SWT
.CENTER
, false, false));
283 // Add a trailing space to workaround a display glitch in RAP 1.3
284 Text text
= tk
.createText(parent
, textValue
+ " ", SWT
.LEFT
);
285 text
.setEditable(false);
289 private void createFilterPart(Composite parent
) {
290 GridLayout layout
= new GridLayout(2, false);
291 layout
.marginWidth
= layout
.marginHeight
= layout
.verticalSpacing
= 0;
292 layout
.horizontalSpacing
= 5;
293 parent
.setLayout(layout
);
295 // Text Area to filter
296 artifactTxt
= tk
.createText(parent
, "", SWT
.BORDER
| SWT
.SINGLE
297 | SWT
.SEARCH
| SWT
.CANCEL
);
298 artifactTxt
.setMessage(FILTER_HELP_MSG
);
299 GridData gd
= new GridData(SWT
.FILL
, SWT
.FILL
, false, false);
300 gd
.grabExcessHorizontalSpace
= true;
301 artifactTxt
.setLayoutData(gd
);
302 artifactTxt
.addModifyListener(new ModifyListener() {
303 public void modifyText(ModifyEvent event
) {
304 if ("".equals(artifactTxt
.getText().trim()))
305 asynchronousRefresh();
307 refreshFilteredList();
311 Button resetBtn
= tk
.createButton(parent
, null, SWT
.PUSH
);
312 resetBtn
.setImage(DistImages
.IMG_REPO_READONLY
);
313 resetBtn
.addSelectionListener(new SelectionListener() {
315 public void widgetSelected(SelectionEvent e
) {
319 public void widgetDefaultSelected(SelectionEvent e
) {
324 private void resetFilter() {
325 artifactTxt
.setText("");
326 artifactTxt
.setMessage(FILTER_HELP_MSG
);
329 private void refreshFilteredList() {
332 nodes
= JcrUtils
.nodeIteratorToList(listBundleArtifacts(session
,
333 artifactTxt
.getText()));
334 viewer
.setInput(nodes
);
335 } catch (RepositoryException e
) {
336 throw new SlcException("Unable to list bundles", e
);
340 private void createTableViewer(Composite parent
) {
341 parent
.setLayout(new FillLayout());
342 // helpers to enable sorting by column
343 List
<String
> propertiesList
= new ArrayList
<String
>();
344 List
<Integer
> propertyTypesList
= new ArrayList
<Integer
>();
346 // Define the TableViewer
347 viewer
= new TableViewer(parent
, SWT
.MULTI
| SWT
.H_SCROLL
348 | SWT
.V_SCROLL
| SWT
.FULL_SELECTION
| SWT
.BORDER
);
351 TableViewerColumn col
= new TableViewerColumn(viewer
, SWT
.NONE
);
352 col
.getColumn().setWidth(300);
353 col
.getColumn().setText("Name");
354 col
.setLabelProvider(new ColumnLabelProvider() {
356 public String
getText(Object element
) {
357 return JcrUtils
.get((Node
) element
,
358 DistConstants
.SLC_BUNDLE_NAME
);
361 col
.getColumn().addSelectionListener(getSelectionAdapter(0));
362 propertiesList
.add(DistConstants
.SLC_BUNDLE_NAME
);
363 propertyTypesList
.add(PropertyType
.STRING
);
366 col
= new TableViewerColumn(viewer
, SWT
.V_SCROLL
);
367 col
.getColumn().setWidth(300);
368 col
.getColumn().setText("Symbolic Name");
369 col
.setLabelProvider(new ColumnLabelProvider() {
371 public String
getText(Object element
) {
372 return JcrUtils
.get((Node
) element
, SLC_SYMBOLIC_NAME
);
375 col
.getColumn().addSelectionListener(getSelectionAdapter(1));
376 propertiesList
.add(SLC_SYMBOLIC_NAME
);
377 propertyTypesList
.add(PropertyType
.STRING
);
380 col
= new TableViewerColumn(viewer
, SWT
.NONE
);
381 col
.getColumn().setWidth(130);
382 col
.getColumn().setText("Version");
383 col
.setLabelProvider(new ColumnLabelProvider() {
385 public String
getText(Object element
) {
386 return JcrUtils
.get((Node
) element
, SLC_BUNDLE_VERSION
);
389 col
.getColumn().addSelectionListener(getSelectionAdapter(2));
390 propertiesList
.add(SLC_BUNDLE_VERSION
);
391 propertyTypesList
.add(PropertyType
.STRING
);
393 final Table table
= viewer
.getTable();
394 table
.setHeaderVisible(true);
395 table
.setLinesVisible(true);
397 viewer
.setContentProvider(new DistributionsContentProvider());
398 getSite().setSelectionProvider(viewer
);
400 comparator
= new NodeViewerComparator(2,
401 NodeViewerComparator
.ASCENDING
, propertiesList
,
403 viewer
.setComparator(comparator
);
406 MenuManager menuManager
= new MenuManager();
407 Menu menu
= menuManager
.createContextMenu(viewer
.getTable());
408 menuManager
.addMenuListener(new IMenuListener() {
409 public void menuAboutToShow(IMenuManager manager
) {
410 contextMenuAboutToShow(manager
);
413 viewer
.getTable().setMenu(menu
);
414 getSite().registerContextMenu(menuManager
, viewer
);
417 viewer
.addDoubleClickListener(new DoubleClickListener());
421 public void setFocus() {
422 viewer
.getTable().setFocus();
425 /** force refresh of the artifact list */
426 public void refresh() {
427 asynchronousRefresh();
430 /** Programmatically configure the context menu */
431 protected void contextMenuAboutToShow(IMenuManager menuManager
) {
432 IWorkbenchWindow window
= DistPlugin
.getDefault().getWorkbench()
433 .getActiveWorkbenchWindow();
435 // Delete selected artifacts
436 CommandUtils
.refreshCommand(menuManager
, window
, DeleteArtifacts
.ID
,
437 DeleteArtifacts
.DEFAULT_LABEL
, DeleteArtifacts
.DEFAULT_ICON
,
441 private SelectionAdapter
getSelectionAdapter(final int index
) {
442 SelectionAdapter selectionAdapter
= new SelectionAdapter() {
444 public void widgetSelected(SelectionEvent e
) {
445 Table table
= viewer
.getTable();
446 comparator
.setColumn(index
);
447 int dir
= table
.getSortDirection();
448 if (table
.getSortColumn() == table
.getColumn(index
)) {
449 dir
= dir
== SWT
.UP ? SWT
.DOWN
: SWT
.UP
;
453 table
.setSortDirection(dir
);
454 table
.setSortColumn(table
.getColumn(index
));
458 return selectionAdapter
;
462 private class DistributionsContentProvider
implements
463 IStructuredContentProvider
{
464 // we keep a cache of the Nodes in the content provider to be able to
465 // manage long request
466 private List
<Node
> nodes
;
468 public void dispose() {
471 // We expect a list of nodes as a new input
472 @SuppressWarnings("unchecked")
473 public void inputChanged(Viewer viewer
, Object oldInput
, Object newInput
) {
474 nodes
= (List
<Node
>) newInput
;
477 public Object
[] getElements(Object arg0
) {
478 return nodes
.toArray();
482 private class DoubleClickListener
implements IDoubleClickListener
{
484 public void doubleClick(DoubleClickEvent event
) {
485 Object obj
= ((IStructuredSelection
) event
.getSelection())
487 if (obj
instanceof Node
) {
488 Node node
= (Node
) obj
;
490 if (node
.isNodeType(SlcTypes
.SLC_ARTIFACT
)) {
491 DistWkspEditorInput dwip
= (DistWkspEditorInput
) formEditor
493 Map
<String
, String
> params
= new HashMap
<String
, String
>();
494 params
.put(OpenModuleEditor
.PARAM_REPO_NODE_PATH
,
495 dwip
.getRepoNodePath());
496 params
.put(OpenModuleEditor
.PARAM_REPO_URI
,
498 params
.put(OpenModuleEditor
.PARAM_WORKSPACE_NAME
,
499 dwip
.getWorkspaceName());
500 String path
= node
.getPath();
501 params
.put(OpenModuleEditor
.PARAM_MODULE_PATH
, path
);
502 CommandUtils
.callCommand(OpenModuleEditor
.ID
, params
);
504 } catch (RepositoryException re
) {
505 throw new SlcException("Cannot get path for node " + node
506 + " while setting parameters for "
507 + "command OpenModuleEditor", re
);
515 * UI Trick to put scroll bar on the table rather than on the scrollform
517 private void refreshLayout() {
518 // // Compute desired table size
519 // int maxH = getManagedForm().getForm().getSize().y;
520 // int maxW = getManagedForm().getForm().getParent().getSize().x;
521 // // maxH = maxH - header.getSize().y;
522 // final Table table = viewer.getTable();
523 // GridData gd = new GridData(SWT.LEFT, SWT.TOP, true, true);
525 // // when table height is less than 200 px, we let the scroll bar on
528 // // FIXME substract some spare space. There is room here for
530 // gd.heightHint = Math.max(maxH - 35, 200);
531 // gd.widthHint = Math.max(maxW - 35, 200);
533 // table.setLayoutData(gd);
534 // getManagedForm().reflow(true);
538 public void setActive(boolean active
) {
539 super.setActive(active
);