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
.workbench
.CommandUtils
;
43 import org
.argeo
.jcr
.JcrUtils
;
44 import org
.argeo
.slc
.SlcException
;
45 import org
.argeo
.slc
.client
.ui
.dist
.DistConstants
;
46 import org
.argeo
.slc
.client
.ui
.dist
.DistPlugin
;
47 import org
.argeo
.slc
.client
.ui
.dist
.PrivilegedJob
;
48 import org
.argeo
.slc
.client
.ui
.dist
.commands
.DeleteArtifacts
;
49 import org
.argeo
.slc
.client
.ui
.dist
.commands
.OpenModuleEditor
;
50 import org
.argeo
.slc
.client
.ui
.dist
.utils
.DistNodeViewerComparator
;
51 import org
.argeo
.slc
.jcr
.SlcNames
;
52 import org
.argeo
.slc
.jcr
.SlcTypes
;
53 import org
.eclipse
.core
.runtime
.IProgressMonitor
;
54 import org
.eclipse
.core
.runtime
.IStatus
;
55 import org
.eclipse
.core
.runtime
.Status
;
56 import org
.eclipse
.jface
.action
.IMenuListener
;
57 import org
.eclipse
.jface
.action
.IMenuManager
;
58 import org
.eclipse
.jface
.action
.MenuManager
;
59 import org
.eclipse
.jface
.viewers
.ColumnLabelProvider
;
60 import org
.eclipse
.jface
.viewers
.DoubleClickEvent
;
61 import org
.eclipse
.jface
.viewers
.IDoubleClickListener
;
62 import org
.eclipse
.jface
.viewers
.IStructuredContentProvider
;
63 import org
.eclipse
.jface
.viewers
.IStructuredSelection
;
64 import org
.eclipse
.jface
.viewers
.TableViewer
;
65 import org
.eclipse
.jface
.viewers
.TableViewerColumn
;
66 import org
.eclipse
.jface
.viewers
.Viewer
;
67 import org
.eclipse
.swt
.SWT
;
68 import org
.eclipse
.swt
.events
.ModifyEvent
;
69 import org
.eclipse
.swt
.events
.ModifyListener
;
70 import org
.eclipse
.swt
.events
.SelectionAdapter
;
71 import org
.eclipse
.swt
.events
.SelectionEvent
;
72 import org
.eclipse
.swt
.layout
.FillLayout
;
73 import org
.eclipse
.swt
.layout
.GridData
;
74 import org
.eclipse
.swt
.layout
.GridLayout
;
75 import org
.eclipse
.swt
.widgets
.Composite
;
76 import org
.eclipse
.swt
.widgets
.Display
;
77 import org
.eclipse
.swt
.widgets
.Label
;
78 import org
.eclipse
.swt
.widgets
.Menu
;
79 import org
.eclipse
.swt
.widgets
.Table
;
80 import org
.eclipse
.swt
.widgets
.Text
;
81 import org
.eclipse
.ui
.IWorkbenchWindow
;
82 import org
.eclipse
.ui
.forms
.IManagedForm
;
83 import org
.eclipse
.ui
.forms
.editor
.FormPage
;
84 import org
.eclipse
.ui
.forms
.widgets
.FormToolkit
;
85 import org
.eclipse
.ui
.forms
.widgets
.ScrolledForm
;
86 import org
.eclipse
.ui
.forms
.widgets
.Section
;
88 /** Show all bundles contained in a given workspace as filter-able table */
89 public class DistWkspSearchPage
extends FormPage
implements SlcNames
{
90 // final private static Log log = LogFactory
91 // .getLog(DistributionOverviewPage.class);
93 final static String PAGE_ID
= "distributionOverviewPage";
96 private Session session
;
99 private DistWorkspaceEditor formEditor
;
100 private FormToolkit tk
;
102 private DistNodeViewerComparator comparator
;
103 private TableViewer viewer
;
105 // private Composite header;
106 private Text artifactTxt
;
107 private final static String FILTER_HELP_MSG
= "Filter criterion, separated by a space";
109 public DistWkspSearchPage(DistWorkspaceEditor formEditor
, String title
,
111 super(formEditor
, PAGE_ID
, title
);
112 this.formEditor
= formEditor
;
113 this.session
= session
;
116 private void asynchronousRefresh() {
117 RefreshJob job
= new RefreshJob(artifactTxt
.getText(), viewer
,
118 getSite().getShell().getDisplay());
123 private class RefreshJob
extends PrivilegedJob
{
124 private TableViewer viewer
;
125 private String filter
;
126 private Display display
;
128 public RefreshJob(String filter
, TableViewer viewer
, Display display
) {
129 super("Get bundle list");
130 this.filter
= filter
;
131 this.viewer
= viewer
;
132 this.display
= display
;
136 protected IStatus
doRun(IProgressMonitor progressMonitor
) {
138 ArgeoMonitor monitor
= new EclipseArgeoMonitor(progressMonitor
);
139 monitor
.beginTask("Getting bundle list", -1);
140 final List
<Node
> result
= JcrUtils
141 .nodeIteratorToList(listBundleArtifacts(session
, filter
));
143 display
.asyncExec(new Runnable() {
145 viewer
.setInput(result
);
148 } catch (Exception e
) {
149 return new Status(IStatus
.ERROR
, DistPlugin
.ID
,
150 "Cannot get bundle list", e
);
152 return Status
.OK_STATUS
;
157 protected void createFormContent(IManagedForm managedForm
) {
158 ScrolledForm form
= managedForm
.getForm();
159 tk
= managedForm
.getToolkit();
162 GridLayout layout
= new GridLayout(1, false);
163 Composite body
= form
.getBody();
164 body
.setLayout(layout
);
166 // Meta info about current workspace
167 Composite header
= tk
.createComposite(body
);
168 header
.setLayoutData(new GridData(SWT
.FILL
, SWT
.TOP
, true, false));
169 createHeaderPart(form
, header
);
171 Composite modules
= tk
.createComposite(body
);
172 modules
.setLayoutData(new GridData(SWT
.FILL
, SWT
.FILL
, true, true));
173 populateModuleSection(modules
);
176 private void createHeaderPart(ScrolledForm form
, Composite parent
) {
177 GridLayout layout
= new GridLayout(4, false);
178 // layout.marginWidth = layout.marginHeight = layout.verticalSpacing =
180 // layout.horizontalSpacing = 2;
181 parent
.setLayout(layout
);
183 String wkspName
= ((DistWkspEditorInput
) getEditorInput())
185 wkspName
= wkspName
.replaceAll("-", " ");
186 form
.setText(wkspName
);
188 String repoAlias
= "";
189 Node repoNode
= ((DistWorkspaceEditor
) getEditor()).getRepoNode();
190 if (repoNode
!= null)
192 repoAlias
= repoNode
.isNodeType(NodeType
.MIX_TITLE
) ? repoNode
193 .getProperty(Property
.JCR_TITLE
).getString() : repoNode
195 } catch (RepositoryException e1
) {
196 throw new SlcException("Unable to get repository alias ", e1
);
201 createLT(parent
, "Repository alias", repoAlias
);
202 createLT(parent
, "URI",
203 ((DistWkspEditorInput
) getEditorInput()).getUri());
206 private void populateModuleSection(Composite parent
) {
207 GridLayout layout
= new GridLayout(1, false);
208 layout
.marginWidth
= layout
.horizontalSpacing
= layout
.horizontalSpacing
= layout
.marginHeight
= 0;
209 parent
.setLayout(layout
);
211 Section section
= tk
.createSection(parent
, Section
.TITLE_BAR
212 | Section
.DESCRIPTION
);
213 section
.setLayoutData(new GridData(SWT
.FILL
, SWT
.FILL
, true, true));
215 section
.setText("Artifacts");
216 section
.setDescription("Search among all artifacts that are referenced in the current workspace");
217 section
.setLayoutData(new GridData(SWT
.FILL
, SWT
.FILL
, true, true));
219 Composite body
= tk
.createComposite(section
);
220 layout
= new GridLayout(1, false);
221 layout
.marginWidth
= layout
.horizontalSpacing
= layout
.horizontalSpacing
= layout
.marginHeight
= 0;
222 body
.setLayout(new GridLayout());
225 Composite filter
= tk
.createComposite(body
);
226 filter
.setLayoutData(new GridData(SWT
.FILL
, SWT
.TOP
, true, false));
227 createFilterPart(filter
);
230 Composite tableCmp
= tk
.createComposite(body
);
231 tableCmp
.setLayoutData(new GridData(SWT
.FILL
, SWT
.FILL
, true, true));
232 createTableViewer(tableCmp
);
234 section
.setClient(body
);
237 /** Build repository request */
238 private NodeIterator
listBundleArtifacts(Session session
, String filter
)
239 throws RepositoryException
{
240 QueryManager queryManager
= session
.getWorkspace().getQueryManager();
241 QueryObjectModelFactory factory
= queryManager
.getQOMFactory();
243 final String bundleArtifactsSelector
= "bundleArtifacts";
244 Selector source
= factory
.selector(SlcTypes
.SLC_BUNDLE_ARTIFACT
,
245 bundleArtifactsSelector
);
247 // Create a dynamic operand for each property on which we want to filter
248 DynamicOperand symbNameDO
= factory
.propertyValue(
249 source
.getSelectorName(), SlcNames
.SLC_SYMBOLIC_NAME
);
250 DynamicOperand versionDO
= factory
.propertyValue(
251 source
.getSelectorName(), SlcNames
.SLC_BUNDLE_VERSION
);
252 DynamicOperand nameDO
= factory
.propertyValue(source
.getSelectorName(),
253 DistConstants
.SLC_BUNDLE_NAME
);
255 // Default Constraint: no source artifacts
256 Constraint defaultC
= factory
.not(factory
.comparison(
258 QueryObjectModelFactory
.JCR_OPERATOR_LIKE
,
259 factory
.literal(session
.getValueFactory().createValue(
262 // Build constraints based the textArea content
263 if (filter
!= null && !"".equals(filter
.trim())) {
265 String
[] strs
= filter
.trim().split(" ");
266 for (String token
: strs
) {
267 token
= token
.replace('*', '%');
268 StaticOperand so
= factory
.literal(session
.getValueFactory()
269 .createValue("%" + token
+ "%"));
271 Constraint currC
= factory
.comparison(symbNameDO
,
272 QueryObjectModelFactory
.JCR_OPERATOR_LIKE
, so
);
273 currC
= factory
.or(currC
, factory
.comparison(versionDO
,
274 QueryObjectModelFactory
.JCR_OPERATOR_LIKE
, so
));
275 currC
= factory
.or(currC
, factory
.comparison(nameDO
,
276 QueryObjectModelFactory
.JCR_OPERATOR_LIKE
, so
));
278 defaultC
= factory
.and(defaultC
, currC
);
282 Ordering order
= factory
.descending(factory
.propertyValue(
283 bundleArtifactsSelector
, SlcNames
.SLC_BUNDLE_VERSION
));
284 Ordering order2
= factory
.ascending(factory
.propertyValue(
285 bundleArtifactsSelector
, SlcNames
.SLC_SYMBOLIC_NAME
));
286 Ordering
[] orderings
= { order
, order2
};
288 QueryObjectModel query
= factory
.createQuery(source
, defaultC
,
291 QueryResult result
= query
.execute();
292 return result
.getNodes();
296 private Text
createLT(Composite parent
, String labelValue
, String textValue
) {
297 Label label
= new Label(parent
, SWT
.RIGHT
);
298 label
.setText(labelValue
);
299 // label.setFont(EclipseUiUtils.getBoldFont(parent));
300 label
.setLayoutData(new GridData(SWT
.RIGHT
, SWT
.CENTER
, false, false));
302 // Add a trailing space to workaround a display glitch in RAP 1.3
303 Text text
= new Text(parent
, SWT
.LEFT
); // | SWT.BORDER
304 text
.setText(textValue
+ " ");
305 text
.setEditable(false);
309 private void createFilterPart(Composite parent
) {
310 GridLayout layout
= new GridLayout();
311 layout
.marginWidth
= layout
.marginHeight
= layout
.verticalSpacing
= 0;
312 layout
.horizontalSpacing
= 5;
313 parent
.setLayout(layout
);
315 // Text Area to filter
316 artifactTxt
= tk
.createText(parent
, "", SWT
.BORDER
| SWT
.SINGLE
317 | SWT
.SEARCH
| SWT
.CANCEL
);
318 artifactTxt
.setMessage(FILTER_HELP_MSG
);
319 GridData gd
= new GridData(SWT
.FILL
, SWT
.FILL
, false, false);
320 gd
.grabExcessHorizontalSpace
= true;
321 artifactTxt
.setLayoutData(gd
);
322 artifactTxt
.addModifyListener(new ModifyListener() {
323 public void modifyText(ModifyEvent event
) {
324 if ("".equals(artifactTxt
.getText().trim()))
325 asynchronousRefresh();
327 refreshFilteredList();
332 private void refreshFilteredList() {
335 nodes
= JcrUtils
.nodeIteratorToList(listBundleArtifacts(session
,
336 artifactTxt
.getText()));
337 viewer
.setInput(nodes
);
338 } catch (RepositoryException e
) {
339 throw new SlcException("Unable to list bundles", e
);
343 private void createTableViewer(Composite parent
) {
344 parent
.setLayout(new FillLayout());
345 // helpers to enable sorting by column
346 List
<String
> propertiesList
= new ArrayList
<String
>();
347 List
<Integer
> propertyTypesList
= new ArrayList
<Integer
>();
349 // Define the TableViewer
350 viewer
= new TableViewer(parent
, SWT
.MULTI
| SWT
.H_SCROLL
351 | SWT
.V_SCROLL
| SWT
.FULL_SELECTION
| SWT
.BORDER
);
354 TableViewerColumn col
= new TableViewerColumn(viewer
, SWT
.NONE
);
355 col
.getColumn().setWidth(300);
356 col
.getColumn().setText("Name");
357 col
.setLabelProvider(new ColumnLabelProvider() {
359 public String
getText(Object element
) {
360 return JcrUtils
.get((Node
) element
,
361 DistConstants
.SLC_BUNDLE_NAME
);
364 col
.getColumn().addSelectionListener(getSelectionAdapter(0));
365 propertiesList
.add(DistConstants
.SLC_BUNDLE_NAME
);
366 propertyTypesList
.add(PropertyType
.STRING
);
369 col
= new TableViewerColumn(viewer
, SWT
.V_SCROLL
);
370 col
.getColumn().setWidth(300);
371 col
.getColumn().setText("Symbolic Name");
372 col
.setLabelProvider(new ColumnLabelProvider() {
374 public String
getText(Object element
) {
375 return JcrUtils
.get((Node
) element
, SLC_SYMBOLIC_NAME
);
378 col
.getColumn().addSelectionListener(getSelectionAdapter(1));
379 propertiesList
.add(SLC_SYMBOLIC_NAME
);
380 propertyTypesList
.add(PropertyType
.STRING
);
383 col
= new TableViewerColumn(viewer
, SWT
.NONE
);
384 col
.getColumn().setWidth(130);
385 col
.getColumn().setText("Version");
386 col
.setLabelProvider(new ColumnLabelProvider() {
388 public String
getText(Object element
) {
389 return JcrUtils
.get((Node
) element
, SLC_BUNDLE_VERSION
);
392 col
.getColumn().addSelectionListener(getSelectionAdapter(2));
393 propertiesList
.add(SLC_BUNDLE_VERSION
);
394 propertyTypesList
.add(DistNodeViewerComparator
.VERSION_TYPE
);
396 final Table table
= viewer
.getTable();
397 table
.setHeaderVisible(true);
398 table
.setLinesVisible(true);
400 viewer
.setContentProvider(new DistributionsContentProvider());
401 getSite().setSelectionProvider(viewer
);
403 comparator
= new DistNodeViewerComparator(2,
404 DistNodeViewerComparator
.ASCENDING
, propertiesList
,
406 viewer
.setComparator(comparator
);
409 MenuManager menuManager
= new MenuManager();
410 Menu menu
= menuManager
.createContextMenu(viewer
.getTable());
411 menuManager
.addMenuListener(new IMenuListener() {
412 public void menuAboutToShow(IMenuManager manager
) {
413 contextMenuAboutToShow(manager
);
416 viewer
.getTable().setMenu(menu
);
417 getSite().registerContextMenu(menuManager
, viewer
);
420 viewer
.addDoubleClickListener(new DoubleClickListener());
424 public void setFocus() {
425 viewer
.getTable().setFocus();
428 /** force refresh of the artifact list */
429 public void refresh() {
430 asynchronousRefresh();
433 /** Programmatically configure the context menu */
434 protected void contextMenuAboutToShow(IMenuManager menuManager
) {
435 IWorkbenchWindow window
= DistPlugin
.getDefault().getWorkbench()
436 .getActiveWorkbenchWindow();
438 // Delete selected artifacts
439 CommandUtils
.refreshCommand(menuManager
, window
, DeleteArtifacts
.ID
,
440 DeleteArtifacts
.DEFAULT_LABEL
, DeleteArtifacts
.DEFAULT_ICON
,
444 private SelectionAdapter
getSelectionAdapter(final int index
) {
445 SelectionAdapter selectionAdapter
= new SelectionAdapter() {
447 public void widgetSelected(SelectionEvent e
) {
448 Table table
= viewer
.getTable();
449 comparator
.setColumn(index
);
450 int dir
= table
.getSortDirection();
451 if (table
.getSortColumn() == table
.getColumn(index
)) {
452 dir
= dir
== SWT
.UP ? SWT
.DOWN
: SWT
.UP
;
456 table
.setSortDirection(dir
);
457 table
.setSortColumn(table
.getColumn(index
));
461 return selectionAdapter
;
465 private class DistributionsContentProvider
implements
466 IStructuredContentProvider
{
467 // we keep a cache of the Nodes in the content provider to be able to
468 // manage long request
469 private List
<Node
> nodes
;
471 public void dispose() {
474 // We expect a list of nodes as a new input
475 @SuppressWarnings("unchecked")
476 public void inputChanged(Viewer viewer
, Object oldInput
, Object newInput
) {
477 nodes
= (List
<Node
>) newInput
;
480 public Object
[] getElements(Object arg0
) {
481 return nodes
.toArray();
485 private class DoubleClickListener
implements IDoubleClickListener
{
487 public void doubleClick(DoubleClickEvent event
) {
488 Object obj
= ((IStructuredSelection
) event
.getSelection())
490 if (obj
instanceof Node
) {
491 Node node
= (Node
) obj
;
493 if (node
.isNodeType(SlcTypes
.SLC_ARTIFACT
)) {
494 DistWkspEditorInput dwip
= (DistWkspEditorInput
) formEditor
496 Map
<String
, String
> params
= new HashMap
<String
, String
>();
497 params
.put(OpenModuleEditor
.PARAM_REPO_NODE_PATH
,
498 dwip
.getRepoNodePath());
499 params
.put(OpenModuleEditor
.PARAM_REPO_URI
,
501 params
.put(OpenModuleEditor
.PARAM_WORKSPACE_NAME
,
502 dwip
.getWorkspaceName());
503 String path
= node
.getPath();
504 params
.put(OpenModuleEditor
.PARAM_MODULE_PATH
, path
);
505 CommandUtils
.callCommand(OpenModuleEditor
.ID
, params
);
507 } catch (RepositoryException re
) {
508 throw new SlcException("Cannot get path for node " + node
509 + " while setting parameters for "
510 + "command OpenModuleEditor", re
);