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