]> 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
work on fetch and normalization.
[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 /** 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 layout.marginWidth = layout.marginHeight = layout.verticalSpacing = 0;
243 layout.horizontalSpacing = 5;
244 header.setLayout(layout);
245 GridData gd = new GridData(SWT.FILL, SWT.FILL, false, false);
246 header.setLayoutData(gd);
247
248 // Title: some meta information
249 String desc = ((DistributionEditorInput) getEditorInput())
250 .getRepositoryDescription();
251 Label lbl = tk.createLabel(header, desc, SWT.NONE);
252
253 gd = new GridData(SWT.FILL, SWT.FILL, false, false);
254 gd.horizontalSpan = 2;
255 lbl.setLayoutData(gd);
256
257 // Text Area to filter
258 artifactTxt = tk.createText(header, "", SWT.BORDER | SWT.SINGLE
259 | SWT.SEARCH | SWT.CANCEL);
260 artifactTxt.setMessage(FILTER_HELP_MSG);
261 gd = new GridData(SWT.FILL, SWT.FILL, false, false);
262 gd.grabExcessHorizontalSpace = true;
263 artifactTxt.setLayoutData(gd);
264 artifactTxt.addModifyListener(new ModifyListener() {
265 public void modifyText(ModifyEvent event) {
266 if ("".equals(artifactTxt.getText().trim()))
267 asynchronousRefresh();
268 else
269 refreshFilteredList();
270 }
271 });
272
273 Button resetBtn = tk.createButton(header, null, SWT.PUSH);
274 resetBtn.setImage(DistImages.IMG_REPO_READONLY);
275 resetBtn.addSelectionListener(new SelectionListener() {
276
277 public void widgetSelected(SelectionEvent e) {
278 resetFilter();
279 }
280
281 public void widgetDefaultSelected(SelectionEvent e) {
282 }
283 });
284
285 }
286
287 private void resetFilter() {
288 artifactTxt.setText("");
289 artifactTxt.setMessage(FILTER_HELP_MSG);
290 }
291
292 private void refreshFilteredList() {
293 List<Node> nodes;
294 try {
295 nodes = JcrUtils.nodeIteratorToList(listBundleArtifacts(session,
296 artifactTxt.getText()));
297 viewer.setInput(nodes);
298 } catch (RepositoryException e) {
299 throw new SlcException("Unable to list bundles", e);
300 }
301 }
302
303 private void createTableViewer(Composite parent) {
304 // helpers to enable sorting by column
305 List<String> propertiesList = new ArrayList<String>();
306 List<Integer> propertyTypesList = new ArrayList<Integer>();
307
308 // Define the TableViewer
309 viewer = new TableViewer(parent, SWT.MULTI | SWT.H_SCROLL
310 | SWT.V_SCROLL | SWT.FULL_SELECTION | SWT.BORDER);
311
312 // Name
313 TableViewerColumn col = new TableViewerColumn(viewer, SWT.NONE);
314 col.getColumn().setWidth(300);
315 col.getColumn().setText("Name");
316 col.setLabelProvider(new ColumnLabelProvider() {
317 @Override
318 public String getText(Object element) {
319 return JcrUtils.get((Node) element,
320 DistConstants.SLC_BUNDLE_NAME);
321 }
322 });
323 col.getColumn().addSelectionListener(getSelectionAdapter(0));
324 propertiesList.add(DistConstants.SLC_BUNDLE_NAME);
325 propertyTypesList.add(PropertyType.STRING);
326
327 // Symbolic name
328 col = new TableViewerColumn(viewer, SWT.V_SCROLL);
329 col.getColumn().setWidth(300);
330 col.getColumn().setText("Symbolic Name");
331 col.setLabelProvider(new ColumnLabelProvider() {
332 @Override
333 public String getText(Object element) {
334 return JcrUtils.get((Node) element, SLC_SYMBOLIC_NAME);
335 }
336 });
337 col.getColumn().addSelectionListener(getSelectionAdapter(1));
338 propertiesList.add(SLC_SYMBOLIC_NAME);
339 propertyTypesList.add(PropertyType.STRING);
340
341 // Version
342 col = new TableViewerColumn(viewer, SWT.NONE);
343 col.getColumn().setWidth(130);
344 col.getColumn().setText("Version");
345 col.setLabelProvider(new ColumnLabelProvider() {
346 @Override
347 public String getText(Object element) {
348 return JcrUtils.get((Node) element, SLC_BUNDLE_VERSION);
349 }
350 });
351 col.getColumn().addSelectionListener(getSelectionAdapter(2));
352 propertiesList.add(SLC_BUNDLE_VERSION);
353 propertyTypesList.add(PropertyType.STRING);
354
355 final Table table = viewer.getTable();
356 table.setHeaderVisible(true);
357 table.setLinesVisible(true);
358
359 viewer.setContentProvider(new DistributionsContentProvider());
360 getSite().setSelectionProvider(viewer);
361
362 comparator = new NodeViewerComparator(2,
363 NodeViewerComparator.ASCENDING, propertiesList,
364 propertyTypesList);
365 viewer.setComparator(comparator);
366
367 // Context Menu
368 MenuManager menuManager = new MenuManager();
369 Menu menu = menuManager.createContextMenu(viewer.getTable());
370 menuManager.addMenuListener(new IMenuListener() {
371 public void menuAboutToShow(IMenuManager manager) {
372 contextMenuAboutToShow(manager);
373 }
374 });
375 viewer.getTable().setMenu(menu);
376 getSite().registerContextMenu(menuManager, viewer);
377
378 // Double click
379 viewer.addDoubleClickListener(new DoubleClickListener());
380 }
381
382 @Override
383 public void setFocus() {
384 viewer.getTable().setFocus();
385 }
386
387 /** force refresh of the artifact list */
388 public void refresh() {
389 asynchronousRefresh();
390 }
391
392 /** Programmatically configure the context menu */
393 protected void contextMenuAboutToShow(IMenuManager menuManager) {
394 IWorkbenchWindow window = DistPlugin.getDefault().getWorkbench()
395 .getActiveWorkbenchWindow();
396 // Build conditions
397 // Delete selected artifacts
398 CommandUtils.refreshCommand(menuManager, window, DeleteArtifacts.ID,
399 DeleteArtifacts.DEFAULT_LABEL, DeleteArtifacts.DEFAULT_ICON,
400 true);
401 }
402
403 private SelectionAdapter getSelectionAdapter(final int index) {
404 SelectionAdapter selectionAdapter = new SelectionAdapter() {
405 @Override
406 public void widgetSelected(SelectionEvent e) {
407 Table table = viewer.getTable();
408 comparator.setColumn(index);
409 int dir = table.getSortDirection();
410 if (table.getSortColumn() == table.getColumn(index)) {
411 dir = dir == SWT.UP ? SWT.DOWN : SWT.UP;
412 } else {
413 dir = SWT.DOWN;
414 }
415 table.setSortDirection(dir);
416 table.setSortColumn(table.getColumn(index));
417 viewer.refresh();
418 }
419 };
420 return selectionAdapter;
421 }
422
423 /* LOCAL CLASSES */
424 private class DistributionsContentProvider implements
425 IStructuredContentProvider {
426 // we keep a cache of the Nodes in the content provider to be able to
427 // manage long request
428 private List<Node> nodes;
429
430 public void dispose() {
431 }
432
433 // We expect a list of nodes as a new input
434 @SuppressWarnings("unchecked")
435 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
436 nodes = (List<Node>) newInput;
437 }
438
439 public Object[] getElements(Object arg0) {
440 return nodes.toArray();
441 }
442 }
443
444 private class DoubleClickListener implements IDoubleClickListener {
445
446 public void doubleClick(DoubleClickEvent event) {
447 Object obj = ((IStructuredSelection) event.getSelection())
448 .getFirstElement();
449 try {
450 if (obj instanceof Node) {
451 Node node = (Node) obj;
452 if (node.isNodeType(SlcTypes.SLC_BUNDLE_ARTIFACT)) {
453 GenericBundleEditorInput gaei = new GenericBundleEditorInput(
454 node);
455 DistPlugin.getDefault().getWorkbench()
456 .getActiveWorkbenchWindow().getActivePage()
457 .openEditor(gaei, GenericBundleEditor.ID);
458 }
459 }
460 } catch (RepositoryException re) {
461 throw new ArgeoException(
462 "Repository error while getting node info", re);
463 } catch (PartInitException pie) {
464 throw new ArgeoException(
465 "Unexepected exception while opening artifact editor",
466 pie);
467 }
468 }
469 }
470
471 /**
472 * UI Trick to put scroll bar on the table rather than on the scrollform
473 */
474 private void refreshLayout() {
475 // Compute desired table size
476 int maxH = getManagedForm().getForm().getSize().y;
477 int maxW = getManagedForm().getForm().getParent().getSize().x;
478 maxH = maxH - header.getSize().y;
479 final Table table = viewer.getTable();
480 GridData gd = new GridData(SWT.LEFT, SWT.TOP, true, true);
481
482 // when table height is less than 200 px, we let the scroll bar on the
483 // scrollForm
484 // FIXME substract some spare space. There is room here for optimization
485 gd.heightHint = Math.max(maxH - 35, 200);
486 gd.widthHint = Math.max(maxW - 35, 200);
487
488 table.setLayoutData(gd);
489 getManagedForm().reflow(true);
490 }
491
492 @Override
493 public void setActive(boolean active) {
494 super.setActive(active);
495 if (active) {
496 refreshLayout();
497 }
498 }
499
500 }