]> 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/ModularDistVersionOverviewPage.java
38b3a8b463e01efcf04fc91b91da55b0aa95a5de
[gpl/argeo-slc.git] / plugins / org.argeo.slc.client.ui.dist / src / main / java / org / argeo / slc / client / ui / dist / editors / ModularDistVersionOverviewPage.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.net.URL;
19 import java.util.ArrayList;
20 import java.util.List;
21
22 import javax.jcr.Node;
23 import javax.jcr.NodeIterator;
24 import javax.jcr.PropertyType;
25 import javax.jcr.RepositoryException;
26 import javax.jcr.Session;
27 import javax.jcr.query.QueryManager;
28 import javax.jcr.query.QueryResult;
29 import javax.jcr.query.qom.Constraint;
30 import javax.jcr.query.qom.DynamicOperand;
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.eclipse.ui.EclipseUiUtils;
38 import org.argeo.eclipse.ui.utils.CommandUtils;
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.commands.DeleteArtifacts;
45 import org.argeo.slc.client.ui.dist.utils.AbstractHyperlinkListener;
46 import org.argeo.slc.client.ui.dist.utils.NodeViewerComparator;
47 import org.argeo.slc.jcr.SlcNames;
48 import org.argeo.slc.jcr.SlcTypes;
49 import org.argeo.slc.repo.RepoConstants;
50 import org.argeo.slc.repo.RepoUtils;
51 import org.eclipse.jface.action.IMenuListener;
52 import org.eclipse.jface.action.IMenuManager;
53 import org.eclipse.jface.action.MenuManager;
54 import org.eclipse.jface.dialogs.IMessageProvider;
55 import org.eclipse.jface.viewers.ColumnLabelProvider;
56 import org.eclipse.jface.viewers.DoubleClickEvent;
57 import org.eclipse.jface.viewers.IDoubleClickListener;
58 import org.eclipse.jface.viewers.IStructuredContentProvider;
59 import org.eclipse.jface.viewers.IStructuredSelection;
60 import org.eclipse.jface.viewers.TableViewer;
61 import org.eclipse.jface.viewers.TableViewerColumn;
62 import org.eclipse.jface.viewers.Viewer;
63 import org.eclipse.swt.SWT;
64 import org.eclipse.swt.events.ModifyEvent;
65 import org.eclipse.swt.events.ModifyListener;
66 import org.eclipse.swt.events.SelectionAdapter;
67 import org.eclipse.swt.events.SelectionEvent;
68 import org.eclipse.swt.layout.FillLayout;
69 import org.eclipse.swt.layout.GridData;
70 import org.eclipse.swt.layout.GridLayout;
71 import org.eclipse.swt.widgets.Button;
72 import org.eclipse.swt.widgets.Composite;
73 import org.eclipse.swt.widgets.Label;
74 import org.eclipse.swt.widgets.Menu;
75 import org.eclipse.swt.widgets.Table;
76 import org.eclipse.swt.widgets.Text;
77 import org.eclipse.ui.IWorkbenchWindow;
78 import org.eclipse.ui.PartInitException;
79 import org.eclipse.ui.PlatformUI;
80 import org.eclipse.ui.browser.IWebBrowser;
81 import org.eclipse.ui.browser.IWorkbenchBrowserSupport;
82 import org.eclipse.ui.forms.IManagedForm;
83 import org.eclipse.ui.forms.editor.FormEditor;
84 import org.eclipse.ui.forms.editor.FormPage;
85 import org.eclipse.ui.forms.events.HyperlinkEvent;
86 import org.eclipse.ui.forms.widgets.FormToolkit;
87 import org.eclipse.ui.forms.widgets.Hyperlink;
88 import org.eclipse.ui.forms.widgets.ScrolledForm;
89
90 /**
91 * Show all modules contained in a given modular distribution as filter-able
92 * table
93 */
94 public class ModularDistVersionOverviewPage extends FormPage implements
95 SlcNames {
96
97 final static String PAGE_ID = "ModularDistVersionOverviewPage";
98
99 // Business Objects
100 private Node modularDistribution;
101 // private Node modularDistributionBase;
102
103 // This page widgets
104 private NodeViewerComparator comparator;
105 private TableViewer viewer;
106 private FormToolkit tk;
107 private Text filterTxt;
108 private final static String FILTER_HELP_MSG = "Enter filter criterion separated by a space";
109
110 public ModularDistVersionOverviewPage(FormEditor formEditor, String title,
111 Node modularDistribution) {
112 super(formEditor, PAGE_ID, title);
113 this.modularDistribution = modularDistribution;
114 }
115
116 @Override
117 protected void createFormContent(IManagedForm managedForm) {
118 // General settings for this page
119 ScrolledForm form = managedForm.getForm();
120 tk = managedForm.getToolkit();
121 Composite body = form.getBody();
122
123 GridLayout layout = new GridLayout(1, false);
124 layout.marginWidth = 5;
125 layout.marginRight = 15;
126 layout.verticalSpacing = 0;
127 body.setLayout(layout);
128 try {
129 form.setText(modularDistribution.hasProperty(SlcNames.SLC_NAME) ? modularDistribution
130 .getProperty(SlcNames.SLC_NAME).getString() : "");
131 form.setMessage(
132 modularDistribution
133 .hasProperty(SlcNames.SLC_BUNDLE_DESCRIPTION) ? modularDistribution
134 .getProperty(SlcNames.SLC_BUNDLE_DESCRIPTION)
135 .getString() : "", IMessageProvider.NONE);
136 } catch (RepositoryException re) {
137 throw new SlcException("Unable to get bundle name for node "
138 + modularDistribution, re);
139 }
140
141 // Main layout
142 Composite header = tk.createComposite(body);
143 header.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
144 populateHeaderPart(header);
145
146 Composite moduleTablePart = tk.createComposite(body);
147 moduleTablePart.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true,
148 true));
149 populateModuleTablePart(moduleTablePart);
150 }
151
152 private void populateHeaderPart(Composite parent) {
153 GridLayout layout = new GridLayout(6, false);
154 // layout.marginWidth = layout.horizontalSpacing = layout.marginHeight =
155 // 0;
156 layout.horizontalSpacing = 10;
157 parent.setLayout(layout);
158 try {
159 // 1st Line: Category, name version
160 createLT(
161 parent,
162 "Category",
163 modularDistribution.hasProperty(SlcNames.SLC_CATEGORY) ? modularDistribution
164 .getProperty(SlcNames.SLC_CATEGORY).getString()
165 : "");
166 createLT(
167 parent,
168 "Name",
169 modularDistribution.hasProperty(SlcNames.SLC_NAME) ? modularDistribution
170 .getProperty(SlcNames.SLC_NAME).getString() : "");
171 createLT(
172 parent,
173 "Version",
174 modularDistribution.hasProperty(SlcNames.SLC_VERSION) ? modularDistribution
175 .getProperty(SlcNames.SLC_VERSION).getString() : "");
176
177 // 2nd Line: Vendor, licence, sources
178 createLT(
179 parent,
180 "Vendor",
181 modularDistribution
182 .hasProperty(DistConstants.SLC_BUNDLE_VENDOR) ? modularDistribution
183 .getProperty(DistConstants.SLC_BUNDLE_VENDOR)
184 .getString() : "N/A");
185
186 createHyperlink(parent, "Licence", DistConstants.SLC_BUNDLE_LICENCE);
187 addSourceSourcesLink(parent);
188 } catch (RepositoryException re) {
189 throw new SlcException("Unable to get bundle name for node "
190 + modularDistribution, re);
191 }
192
193 }
194
195 private Text createLT(Composite parent, String labelValue, String textValue) {
196 Label label = tk.createLabel(parent, labelValue, SWT.RIGHT);
197 // label.setFont(EclipseUiUtils.getBoldFont(parent));
198 label.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false));
199
200 // Add a trailing space to workaround a display glitch in RAP 1.3
201 Text text = tk.createText(parent, textValue + " ", SWT.LEFT);
202 text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
203
204 text.setEditable(false);
205 return text;
206 }
207
208 private void createHyperlink(Composite parent, String label,
209 String jcrPropName) throws RepositoryException {
210 tk.createLabel(parent, label, SWT.NONE);
211 if (modularDistribution.hasProperty(jcrPropName)) {
212 final Hyperlink link = tk.createHyperlink(parent,
213 modularDistribution.getProperty(jcrPropName).getString(),
214 SWT.NONE);
215 link.addHyperlinkListener(new AbstractHyperlinkListener() {
216 @Override
217 public void linkActivated(HyperlinkEvent e) {
218 try {
219 IWorkbenchBrowserSupport browserSupport = PlatformUI
220 .getWorkbench().getBrowserSupport();
221 IWebBrowser browser = browserSupport
222 .createBrowser(
223 IWorkbenchBrowserSupport.LOCATION_BAR
224 | IWorkbenchBrowserSupport.NAVIGATION_BAR,
225 "SLC Distribution browser",
226 "SLC Distribution browser",
227 "A tool tip");
228 browser.openURL(new URL(link.getText()));
229 } catch (Exception ex) {
230 throw new SlcException("error opening browser", ex); //$NON-NLS-1$
231 }
232 }
233 });
234 } else
235 tk.createLabel(parent, "N/A", SWT.NONE);
236 }
237
238 // helper to check if sources are available
239 private void addSourceSourcesLink(Composite parent) {
240 try {
241 String srcPath = RepoUtils.relatedPdeSourcePath(
242 RepoConstants.DEFAULT_ARTIFACTS_BASE_PATH,
243 modularDistribution);
244 if (!modularDistribution.getSession().nodeExists(srcPath)) {
245 createLT(parent, "Sources", "N/A");
246 } else {
247 Node sourcesNode = modularDistribution.getSession().getNode(
248 srcPath);
249
250 String srcName = null;
251 if (sourcesNode.hasProperty(SlcNames.SLC_SYMBOLIC_NAME))
252 srcName = sourcesNode.getProperty(
253 SlcNames.SLC_SYMBOLIC_NAME).getString();
254 else
255 srcName = sourcesNode.getName();
256 Label label = tk.createLabel(parent, "Sources", SWT.RIGHT);
257 label.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false,
258 false));
259 final Hyperlink link = tk.createHyperlink(parent, srcName,
260 SWT.NONE);
261 link.addHyperlinkListener(new AbstractHyperlinkListener() {
262 @Override
263 public void linkActivated(HyperlinkEvent e) {
264 try {
265 System.out.println("CLICK on Sources link");
266 } catch (Exception ex) {
267 throw new SlcException("error opening browser", ex); //$NON-NLS-1$
268 }
269 }
270 });
271
272 }
273 } catch (RepositoryException e) {
274 throw new SlcException("Unable to configure sources link for "
275 + modularDistribution, e);
276 }
277 }
278
279 private void populateModuleTablePart(Composite parent) {
280 GridLayout layout = new GridLayout(1, false);
281 layout.marginWidth = layout.horizontalSpacing = 0;
282 layout.verticalSpacing = 5;
283 layout.marginTop = 15;
284 parent.setLayout(layout);
285 // A sub title
286 Label label = tk.createLabel(parent,
287 "Modules included in the current distribution", SWT.NONE);
288 label.setFont(EclipseUiUtils.getBoldFont(parent));
289
290 // Add the filter section
291 Composite filterPart = tk.createComposite(parent);
292 filterPart.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
293 createFilterPart(filterPart);
294
295 // Add the table
296 Composite tablePart = tk.createComposite(parent);
297 tablePart.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
298 createTableViewer(tablePart);
299 // populate it on first pass.
300 refresh();
301
302 }
303
304 private void createFilterPart(Composite parent) {
305 GridLayout layout = new GridLayout(2, false);
306 layout.marginWidth = layout.marginHeight = layout.verticalSpacing = 0;
307 layout.horizontalSpacing = 5;
308 parent.setLayout(layout);
309
310 // Text Area to filter
311 filterTxt = tk.createText(parent, "", SWT.BORDER | SWT.SINGLE
312 | SWT.SEARCH | SWT.CANCEL);
313 filterTxt.setMessage(FILTER_HELP_MSG);
314 filterTxt.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
315 filterTxt.addModifyListener(new ModifyListener() {
316 public void modifyText(ModifyEvent event) {
317 refresh();
318 }
319 });
320
321 Button resetBtn = tk.createButton(parent, null, SWT.PUSH);
322 resetBtn.setImage(DistImages.IMG_CLEAR);
323 resetBtn.addSelectionListener(new SelectionAdapter() {
324 public void widgetSelected(SelectionEvent e) {
325 filterTxt.setText("");
326 filterTxt.setMessage(FILTER_HELP_MSG);
327 }
328 });
329 }
330
331 private void createTableViewer(Composite parent) {
332 parent.setLayout(new FillLayout());
333 // helpers to enable sorting by column
334 List<String> propertiesList = new ArrayList<String>();
335 List<Integer> propertyTypesList = new ArrayList<Integer>();
336
337 // Define the TableViewer
338 viewer = new TableViewer(parent, SWT.MULTI | SWT.H_SCROLL
339 | SWT.V_SCROLL | SWT.FULL_SELECTION | SWT.BORDER);
340
341 // Name
342 TableViewerColumn col = new TableViewerColumn(viewer, SWT.NONE);
343 col.getColumn().setWidth(220);
344 col.getColumn().setText("Category");
345 col.setLabelProvider(new ColumnLabelProvider() {
346 @Override
347 public String getText(Object element) {
348 return JcrUtils.get((Node) element, SlcNames.SLC_CATEGORY);
349 }
350 });
351 col.getColumn().addSelectionListener(getSelectionAdapter(0));
352 propertiesList.add(SlcNames.SLC_CATEGORY);
353 propertyTypesList.add(PropertyType.STRING);
354
355 // Symbolic name
356 col = new TableViewerColumn(viewer, SWT.NONE);
357 col.getColumn().setWidth(220);
358 col.getColumn().setText("Name");
359 col.setLabelProvider(new ColumnLabelProvider() {
360 @Override
361 public String getText(Object element) {
362 return JcrUtils.get((Node) element, SLC_NAME);
363 }
364 });
365 col.getColumn().addSelectionListener(getSelectionAdapter(1));
366 propertiesList.add(SLC_NAME);
367 propertyTypesList.add(PropertyType.STRING);
368
369 // Version
370 col = new TableViewerColumn(viewer, SWT.NONE);
371 col.getColumn().setWidth(160);
372 col.getColumn().setText("Version");
373 col.setLabelProvider(new ColumnLabelProvider() {
374 @Override
375 public String getText(Object element) {
376 return JcrUtils.get((Node) element, SLC_VERSION);
377 }
378 });
379 col.getColumn().addSelectionListener(getSelectionAdapter(2));
380 propertiesList.add(SLC_VERSION);
381 propertyTypesList.add(PropertyType.STRING);
382
383 final Table table = viewer.getTable();
384 table.setHeaderVisible(true);
385 table.setLinesVisible(true);
386
387 viewer.setContentProvider(new DistributionsContentProvider());
388 getSite().setSelectionProvider(viewer);
389
390 comparator = new NodeViewerComparator(2,
391 NodeViewerComparator.ASCENDING, propertiesList,
392 propertyTypesList);
393 viewer.setComparator(comparator);
394
395 // Context Menu
396 MenuManager menuManager = new MenuManager();
397 Menu menu = menuManager.createContextMenu(viewer.getTable());
398 menuManager.addMenuListener(new IMenuListener() {
399 public void menuAboutToShow(IMenuManager manager) {
400 contextMenuAboutToShow(manager);
401 }
402 });
403 viewer.getTable().setMenu(menu);
404 getSite().registerContextMenu(menuManager, viewer);
405
406 // Double click
407 viewer.addDoubleClickListener(new DoubleClickListener());
408 }
409
410 private void refresh() {
411 final List<Node> result = JcrUtils
412 .nodeIteratorToList(listBundleArtifacts());
413 viewer.setInput(result);
414 }
415
416 /** Build repository request */
417 private NodeIterator listBundleArtifacts() {
418 try {
419 Session session = modularDistribution.getSession();
420 QueryManager queryManager = session.getWorkspace()
421 .getQueryManager();
422 QueryObjectModelFactory factory = queryManager.getQOMFactory();
423
424 Selector source = factory.selector(SlcTypes.SLC_MODULE_COORDINATES,
425 SlcTypes.SLC_MODULE_COORDINATES);
426
427 // Create a dynamic operand for each property on which we want to
428 // filter
429 DynamicOperand catDO = factory.propertyValue(
430 source.getSelectorName(), SlcNames.SLC_CATEGORY);
431 DynamicOperand nameDO = factory.propertyValue(
432 source.getSelectorName(), SlcNames.SLC_NAME);
433 DynamicOperand versionDO = factory.propertyValue(
434 source.getSelectorName(), SlcNames.SLC_VERSION);
435
436 String path = modularDistribution.getPath() + "/"
437 + SlcNames.SLC_MODULES;
438
439 // Default Constraint: correct children
440 Constraint defaultC = factory.descendantNode(
441 source.getSelectorName(), path);
442
443 String filter = filterTxt.getText();
444
445 // Build constraints based the textArea content
446 if (filter != null && !"".equals(filter.trim())) {
447 // Parse the String
448 String[] strs = filter.trim().split(" ");
449 for (String token : strs) {
450 token = token.replace('*', '%');
451 StaticOperand so = factory.literal(session
452 .getValueFactory().createValue("%" + token + "%"));
453
454 Constraint currC = factory.comparison(catDO,
455 QueryObjectModelFactory.JCR_OPERATOR_LIKE, so);
456 currC = factory.or(currC, factory.comparison(versionDO,
457 QueryObjectModelFactory.JCR_OPERATOR_LIKE, so));
458 currC = factory.or(currC, factory.comparison(nameDO,
459 QueryObjectModelFactory.JCR_OPERATOR_LIKE, so));
460
461 defaultC = factory.and(defaultC, currC);
462 }
463 }
464
465 QueryObjectModel query = factory.createQuery(source, defaultC,
466 null, null);
467 QueryResult result = query.execute();
468 return result.getNodes();
469 } catch (RepositoryException re) {
470 throw new SlcException("Unable to refresh module list for node "
471 + modularDistribution, re);
472 }
473 }
474
475 @Override
476 public void setFocus() {
477 viewer.getTable().setFocus();
478 }
479
480 /** Programmatically configure the context menu */
481 protected void contextMenuAboutToShow(IMenuManager menuManager) {
482 IWorkbenchWindow window = DistPlugin.getDefault().getWorkbench()
483 .getActiveWorkbenchWindow();
484 // Build conditions
485 // Delete selected artifacts
486 CommandUtils.refreshCommand(menuManager, window, DeleteArtifacts.ID,
487 DeleteArtifacts.DEFAULT_LABEL, DeleteArtifacts.DEFAULT_ICON,
488 true);
489 }
490
491 private SelectionAdapter getSelectionAdapter(final int index) {
492 SelectionAdapter selectionAdapter = new SelectionAdapter() {
493 @Override
494 public void widgetSelected(SelectionEvent e) {
495 Table table = viewer.getTable();
496 comparator.setColumn(index);
497 int dir = table.getSortDirection();
498 if (table.getSortColumn() == table.getColumn(index)) {
499 dir = dir == SWT.UP ? SWT.DOWN : SWT.UP;
500 } else {
501 dir = SWT.DOWN;
502 }
503 table.setSortDirection(dir);
504 table.setSortColumn(table.getColumn(index));
505 viewer.refresh();
506 }
507 };
508 return selectionAdapter;
509 }
510
511 /* LOCAL CLASSES */
512 private class DistributionsContentProvider implements
513 IStructuredContentProvider {
514 // we keep a cache of the Nodes in the content provider to be able to
515 // manage long request
516 private List<Node> nodes;
517
518 public void dispose() {
519 }
520
521 // We expect a list of nodes as a new input
522 @SuppressWarnings("unchecked")
523 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
524 nodes = (List<Node>) newInput;
525 }
526
527 public Object[] getElements(Object arg0) {
528 return nodes.toArray();
529 }
530 }
531
532 private class DoubleClickListener implements IDoubleClickListener {
533
534 public void doubleClick(DoubleClickEvent event) {
535 Object obj = ((IStructuredSelection) event.getSelection())
536 .getFirstElement();
537 try {
538 if (obj instanceof Node) {
539 Node node = (Node) obj;
540 if (node.isNodeType(SlcTypes.SLC_BUNDLE_ARTIFACT)) {
541 GenericBundleEditorInput gaei = new GenericBundleEditorInput(
542 node);
543 DistPlugin.getDefault().getWorkbench()
544 .getActiveWorkbenchWindow().getActivePage()
545 .openEditor(gaei, GenericBundleEditor.ID);
546 }
547 }
548 } catch (RepositoryException re) {
549 throw new ArgeoException(
550 "Repository error while getting node info", re);
551 } catch (PartInitException pie) {
552 throw new ArgeoException(
553 "Unexepected exception while opening artifact editor",
554 pie);
555 }
556 }
557 }
558
559 // /**
560 // * UI Trick to put scroll bar on the table rather than on the scrollform
561 // */
562 // private void refreshLayout() {
563 // // Compute desired table size
564 // int maxH = getManagedForm().getForm().getSize().y;
565 // int maxW = getManagedForm().getForm().getParent().getSize().x;
566 // maxH = maxH - header.getSize().y;
567 // final Table table = viewer.getTable();
568 // GridData gd = new GridData(SWT.LEFT, SWT.TOP, true, true);
569 //
570 // // when table height is less than 200 px, we let the scroll bar on the
571 // // scrollForm
572 // // FIXME substract some spare space. There is room here for optimization
573 // gd.heightHint = Math.max(maxH - 35, 200);
574 // gd.widthHint = Math.max(maxW - 35, 200);
575 //
576 // table.setLayoutData(gd);
577 // getManagedForm().reflow(true);
578 // }
579
580 @Override
581 public void setActive(boolean active) {
582 super.setActive(active);
583 if (active) {
584 // refreshLayout();
585 }
586 }
587 }