]> 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
Clean session management.
[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.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;
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 NodeViewerComparator comparator;
106 private TableViewer viewer;
107
108 // private Composite header;
109 private Text artifactTxt;
110 private final static String FILTER_HELP_MSG = "Search bundles in the current workspace";
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 // filter text
175 Composite filter = tk.createComposite(body);
176 filter.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
177 createFilterPart(filter);
178
179 // the table
180 Composite tableCmp = tk.createComposite(body);
181 tableCmp.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
182 createTableViewer(tableCmp);
183 }
184
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();
190
191 final String bundleArtifactsSelector = "bundleArtifacts";
192 Selector source = factory.selector(SlcTypes.SLC_BUNDLE_ARTIFACT,
193 bundleArtifactsSelector);
194
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);
202
203 // Default Constraint: no source artifacts
204 Constraint defaultC = factory.not(factory.comparison(
205 symbNameDO,
206 QueryObjectModelFactory.JCR_OPERATOR_LIKE,
207 factory.literal(session.getValueFactory().createValue(
208 "%.source"))));
209
210 // Build constraints based the textArea content
211 if (filter != null && !"".equals(filter.trim())) {
212 // Parse the String
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 + "%"));
218
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));
225
226 defaultC = factory.and(defaultC, currC);
227 }
228 }
229
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 };
235
236 QueryObjectModel query = factory.createQuery(source, defaultC,
237 orderings, null);
238
239 QueryResult result = query.execute();
240 return result.getNodes();
241
242 }
243
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);
249
250 String wkspName = ((DistWkspEditorInput) getEditorInput())
251 .getWorkspaceName();
252 // wkspName = "Workspace " + wkspName;
253 form.setText(wkspName);
254
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);
259
260 String repoAlias = "";
261 Node repoNode = ((DistWorkspaceEditor) getEditor()).getRepoNode();
262 if (repoNode != null)
263 try {
264 repoAlias = repoNode.isNodeType(NodeType.MIX_TITLE) ? repoNode
265 .getProperty(Property.JCR_TITLE).getString() : repoNode
266 .getName();
267 } catch (RepositoryException e1) {
268 throw new SlcException("Unable to get repository alias ", e1);
269 }
270 else
271 repoAlias = " - ";
272
273 createLT(parent, "Repository alias", repoAlias);
274 createLT(parent, "URI",
275 ((DistWkspEditorInput) getEditorInput()).getUri());
276 }
277
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));
282
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);
286 return text;
287 }
288
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);
294
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();
306 else
307 refreshFilteredList();
308 }
309 });
310
311 Button resetBtn = tk.createButton(parent, null, SWT.PUSH);
312 resetBtn.setImage(DistImages.IMG_REPO_READONLY);
313 resetBtn.addSelectionListener(new SelectionListener() {
314
315 public void widgetSelected(SelectionEvent e) {
316 resetFilter();
317 }
318
319 public void widgetDefaultSelected(SelectionEvent e) {
320 }
321 });
322 }
323
324 private void resetFilter() {
325 artifactTxt.setText("");
326 artifactTxt.setMessage(FILTER_HELP_MSG);
327 }
328
329 private void refreshFilteredList() {
330 List<Node> nodes;
331 try {
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);
337 }
338 }
339
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>();
345
346 // Define the TableViewer
347 viewer = new TableViewer(parent, SWT.MULTI | SWT.H_SCROLL
348 | SWT.V_SCROLL | SWT.FULL_SELECTION | SWT.BORDER);
349
350 // Name
351 TableViewerColumn col = new TableViewerColumn(viewer, SWT.NONE);
352 col.getColumn().setWidth(300);
353 col.getColumn().setText("Name");
354 col.setLabelProvider(new ColumnLabelProvider() {
355 @Override
356 public String getText(Object element) {
357 return JcrUtils.get((Node) element,
358 DistConstants.SLC_BUNDLE_NAME);
359 }
360 });
361 col.getColumn().addSelectionListener(getSelectionAdapter(0));
362 propertiesList.add(DistConstants.SLC_BUNDLE_NAME);
363 propertyTypesList.add(PropertyType.STRING);
364
365 // Symbolic name
366 col = new TableViewerColumn(viewer, SWT.V_SCROLL);
367 col.getColumn().setWidth(300);
368 col.getColumn().setText("Symbolic Name");
369 col.setLabelProvider(new ColumnLabelProvider() {
370 @Override
371 public String getText(Object element) {
372 return JcrUtils.get((Node) element, SLC_SYMBOLIC_NAME);
373 }
374 });
375 col.getColumn().addSelectionListener(getSelectionAdapter(1));
376 propertiesList.add(SLC_SYMBOLIC_NAME);
377 propertyTypesList.add(PropertyType.STRING);
378
379 // Version
380 col = new TableViewerColumn(viewer, SWT.NONE);
381 col.getColumn().setWidth(130);
382 col.getColumn().setText("Version");
383 col.setLabelProvider(new ColumnLabelProvider() {
384 @Override
385 public String getText(Object element) {
386 return JcrUtils.get((Node) element, SLC_BUNDLE_VERSION);
387 }
388 });
389 col.getColumn().addSelectionListener(getSelectionAdapter(2));
390 propertiesList.add(SLC_BUNDLE_VERSION);
391 propertyTypesList.add(PropertyType.STRING);
392
393 final Table table = viewer.getTable();
394 table.setHeaderVisible(true);
395 table.setLinesVisible(true);
396
397 viewer.setContentProvider(new DistributionsContentProvider());
398 getSite().setSelectionProvider(viewer);
399
400 comparator = new NodeViewerComparator(2,
401 NodeViewerComparator.ASCENDING, propertiesList,
402 propertyTypesList);
403 viewer.setComparator(comparator);
404
405 // Context Menu
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);
411 }
412 });
413 viewer.getTable().setMenu(menu);
414 getSite().registerContextMenu(menuManager, viewer);
415
416 // Double click
417 viewer.addDoubleClickListener(new DoubleClickListener());
418 }
419
420 @Override
421 public void setFocus() {
422 viewer.getTable().setFocus();
423 }
424
425 /** force refresh of the artifact list */
426 public void refresh() {
427 asynchronousRefresh();
428 }
429
430 /** Programmatically configure the context menu */
431 protected void contextMenuAboutToShow(IMenuManager menuManager) {
432 IWorkbenchWindow window = DistPlugin.getDefault().getWorkbench()
433 .getActiveWorkbenchWindow();
434 // Build conditions
435 // Delete selected artifacts
436 CommandUtils.refreshCommand(menuManager, window, DeleteArtifacts.ID,
437 DeleteArtifacts.DEFAULT_LABEL, DeleteArtifacts.DEFAULT_ICON,
438 true);
439 }
440
441 private SelectionAdapter getSelectionAdapter(final int index) {
442 SelectionAdapter selectionAdapter = new SelectionAdapter() {
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 // we keep a cache of the Nodes in the content provider to be able to
465 // manage long request
466 private List<Node> nodes;
467
468 public void dispose() {
469 }
470
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;
475 }
476
477 public Object[] getElements(Object arg0) {
478 return nodes.toArray();
479 }
480 }
481
482 private class DoubleClickListener implements IDoubleClickListener {
483
484 public void doubleClick(DoubleClickEvent event) {
485 Object obj = ((IStructuredSelection) event.getSelection())
486 .getFirstElement();
487 if (obj instanceof Node) {
488 Node node = (Node) obj;
489 try {
490 if (node.isNodeType(SlcTypes.SLC_ARTIFACT)) {
491 DistWkspEditorInput dwip = (DistWkspEditorInput) formEditor
492 .getEditorInput();
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,
497 dwip.getUri());
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);
503 }
504 } catch (RepositoryException re) {
505 throw new SlcException("Cannot get path for node " + node
506 + " while setting parameters for "
507 + "command OpenModuleEditor", re);
508 }
509
510 }
511 }
512 }
513
514 /**
515 * UI Trick to put scroll bar on the table rather than on the scrollform
516 */
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);
524 //
525 // // when table height is less than 200 px, we let the scroll bar on
526 // the
527 // // scrollForm
528 // // FIXME substract some spare space. There is room here for
529 // optimization
530 // gd.heightHint = Math.max(maxH - 35, 200);
531 // gd.widthHint = Math.max(maxW - 35, 200);
532 //
533 // table.setLayoutData(gd);
534 // getManagedForm().reflow(true);
535 }
536
537 @Override
538 public void setActive(boolean active) {
539 super.setActive(active);
540 if (active) {
541 refreshLayout();
542 }
543 }
544 }