]> git.argeo.org Git - gpl/argeo-slc.git/blob - cms/org.argeo.slc.client.ui.dist/src/org/argeo/slc/client/ui/dist/editors/DistWkspSearchPage.java
1a959a511b8ca9e804a754581837a4cfcadbaa67
[gpl/argeo-slc.git] / cms / org.argeo.slc.client.ui.dist / src / org / argeo / slc / client / ui / dist / editors / DistWkspSearchPage.java
1 package org.argeo.slc.client.ui.dist.editors;
2
3 import java.util.ArrayList;
4 import java.util.HashMap;
5 import java.util.List;
6 import java.util.Map;
7
8 import javax.jcr.Node;
9 import javax.jcr.NodeIterator;
10 import javax.jcr.Property;
11 import javax.jcr.PropertyType;
12 import javax.jcr.RepositoryException;
13 import javax.jcr.Session;
14 import javax.jcr.nodetype.NodeType;
15 import javax.jcr.query.QueryManager;
16 import javax.jcr.query.QueryResult;
17 import javax.jcr.query.qom.Constraint;
18 import javax.jcr.query.qom.DynamicOperand;
19 import javax.jcr.query.qom.Ordering;
20 import javax.jcr.query.qom.QueryObjectModel;
21 import javax.jcr.query.qom.QueryObjectModelFactory;
22 import javax.jcr.query.qom.Selector;
23 import javax.jcr.query.qom.StaticOperand;
24
25 import org.argeo.cms.ui.workbench.util.CommandUtils;
26 import org.argeo.cms.ui.workbench.util.PrivilegedJob;
27 import org.argeo.eclipse.ui.EclipseJcrMonitor;
28 import org.argeo.jcr.JcrMonitor;
29 import org.argeo.jcr.JcrUtils;
30 import org.argeo.slc.SlcException;
31 import org.argeo.slc.SlcNames;
32 import org.argeo.slc.SlcTypes;
33 import org.argeo.slc.client.ui.dist.DistConstants;
34 import org.argeo.slc.client.ui.dist.DistPlugin;
35 import org.argeo.slc.client.ui.dist.commands.DeleteArtifacts;
36 import org.argeo.slc.client.ui.dist.commands.OpenModuleEditor;
37 import org.argeo.slc.client.ui.dist.utils.DistNodeViewerComparator;
38 import org.eclipse.core.runtime.IProgressMonitor;
39 import org.eclipse.core.runtime.IStatus;
40 import org.eclipse.core.runtime.Status;
41 import org.eclipse.jface.action.IMenuListener;
42 import org.eclipse.jface.action.IMenuManager;
43 import org.eclipse.jface.action.MenuManager;
44 import org.eclipse.jface.viewers.ColumnLabelProvider;
45 import org.eclipse.jface.viewers.DoubleClickEvent;
46 import org.eclipse.jface.viewers.IDoubleClickListener;
47 import org.eclipse.jface.viewers.IStructuredContentProvider;
48 import org.eclipse.jface.viewers.IStructuredSelection;
49 import org.eclipse.jface.viewers.TableViewer;
50 import org.eclipse.jface.viewers.TableViewerColumn;
51 import org.eclipse.jface.viewers.Viewer;
52 import org.eclipse.swt.SWT;
53 import org.eclipse.swt.events.ModifyEvent;
54 import org.eclipse.swt.events.ModifyListener;
55 import org.eclipse.swt.events.SelectionAdapter;
56 import org.eclipse.swt.events.SelectionEvent;
57 import org.eclipse.swt.layout.FillLayout;
58 import org.eclipse.swt.layout.GridData;
59 import org.eclipse.swt.layout.GridLayout;
60 import org.eclipse.swt.widgets.Composite;
61 import org.eclipse.swt.widgets.Display;
62 import org.eclipse.swt.widgets.Label;
63 import org.eclipse.swt.widgets.Menu;
64 import org.eclipse.swt.widgets.Table;
65 import org.eclipse.swt.widgets.Text;
66 import org.eclipse.ui.IWorkbenchWindow;
67 import org.eclipse.ui.forms.IManagedForm;
68 import org.eclipse.ui.forms.editor.FormPage;
69 import org.eclipse.ui.forms.widgets.FormToolkit;
70 import org.eclipse.ui.forms.widgets.ScrolledForm;
71 import org.eclipse.ui.forms.widgets.Section;
72
73 /** Show all bundles contained in a given workspace as filter-able table */
74 public class DistWkspSearchPage extends FormPage implements SlcNames {
75 // final private static Log log = LogFactory
76 // .getLog(DistributionOverviewPage.class);
77
78 final static String PAGE_ID = "distributionOverviewPage";
79
80 // Business Objects
81 private Session session;
82
83 // This page widgets
84 private DistWorkspaceEditor formEditor;
85 private FormToolkit tk;
86
87 private DistNodeViewerComparator comparator;
88 private TableViewer viewer;
89
90 // private Composite header;
91 private Text artifactTxt;
92 private final static String FILTER_HELP_MSG = "Filter criterion, separated by a space";
93
94 public DistWkspSearchPage(DistWorkspaceEditor formEditor, String title,
95 Session session) {
96 super(formEditor, PAGE_ID, title);
97 this.formEditor = formEditor;
98 this.session = session;
99 }
100
101 private void asynchronousRefresh() {
102 RefreshJob job = new RefreshJob(artifactTxt.getText(), viewer,
103 getSite().getShell().getDisplay());
104 job.setUser(true);
105 job.schedule();
106 }
107
108 private class RefreshJob extends PrivilegedJob {
109 private TableViewer viewer;
110 private String filter;
111 private Display display;
112
113 public RefreshJob(String filter, TableViewer viewer, Display display) {
114 super("Get bundle list");
115 this.filter = filter;
116 this.viewer = viewer;
117 this.display = display;
118 }
119
120 @Override
121 protected IStatus doRun(IProgressMonitor progressMonitor) {
122 try {
123 JcrMonitor monitor = new EclipseJcrMonitor(progressMonitor);
124 monitor.beginTask("Getting bundle list", -1);
125 final List<Node> result = JcrUtils
126 .nodeIteratorToList(listBundleArtifacts(session, filter));
127
128 display.asyncExec(new Runnable() {
129 public void run() {
130 viewer.setInput(result);
131 }
132 });
133 } catch (Exception e) {
134 return new Status(IStatus.ERROR, DistPlugin.PLUGIN_ID,
135 "Cannot get bundle list", e);
136 }
137 return Status.OK_STATUS;
138 }
139 }
140
141 @Override
142 protected void createFormContent(IManagedForm managedForm) {
143 ScrolledForm form = managedForm.getForm();
144 tk = managedForm.getToolkit();
145
146 // Main Layout
147 GridLayout layout = new GridLayout(1, false);
148 Composite body = form.getBody();
149 body.setLayout(layout);
150
151 // Meta info about current workspace
152 Composite header = tk.createComposite(body);
153 header.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
154 createHeaderPart(form, header);
155
156 Composite modules = tk.createComposite(body);
157 modules.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
158 populateModuleSection(modules);
159 }
160
161 private void createHeaderPart(ScrolledForm form, Composite parent) {
162 GridLayout layout = new GridLayout(4, false);
163 // layout.marginWidth = layout.marginHeight = layout.verticalSpacing =
164 // 0;
165 // layout.horizontalSpacing = 2;
166 parent.setLayout(layout);
167
168 String wkspName = ((DistWkspEditorInput) getEditorInput())
169 .getWorkspaceName();
170 wkspName = wkspName.replaceAll("-", " ");
171 form.setText(wkspName);
172
173 String repoAlias = "";
174 Node repoNode = ((DistWorkspaceEditor) getEditor()).getRepoNode();
175 if (repoNode != null)
176 try {
177 repoAlias = repoNode.isNodeType(NodeType.MIX_TITLE) ? repoNode
178 .getProperty(Property.JCR_TITLE).getString() : repoNode
179 .getName();
180 } catch (RepositoryException e1) {
181 throw new SlcException("Unable to get repository alias ", e1);
182 }
183 else
184 repoAlias = " - ";
185
186 createLT(parent, "Repository alias", repoAlias);
187 createLT(parent, "URI",
188 ((DistWkspEditorInput) getEditorInput()).getUri());
189 }
190
191 private void populateModuleSection(Composite parent) {
192 GridLayout layout = new GridLayout(1, false);
193 layout.marginWidth = layout.horizontalSpacing = layout.horizontalSpacing = layout.marginHeight = 0;
194 parent.setLayout(layout);
195
196 Section section = tk.createSection(parent, Section.TITLE_BAR
197 | Section.DESCRIPTION);
198 section.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
199
200 section.setText("Artifacts");
201 section.setDescription("Search among all artifacts that are referenced in the current workspace");
202 section.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
203
204 Composite body = tk.createComposite(section);
205 layout = new GridLayout(1, false);
206 layout.marginWidth = layout.horizontalSpacing = layout.horizontalSpacing = layout.marginHeight = 0;
207 body.setLayout(new GridLayout());
208
209 // Filter
210 Composite filter = tk.createComposite(body);
211 filter.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
212 createFilterPart(filter);
213
214 // Table
215 Composite tableCmp = tk.createComposite(body);
216 tableCmp.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
217 createTableViewer(tableCmp);
218
219 section.setClient(body);
220 }
221
222 /** Build repository request */
223 private NodeIterator listBundleArtifacts(Session session, String filter)
224 throws RepositoryException {
225 QueryManager queryManager = session.getWorkspace().getQueryManager();
226 QueryObjectModelFactory factory = queryManager.getQOMFactory();
227
228 final String bundleArtifactsSelector = "bundleArtifacts";
229 Selector source = factory.selector(SlcTypes.SLC_BUNDLE_ARTIFACT,
230 bundleArtifactsSelector);
231
232 // Create a dynamic operand for each property on which we want to filter
233 DynamicOperand symbNameDO = factory.propertyValue(
234 source.getSelectorName(), SlcNames.SLC_SYMBOLIC_NAME);
235 DynamicOperand versionDO = factory.propertyValue(
236 source.getSelectorName(), SlcNames.SLC_BUNDLE_VERSION);
237 DynamicOperand nameDO = factory.propertyValue(source.getSelectorName(),
238 DistConstants.SLC_BUNDLE_NAME);
239
240 // Default Constraint: no source artifacts
241 Constraint defaultC = factory.not(factory.comparison(
242 symbNameDO,
243 QueryObjectModelFactory.JCR_OPERATOR_LIKE,
244 factory.literal(session.getValueFactory().createValue(
245 "%.source"))));
246
247 // Build constraints based the textArea content
248 if (filter != null && !"".equals(filter.trim())) {
249 // Parse the String
250 String[] strs = filter.trim().split(" ");
251 for (String token : strs) {
252 token = token.replace('*', '%');
253 StaticOperand so = factory.literal(session.getValueFactory()
254 .createValue("%" + token + "%"));
255
256 Constraint currC = factory.comparison(symbNameDO,
257 QueryObjectModelFactory.JCR_OPERATOR_LIKE, so);
258 currC = factory.or(currC, factory.comparison(versionDO,
259 QueryObjectModelFactory.JCR_OPERATOR_LIKE, so));
260 currC = factory.or(currC, factory.comparison(nameDO,
261 QueryObjectModelFactory.JCR_OPERATOR_LIKE, so));
262
263 defaultC = factory.and(defaultC, currC);
264 }
265 }
266
267 Ordering order = factory.descending(factory.propertyValue(
268 bundleArtifactsSelector, SlcNames.SLC_BUNDLE_VERSION));
269 Ordering order2 = factory.ascending(factory.propertyValue(
270 bundleArtifactsSelector, SlcNames.SLC_SYMBOLIC_NAME));
271 Ordering[] orderings = { order, order2 };
272
273 QueryObjectModel query = factory.createQuery(source, defaultC,
274 orderings, null);
275
276 QueryResult result = query.execute();
277 return result.getNodes();
278
279 }
280
281 private Text createLT(Composite parent, String labelValue, String textValue) {
282 Label label = new Label(parent, SWT.RIGHT);
283 label.setText(labelValue);
284 // label.setFont(EclipseUiUtils.getBoldFont(parent));
285 label.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false));
286
287 // Add a trailing space to workaround a display glitch in RAP 1.3
288 Text text = new Text(parent, SWT.LEFT); // | SWT.BORDER
289 text.setText(textValue + " ");
290 text.setEditable(false);
291 return text;
292 }
293
294 private void createFilterPart(Composite parent) {
295 GridLayout layout = new GridLayout();
296 layout.marginWidth = layout.marginHeight = layout.verticalSpacing = 0;
297 layout.horizontalSpacing = 5;
298 parent.setLayout(layout);
299
300 // Text Area to filter
301 artifactTxt = tk.createText(parent, "", SWT.BORDER | SWT.SINGLE
302 | SWT.SEARCH | SWT.CANCEL);
303 artifactTxt.setMessage(FILTER_HELP_MSG);
304 GridData gd = new GridData(SWT.FILL, SWT.FILL, false, false);
305 gd.grabExcessHorizontalSpace = true;
306 artifactTxt.setLayoutData(gd);
307 artifactTxt.addModifyListener(new ModifyListener() {
308 private static final long serialVersionUID = -2422321852703208573L;
309
310 public void modifyText(ModifyEvent event) {
311 if ("".equals(artifactTxt.getText().trim()))
312 asynchronousRefresh();
313 else
314 refreshFilteredList();
315 }
316 });
317 }
318
319 private void refreshFilteredList() {
320 List<Node> nodes;
321 try {
322 nodes = JcrUtils.nodeIteratorToList(listBundleArtifacts(session,
323 artifactTxt.getText()));
324 viewer.setInput(nodes);
325 } catch (RepositoryException e) {
326 throw new SlcException("Unable to list bundles", e);
327 }
328 }
329
330 private void createTableViewer(Composite parent) {
331 parent.setLayout(new FillLayout());
332 // helpers to enable sorting by column
333 List<String> propertiesList = new ArrayList<String>();
334 List<Integer> propertyTypesList = new ArrayList<Integer>();
335
336 // Define the TableViewer
337 viewer = new TableViewer(parent, SWT.MULTI | SWT.H_SCROLL
338 | SWT.V_SCROLL | SWT.FULL_SELECTION | SWT.BORDER);
339
340 // Name
341 TableViewerColumn col = new TableViewerColumn(viewer, SWT.NONE);
342 col.getColumn().setWidth(300);
343 col.getColumn().setText("Name");
344 col.setLabelProvider(new ColumnLabelProvider() {
345 private static final long serialVersionUID = -760226161605987538L;
346
347 @Override
348 public String getText(Object element) {
349 return JcrUtils.get((Node) element,
350 DistConstants.SLC_BUNDLE_NAME);
351 }
352 });
353 col.getColumn().addSelectionListener(getSelectionAdapter(0));
354 propertiesList.add(DistConstants.SLC_BUNDLE_NAME);
355 propertyTypesList.add(PropertyType.STRING);
356
357 // Symbolic name
358 col = new TableViewerColumn(viewer, SWT.V_SCROLL);
359 col.getColumn().setWidth(300);
360 col.getColumn().setText("Symbolic Name");
361 col.setLabelProvider(new ColumnLabelProvider() {
362 private static final long serialVersionUID = 4431447542158431355L;
363
364 @Override
365 public String getText(Object element) {
366 return JcrUtils.get((Node) element, SLC_SYMBOLIC_NAME);
367 }
368 });
369 col.getColumn().addSelectionListener(getSelectionAdapter(1));
370 propertiesList.add(SLC_SYMBOLIC_NAME);
371 propertyTypesList.add(PropertyType.STRING);
372
373 // Version
374 col = new TableViewerColumn(viewer, SWT.NONE);
375 col.getColumn().setWidth(130);
376 col.getColumn().setText("Version");
377 col.setLabelProvider(new ColumnLabelProvider() {
378 private static final long serialVersionUID = -5616215547236158504L;
379
380 @Override
381 public String getText(Object element) {
382 return JcrUtils.get((Node) element, SLC_BUNDLE_VERSION);
383 }
384 });
385 col.getColumn().addSelectionListener(getSelectionAdapter(2));
386 propertiesList.add(SLC_BUNDLE_VERSION);
387 propertyTypesList.add(DistNodeViewerComparator.VERSION_TYPE);
388
389 final Table table = viewer.getTable();
390 table.setHeaderVisible(true);
391 table.setLinesVisible(true);
392
393 viewer.setContentProvider(new DistributionsContentProvider());
394 getSite().setSelectionProvider(viewer);
395
396 comparator = new DistNodeViewerComparator(2,
397 DistNodeViewerComparator.ASCENDING, propertiesList,
398 propertyTypesList);
399 viewer.setComparator(comparator);
400
401 // Context Menu
402 MenuManager menuManager = new MenuManager();
403 Menu menu = menuManager.createContextMenu(viewer.getTable());
404 menuManager.addMenuListener(new IMenuListener() {
405 private static final long serialVersionUID = -3886983092940055195L;
406
407 public void menuAboutToShow(IMenuManager manager) {
408 contextMenuAboutToShow(manager);
409 }
410 });
411 viewer.getTable().setMenu(menu);
412 getSite().registerContextMenu(menuManager, viewer);
413
414 // Double click
415 viewer.addDoubleClickListener(new DoubleClickListener());
416 }
417
418 @Override
419 public void setFocus() {
420 viewer.getTable().setFocus();
421 }
422
423 /** force refresh of the artifact list */
424 public void refresh() {
425 asynchronousRefresh();
426 }
427
428 /** Programmatically configure the context menu */
429 protected void contextMenuAboutToShow(IMenuManager menuManager) {
430 IWorkbenchWindow window = DistPlugin.getDefault().getWorkbench()
431 .getActiveWorkbenchWindow();
432 // Build conditions
433 // Delete selected artifacts
434 CommandUtils.refreshCommand(menuManager, window, DeleteArtifacts.ID,
435 DeleteArtifacts.DEFAULT_LABEL, DeleteArtifacts.DEFAULT_ICON,
436 true);
437 }
438
439 private SelectionAdapter getSelectionAdapter(final int index) {
440 SelectionAdapter selectionAdapter = new SelectionAdapter() {
441 private static final long serialVersionUID = 5515884441510882460L;
442
443 @Override
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;
450 } else {
451 dir = SWT.DOWN;
452 }
453 table.setSortDirection(dir);
454 table.setSortColumn(table.getColumn(index));
455 viewer.refresh();
456 }
457 };
458 return selectionAdapter;
459 }
460
461 /* LOCAL CLASSES */
462 private class DistributionsContentProvider implements
463 IStructuredContentProvider {
464 private static final long serialVersionUID = -635451814876234147L;
465
466 // we keep a cache of the Nodes in the content provider to be able to
467 // manage long request
468 private List<Node> nodes;
469
470 public void dispose() {
471 }
472
473 // We expect a list of nodes as a new input
474 @SuppressWarnings("unchecked")
475 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
476 nodes = (List<Node>) newInput;
477 }
478
479 public Object[] getElements(Object arg0) {
480 return nodes.toArray();
481 }
482 }
483
484 private class DoubleClickListener implements IDoubleClickListener {
485
486 public void doubleClick(DoubleClickEvent event) {
487 Object obj = ((IStructuredSelection) event.getSelection())
488 .getFirstElement();
489 if (obj instanceof Node) {
490 Node node = (Node) obj;
491 try {
492 if (node.isNodeType(SlcTypes.SLC_ARTIFACT)) {
493 DistWkspEditorInput dwip = (DistWkspEditorInput) formEditor
494 .getEditorInput();
495 Map<String, String> params = new HashMap<String, String>();
496 params.put(OpenModuleEditor.PARAM_REPO_NODE_PATH,
497 dwip.getRepoNodePath());
498 params.put(OpenModuleEditor.PARAM_REPO_URI,
499 dwip.getUri());
500 params.put(OpenModuleEditor.PARAM_WORKSPACE_NAME,
501 dwip.getWorkspaceName());
502 String path = node.getPath();
503 params.put(OpenModuleEditor.PARAM_MODULE_PATH, path);
504 CommandUtils.callCommand(OpenModuleEditor.ID, params);
505 }
506 } catch (RepositoryException re) {
507 throw new SlcException("Cannot get path for node " + node
508 + " while setting parameters for "
509 + "command OpenModuleEditor", re);
510 }
511
512 }
513 }
514 }
515 }