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