]> 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/DistributionOverviewPage.java
Clean: fix typos, update comments
[gpl/argeo-slc.git] / plugins / org.argeo.slc.client.ui.dist / src / main / java / org / argeo / slc / client / ui / dist / editors / DistributionOverviewPage.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.List;
20
21 import javax.jcr.Node;
22 import javax.jcr.NodeIterator;
23 import javax.jcr.PropertyType;
24 import javax.jcr.RepositoryException;
25 import javax.jcr.Session;
26 import javax.jcr.query.QueryManager;
27 import javax.jcr.query.QueryResult;
28 import javax.jcr.query.qom.Constraint;
29 import javax.jcr.query.qom.DynamicOperand;
30 import javax.jcr.query.qom.Ordering;
31 import javax.jcr.query.qom.QueryObjectModel;
32 import javax.jcr.query.qom.QueryObjectModelFactory;
33 import javax.jcr.query.qom.Selector;
34 import javax.jcr.query.qom.StaticOperand;
35
36 import org.argeo.ArgeoException;
37 import org.argeo.ArgeoMonitor;
38 import org.argeo.eclipse.ui.EclipseArgeoMonitor;
39 import org.argeo.eclipse.ui.utils.CommandUtils;
40 import org.argeo.jcr.JcrUtils;
41 import org.argeo.slc.SlcException;
42 import org.argeo.slc.client.ui.dist.DistConstants;
43 import org.argeo.slc.client.ui.dist.DistImages;
44 import org.argeo.slc.client.ui.dist.DistPlugin;
45 import org.argeo.slc.client.ui.dist.PrivilegedJob;
46 import org.argeo.slc.client.ui.dist.commands.DeleteArtifacts;
47 import org.argeo.slc.client.ui.dist.utils.NodeViewerComparator;
48 import org.argeo.slc.jcr.SlcNames;
49 import org.argeo.slc.jcr.SlcTypes;
50 import org.eclipse.core.runtime.IProgressMonitor;
51 import org.eclipse.core.runtime.IStatus;
52 import org.eclipse.core.runtime.Status;
53 import org.eclipse.jface.action.IMenuListener;
54 import org.eclipse.jface.action.IMenuManager;
55 import org.eclipse.jface.action.MenuManager;
56 import org.eclipse.jface.viewers.ColumnLabelProvider;
57 import org.eclipse.jface.viewers.DoubleClickEvent;
58 import org.eclipse.jface.viewers.IDoubleClickListener;
59 import org.eclipse.jface.viewers.IStructuredContentProvider;
60 import org.eclipse.jface.viewers.IStructuredSelection;
61 import org.eclipse.jface.viewers.TableViewer;
62 import org.eclipse.jface.viewers.TableViewerColumn;
63 import org.eclipse.jface.viewers.Viewer;
64 import org.eclipse.swt.SWT;
65 import org.eclipse.swt.events.ControlEvent;
66 import org.eclipse.swt.events.ControlListener;
67 import org.eclipse.swt.events.ModifyEvent;
68 import org.eclipse.swt.events.ModifyListener;
69 import org.eclipse.swt.events.SelectionAdapter;
70 import org.eclipse.swt.events.SelectionEvent;
71 import org.eclipse.swt.events.SelectionListener;
72 import org.eclipse.swt.layout.GridData;
73 import org.eclipse.swt.layout.GridLayout;
74 import org.eclipse.swt.widgets.Button;
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.PartInitException;
83 import org.eclipse.ui.forms.IManagedForm;
84 import org.eclipse.ui.forms.editor.FormEditor;
85 import org.eclipse.ui.forms.editor.FormPage;
86 import org.eclipse.ui.forms.widgets.FormToolkit;
87 import org.eclipse.ui.forms.widgets.ScrolledForm;
88
89 /** Show all bundles contained in a given workspace as filter-able table */
90 public class DistributionOverviewPage extends FormPage implements SlcNames {
91 // final private static Log log = LogFactory
92 // .getLog(DistributionOverviewPage.class);
93
94 final static String PAGE_ID = "distributionOverviewPage";
95
96 // Business Objects
97 private Session session;
98
99 // This page widgets
100 private NodeViewerComparator comparator;
101 private TableViewer viewer;
102 private FormToolkit tk;
103 private Composite header;
104 private Text artifactTxt;
105 private final static String FILTER_HELP_MSG = "Enter filter criterion separated by a space";
106
107 public DistributionOverviewPage(FormEditor formEditor, String title,
108 Session session) {
109 super(formEditor, PAGE_ID, title);
110 this.session = session;
111 }
112
113 private void asynchronousRefresh() {
114 RefreshJob job = new RefreshJob(artifactTxt.getText(), viewer,
115 getSite().getShell().getDisplay());
116 job.setUser(true);
117 job.schedule();
118 }
119
120 private class RefreshJob extends PrivilegedJob {
121 private TableViewer viewer;
122 private String filter;
123 private Display display;
124
125 public RefreshJob(String filter, TableViewer viewer, Display display) {
126 super("Get bundle list");
127 this.filter = filter;
128 this.viewer = viewer;
129 this.display = display;
130 }
131
132 @Override
133 protected IStatus doRun(IProgressMonitor progressMonitor) {
134 try {
135 ArgeoMonitor monitor = new EclipseArgeoMonitor(progressMonitor);
136 monitor.beginTask("Getting bundle list", -1);
137 final List<Node> result = JcrUtils
138 .nodeIteratorToList(listBundleArtifacts(session, filter));
139
140 display.asyncExec(new Runnable() {
141 public void run() {
142 viewer.setInput(result);
143 }
144 });
145 } catch (Exception e) {
146 return new Status(IStatus.ERROR, DistPlugin.ID,
147 "Cannot get bundle list", e);
148 }
149 return Status.OK_STATUS;
150 }
151 }
152
153 @Override
154 protected void createFormContent(IManagedForm managedForm) {
155 ScrolledForm form = managedForm.getForm();
156 tk = managedForm.getToolkit();
157
158 // Main Layout
159 GridLayout layout = new GridLayout(1, false);
160 Composite body = form.getBody();
161 body.setLayout(layout);
162
163 // Add the filter section
164 createFilterPart(body);
165 // Add the table
166 createTableViewer(body);
167 // viewer.setInput(null);
168 // Add a listener to enable custom resize process
169 form.addControlListener(new ControlListener() {
170 // form.addListener(SWT.RESIZE, new Listener() does not work
171 public void controlResized(ControlEvent e) {
172 refreshLayout();
173 }
174
175 public void controlMoved(ControlEvent e) {
176 }
177 });
178 asynchronousRefresh();
179 }
180
181 /** Build repository request */
182 private NodeIterator listBundleArtifacts(Session session, String filter)
183 throws RepositoryException {
184 QueryManager queryManager = session.getWorkspace().getQueryManager();
185 QueryObjectModelFactory factory = queryManager.getQOMFactory();
186
187 final String bundleArtifactsSelector = "bundleArtifacts";
188 Selector source = factory.selector(SlcTypes.SLC_BUNDLE_ARTIFACT,
189 bundleArtifactsSelector);
190
191 // Create a dynamic operand for each property on which we want to filter
192 DynamicOperand symbNameDO = factory.propertyValue(
193 source.getSelectorName(), SlcNames.SLC_SYMBOLIC_NAME);
194 DynamicOperand versionDO = factory.propertyValue(
195 source.getSelectorName(), SlcNames.SLC_BUNDLE_VERSION);
196 DynamicOperand nameDO = factory.propertyValue(source.getSelectorName(),
197 DistConstants.SLC_BUNDLE_NAME);
198
199 // Default Constraint: no source artifacts
200 Constraint defaultC = factory.not(factory.comparison(
201 symbNameDO,
202 QueryObjectModelFactory.JCR_OPERATOR_LIKE,
203 factory.literal(session.getValueFactory().createValue(
204 "%.source"))));
205
206 // Build constraints based the textArea content
207 if (filter != null && !"".equals(filter.trim())) {
208 // Parse the String
209 String[] strs = filter.trim().split(" ");
210 for (String token : strs) {
211 token = token.replace('*', '%');
212 StaticOperand so = factory.literal(session.getValueFactory()
213 .createValue("%" + token + "%"));
214
215 Constraint currC = factory.comparison(symbNameDO,
216 QueryObjectModelFactory.JCR_OPERATOR_LIKE, so);
217 currC = factory.or(currC, factory.comparison(versionDO,
218 QueryObjectModelFactory.JCR_OPERATOR_LIKE, so));
219 currC = factory.or(currC, factory.comparison(nameDO,
220 QueryObjectModelFactory.JCR_OPERATOR_LIKE, so));
221
222 defaultC = factory.and(defaultC, currC);
223 }
224 }
225
226 Ordering order = factory.descending(factory.propertyValue(
227 bundleArtifactsSelector, SlcNames.SLC_BUNDLE_VERSION));
228 Ordering order2 = factory.ascending(factory.propertyValue(
229 bundleArtifactsSelector, SlcNames.SLC_SYMBOLIC_NAME));
230 Ordering[] orderings = { order, order2 };
231
232 QueryObjectModel query = factory.createQuery(source, defaultC,
233 orderings, null);
234
235 QueryResult result = query.execute();
236 return result.getNodes();
237
238 }
239
240 private void createFilterPart(Composite parent) {
241 header = tk.createComposite(parent);
242 GridLayout layout = new GridLayout(2, false);
243 layout.marginWidth = layout.marginHeight = layout.verticalSpacing = 0;
244 layout.horizontalSpacing = 5;
245 header.setLayout(layout);
246 GridData gd = new GridData(SWT.FILL, SWT.FILL, false, false);
247 header.setLayoutData(gd);
248
249 // Title: some meta information
250 String desc = ((DistributionEditorInput) getEditorInput())
251 .getRepositoryDescription();
252 Label lbl = tk.createLabel(header, desc, SWT.NONE);
253
254 gd = new GridData(SWT.FILL, SWT.FILL, false, false);
255 gd.horizontalSpan = 2;
256 lbl.setLayoutData(gd);
257
258 // Text Area to filter
259 artifactTxt = tk.createText(header, "", SWT.BORDER | SWT.SINGLE
260 | SWT.SEARCH | SWT.CANCEL);
261 artifactTxt.setMessage(FILTER_HELP_MSG);
262 gd = new GridData(SWT.FILL, SWT.FILL, false, false);
263 gd.grabExcessHorizontalSpace = true;
264 artifactTxt.setLayoutData(gd);
265 artifactTxt.addModifyListener(new ModifyListener() {
266 public void modifyText(ModifyEvent event) {
267 if ("".equals(artifactTxt.getText().trim()))
268 asynchronousRefresh();
269 else
270 refreshFilteredList();
271 }
272 });
273
274 Button resetBtn = tk.createButton(header, null, SWT.PUSH);
275 resetBtn.setImage(DistImages.IMG_REPO_READONLY);
276 resetBtn.addSelectionListener(new SelectionListener() {
277
278 public void widgetSelected(SelectionEvent e) {
279 resetFilter();
280 }
281
282 public void widgetDefaultSelected(SelectionEvent e) {
283 }
284 });
285
286 }
287
288 private void resetFilter() {
289 artifactTxt.setText("");
290 artifactTxt.setMessage(FILTER_HELP_MSG);
291 }
292
293 private void refreshFilteredList() {
294 List<Node> nodes;
295 try {
296 nodes = JcrUtils.nodeIteratorToList(listBundleArtifacts(session,
297 artifactTxt.getText()));
298 viewer.setInput(nodes);
299 } catch (RepositoryException e) {
300 throw new SlcException("Unable to list bundles", e);
301 }
302 }
303
304 private void createTableViewer(Composite parent) {
305 // helpers to enable sorting by column
306 List<String> propertiesList = new ArrayList<String>();
307 List<Integer> propertyTypesList = new ArrayList<Integer>();
308
309 // Define the TableViewer
310 viewer = new TableViewer(parent, SWT.MULTI | SWT.H_SCROLL
311 | SWT.V_SCROLL | SWT.FULL_SELECTION | SWT.BORDER);
312
313 // Name
314 TableViewerColumn col = new TableViewerColumn(viewer, SWT.NONE);
315 col.getColumn().setWidth(300);
316 col.getColumn().setText("Name");
317 col.setLabelProvider(new ColumnLabelProvider() {
318 @Override
319 public String getText(Object element) {
320 return JcrUtils.get((Node) element,
321 DistConstants.SLC_BUNDLE_NAME);
322 }
323 });
324 col.getColumn().addSelectionListener(getSelectionAdapter(0));
325 propertiesList.add(DistConstants.SLC_BUNDLE_NAME);
326 propertyTypesList.add(PropertyType.STRING);
327
328 // Symbolic name
329 col = new TableViewerColumn(viewer, SWT.V_SCROLL);
330 col.getColumn().setWidth(300);
331 col.getColumn().setText("Symbolic Name");
332 col.setLabelProvider(new ColumnLabelProvider() {
333 @Override
334 public String getText(Object element) {
335 return JcrUtils.get((Node) element, SLC_SYMBOLIC_NAME);
336 }
337 });
338 col.getColumn().addSelectionListener(getSelectionAdapter(1));
339 propertiesList.add(SLC_SYMBOLIC_NAME);
340 propertyTypesList.add(PropertyType.STRING);
341
342 // Version
343 col = new TableViewerColumn(viewer, SWT.NONE);
344 col.getColumn().setWidth(130);
345 col.getColumn().setText("Version");
346 col.setLabelProvider(new ColumnLabelProvider() {
347 @Override
348 public String getText(Object element) {
349 return JcrUtils.get((Node) element, SLC_BUNDLE_VERSION);
350 }
351 });
352 col.getColumn().addSelectionListener(getSelectionAdapter(2));
353 propertiesList.add(SLC_BUNDLE_VERSION);
354 propertyTypesList.add(PropertyType.STRING);
355
356 final Table table = viewer.getTable();
357 table.setHeaderVisible(true);
358 table.setLinesVisible(true);
359
360 viewer.setContentProvider(new DistributionsContentProvider());
361 getSite().setSelectionProvider(viewer);
362
363 comparator = new NodeViewerComparator(2,
364 NodeViewerComparator.ASCENDING, propertiesList,
365 propertyTypesList);
366 viewer.setComparator(comparator);
367
368 // Context Menu
369 MenuManager menuManager = new MenuManager();
370 Menu menu = menuManager.createContextMenu(viewer.getTable());
371 menuManager.addMenuListener(new IMenuListener() {
372 public void menuAboutToShow(IMenuManager manager) {
373 contextMenuAboutToShow(manager);
374 }
375 });
376 viewer.getTable().setMenu(menu);
377 getSite().registerContextMenu(menuManager, viewer);
378
379 // Double click
380 viewer.addDoubleClickListener(new DoubleClickListener());
381 }
382
383 @Override
384 public void setFocus() {
385 viewer.getTable().setFocus();
386 }
387
388 /** force refresh of the artifact list */
389 public void refresh() {
390 asynchronousRefresh();
391 }
392
393 /** Programmatically configure the context menu */
394 protected void contextMenuAboutToShow(IMenuManager menuManager) {
395 IWorkbenchWindow window = DistPlugin.getDefault().getWorkbench()
396 .getActiveWorkbenchWindow();
397 // Build conditions
398 // Delete selected artifacts
399 CommandUtils.refreshCommand(menuManager, window, DeleteArtifacts.ID,
400 DeleteArtifacts.DEFAULT_LABEL, DeleteArtifacts.DEFAULT_ICON,
401 true);
402 }
403
404 private SelectionAdapter getSelectionAdapter(final int index) {
405 SelectionAdapter selectionAdapter = new SelectionAdapter() {
406 @Override
407 public void widgetSelected(SelectionEvent e) {
408 Table table = viewer.getTable();
409 comparator.setColumn(index);
410 int dir = table.getSortDirection();
411 if (table.getSortColumn() == table.getColumn(index)) {
412 dir = dir == SWT.UP ? SWT.DOWN : SWT.UP;
413 } else {
414 dir = SWT.DOWN;
415 }
416 table.setSortDirection(dir);
417 table.setSortColumn(table.getColumn(index));
418 viewer.refresh();
419 }
420 };
421 return selectionAdapter;
422 }
423
424 /* LOCAL CLASSES */
425 private class DistributionsContentProvider implements
426 IStructuredContentProvider {
427 // we keep a cache of the Nodes in the content provider to be able to
428 // manage long request
429 private List<Node> nodes;
430
431 public void dispose() {
432 }
433
434 // We expect a list of nodes as a new input
435 @SuppressWarnings("unchecked")
436 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
437 nodes = (List<Node>) newInput;
438 }
439
440 public Object[] getElements(Object arg0) {
441 return nodes.toArray();
442 }
443 }
444
445 private class DoubleClickListener implements IDoubleClickListener {
446
447 public void doubleClick(DoubleClickEvent event) {
448 Object obj = ((IStructuredSelection) event.getSelection())
449 .getFirstElement();
450 try {
451 if (obj instanceof Node) {
452 Node node = (Node) obj;
453 if (node.isNodeType(SlcTypes.SLC_BUNDLE_ARTIFACT)) {
454 GenericBundleEditorInput gaei = new GenericBundleEditorInput(
455 node);
456 DistPlugin.getDefault().getWorkbench()
457 .getActiveWorkbenchWindow().getActivePage()
458 .openEditor(gaei, GenericBundleEditor.ID);
459 }
460 }
461 } catch (RepositoryException re) {
462 throw new ArgeoException(
463 "Repository error while getting node info", re);
464 } catch (PartInitException pie) {
465 throw new ArgeoException(
466 "Unexepected exception while opening artifact editor",
467 pie);
468 }
469 }
470 }
471
472 /**
473 * UI Trick to put scroll bar on the table rather than on the scrollform
474 */
475 private void refreshLayout() {
476 // Compute desired table size
477 int maxH = getManagedForm().getForm().getSize().y;
478 int maxW = getManagedForm().getForm().getParent().getSize().x;
479 maxH = maxH - header.getSize().y;
480 final Table table = viewer.getTable();
481 GridData gd = new GridData(SWT.LEFT, SWT.TOP, true, true);
482
483 // when table height is less than 200 px, we let the scroll bar on the
484 // scrollForm
485 // FIXME substract some spare space. There is room here for optimization
486 gd.heightHint = Math.max(maxH - 35, 200);
487 gd.widthHint = Math.max(maxW - 35, 200);
488
489 table.setLayoutData(gd);
490 getManagedForm().reflow(true);
491 }
492
493 @Override
494 public void setActive(boolean active) {
495 super.setActive(active);
496 if (active) {
497 refreshLayout();
498 }
499 }
500
501 }