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