2 * Copyright (C) 2007-2012 Argeo GmbH
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package org
.argeo
.slc
.client
.ui
.dist
.editors
;
19 import java
.util
.ArrayList
;
20 import java
.util
.List
;
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
;
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
;
91 * Show all modules contained in a given modular distribution as filter-able
94 public class ModularDistVersionOverviewPage
extends FormPage
implements
97 final static String PAGE_ID
= "ModularDistVersionOverviewPage";
100 private Node modularDistribution
;
101 // private Node modularDistributionBase;
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";
110 public ModularDistVersionOverviewPage(FormEditor formEditor
, String title
,
111 Node modularDistribution
) {
112 super(formEditor
, PAGE_ID
, title
);
113 this.modularDistribution
= modularDistribution
;
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();
123 GridLayout layout
= new GridLayout(1, false);
124 layout
.marginWidth
= 5;
125 layout
.marginRight
= 15;
126 layout
.verticalSpacing
= 0;
127 body
.setLayout(layout
);
129 form
.setText(modularDistribution
130 .hasProperty(DistConstants
.SLC_BUNDLE_NAME
) ? modularDistribution
131 .getProperty(DistConstants
.SLC_BUNDLE_NAME
).getString()
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
);
144 Composite header
= tk
.createComposite(body
);
145 header
.setLayoutData(new GridData(SWT
.FILL
, SWT
.TOP
, true, false));
146 populateHeaderPart(header
);
148 Composite moduleTablePart
= tk
.createComposite(body
);
149 moduleTablePart
.setLayoutData(new GridData(SWT
.FILL
, SWT
.FILL
, true,
151 populateModuleTablePart(moduleTablePart
);
154 private void populateHeaderPart(Composite parent
) {
155 GridLayout layout
= new GridLayout(6, false);
156 // layout.marginWidth = layout.horizontalSpacing = layout.marginHeight =
158 layout
.horizontalSpacing
= 10;
159 parent
.setLayout(layout
);
161 // 1st Line: Category, name version
165 modularDistribution
.hasProperty(SlcNames
.SLC_GROUP_ID
) ? modularDistribution
166 .getProperty(SlcNames
.SLC_GROUP_ID
).getString()
171 modularDistribution
.hasProperty(SlcNames
.SLC_SYMBOLIC_NAME
) ? modularDistribution
172 .getProperty(SlcNames
.SLC_SYMBOLIC_NAME
)
178 .hasProperty(SlcNames
.SLC_BUNDLE_VERSION
) ? modularDistribution
179 .getProperty(SlcNames
.SLC_BUNDLE_VERSION
)
182 // 2nd Line: Vendor, licence, sources
187 .hasProperty(DistConstants
.SLC_BUNDLE_VENDOR
) ? modularDistribution
188 .getProperty(DistConstants
.SLC_BUNDLE_VENDOR
)
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
);
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));
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));
209 text
.setEditable(false);
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(),
220 link
.addHyperlinkListener(new AbstractHyperlinkListener() {
222 public void linkActivated(HyperlinkEvent e
) {
224 IWorkbenchBrowserSupport browserSupport
= PlatformUI
225 .getWorkbench().getBrowserSupport();
226 IWebBrowser browser
= browserSupport
228 IWorkbenchBrowserSupport
.LOCATION_BAR
229 | IWorkbenchBrowserSupport
.NAVIGATION_BAR
,
230 "SLC Distribution browser",
231 "SLC Distribution browser",
233 browser
.openURL(new URL(link
.getText()));
234 } catch (Exception ex
) {
235 throw new SlcException("error opening browser", ex
); //$NON-NLS-1$
240 tk
.createLabel(parent
, "", SWT
.NONE
);
243 // helper to check if sources are available
244 private void addSourceSourcesLink(Composite parent
) {
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");
252 Node sourcesNode
= modularDistribution
.getSession().getNode(
255 Label label
= tk
.createLabel(parent
, "Sources", SWT
.RIGHT
);
256 label
.setLayoutData(new GridData(SWT
.RIGHT
, SWT
.CENTER
, false,
259 final Hyperlink link
= tk
.createHyperlink(parent
, sourcesNode
260 .getProperty(SlcNames
.SLC_SYMBOLIC_NAME
).getString(),
262 link
.addHyperlinkListener(new AbstractHyperlinkListener() {
264 public void linkActivated(HyperlinkEvent e
) {
266 System
.out
.println("CLICK on Sources link");
267 } catch (Exception ex
) {
268 throw new SlcException("error opening browser", ex
); //$NON-NLS-1$
274 } catch (RepositoryException e
) {
275 throw new SlcException("Unable to configure sources link for "
276 + modularDistribution
, e
);
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
);
287 Label label
= tk
.createLabel(parent
,
288 "Modules included in the current distribution", SWT
.NONE
);
289 label
.setFont(EclipseUiUtils
.getBoldFont(parent
));
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
);
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.
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
);
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
) {
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
);
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
>();
338 // Define the TableViewer
339 viewer
= new TableViewer(parent
, SWT
.MULTI
| SWT
.H_SCROLL
340 | SWT
.V_SCROLL
| SWT
.FULL_SELECTION
| SWT
.BORDER
);
343 TableViewerColumn col
= new TableViewerColumn(viewer
, SWT
.NONE
);
344 col
.getColumn().setWidth(220);
345 col
.getColumn().setText("Category");
346 col
.setLabelProvider(new ColumnLabelProvider() {
348 public String
getText(Object element
) {
349 return JcrUtils
.get((Node
) element
, SlcNames
.SLC_CATEGORY
);
352 col
.getColumn().addSelectionListener(getSelectionAdapter(0));
353 propertiesList
.add(SlcNames
.SLC_CATEGORY
);
354 propertyTypesList
.add(PropertyType
.STRING
);
357 col
= new TableViewerColumn(viewer
, SWT
.NONE
);
358 col
.getColumn().setWidth(220);
359 col
.getColumn().setText("Name");
360 col
.setLabelProvider(new ColumnLabelProvider() {
362 public String
getText(Object element
) {
363 return JcrUtils
.get((Node
) element
, SLC_NAME
);
366 col
.getColumn().addSelectionListener(getSelectionAdapter(1));
367 propertiesList
.add(SLC_NAME
);
368 propertyTypesList
.add(PropertyType
.STRING
);
371 col
= new TableViewerColumn(viewer
, SWT
.NONE
);
372 col
.getColumn().setWidth(160);
373 col
.getColumn().setText("Version");
374 col
.setLabelProvider(new ColumnLabelProvider() {
376 public String
getText(Object element
) {
377 return JcrUtils
.get((Node
) element
, SLC_VERSION
);
380 col
.getColumn().addSelectionListener(getSelectionAdapter(2));
381 propertiesList
.add(SLC_VERSION
);
382 propertyTypesList
.add(PropertyType
.STRING
);
384 final Table table
= viewer
.getTable();
385 table
.setHeaderVisible(true);
386 table
.setLinesVisible(true);
388 viewer
.setContentProvider(new DistributionsContentProvider());
389 getSite().setSelectionProvider(viewer
);
391 comparator
= new NodeViewerComparator(2,
392 NodeViewerComparator
.ASCENDING
, propertiesList
,
394 viewer
.setComparator(comparator
);
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
);
404 viewer
.getTable().setMenu(menu
);
405 getSite().registerContextMenu(menuManager
, viewer
);
408 viewer
.addDoubleClickListener(new DoubleClickListener());
411 private void refresh() {
412 final List
<Node
> result
= JcrUtils
413 .nodeIteratorToList(listBundleArtifacts());
414 viewer
.setInput(result
);
417 /** Build repository request */
418 private NodeIterator
listBundleArtifacts() {
420 Session session
= modularDistribution
.getSession();
421 QueryManager queryManager
= session
.getWorkspace()
423 QueryObjectModelFactory factory
= queryManager
.getQOMFactory();
425 Selector source
= factory
.selector(SlcTypes
.SLC_MODULE_COORDINATES
,
426 SlcTypes
.SLC_MODULE_COORDINATES
);
428 // Create a dynamic operand for each property on which we want to
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
);
437 String path
= modularDistribution
.getPath() + "/"
438 + SlcNames
.SLC_MODULES
;
440 // Default Constraint: correct children
441 Constraint defaultC
= factory
.descendantNode(
442 source
.getSelectorName(), path
);
444 String filter
= filterTxt
.getText();
446 // Build constraints based the textArea content
447 if (filter
!= null && !"".equals(filter
.trim())) {
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
+ "%"));
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
));
462 defaultC
= factory
.and(defaultC
, currC
);
466 QueryObjectModel query
= factory
.createQuery(source
, defaultC
,
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
);
477 public void setFocus() {
478 viewer
.getTable().setFocus();
481 /** Programmatically configure the context menu */
482 protected void contextMenuAboutToShow(IMenuManager menuManager
) {
483 IWorkbenchWindow window
= DistPlugin
.getDefault().getWorkbench()
484 .getActiveWorkbenchWindow();
486 // Delete selected artifacts
487 CommandUtils
.refreshCommand(menuManager
, window
, DeleteArtifacts
.ID
,
488 DeleteArtifacts
.DEFAULT_LABEL
, DeleteArtifacts
.DEFAULT_ICON
,
492 private SelectionAdapter
getSelectionAdapter(final int index
) {
493 SelectionAdapter selectionAdapter
= new SelectionAdapter() {
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
;
504 table
.setSortDirection(dir
);
505 table
.setSortColumn(table
.getColumn(index
));
509 return selectionAdapter
;
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
;
519 public void dispose() {
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
;
528 public Object
[] getElements(Object arg0
) {
529 return nodes
.toArray();
533 private class DoubleClickListener
implements IDoubleClickListener
{
535 public void doubleClick(DoubleClickEvent event
) {
536 Object obj
= ((IStructuredSelection
) event
.getSelection())
539 if (obj
instanceof Node
) {
540 Node node
= (Node
) obj
;
541 if (node
.isNodeType(SlcTypes
.SLC_BUNDLE_ARTIFACT
)) {
542 GenericBundleEditorInput gaei
= new GenericBundleEditorInput(
544 DistPlugin
.getDefault().getWorkbench()
545 .getActiveWorkbenchWindow().getActivePage()
546 .openEditor(gaei
, GenericBundleEditor
.ID
);
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",
561 // * UI Trick to put scroll bar on the table rather than on the scrollform
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);
571 // // when table height is less than 200 px, we let the scroll bar on the
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);
577 // table.setLayoutData(gd);
578 // getManagedForm().reflow(true);
582 public void setActive(boolean active
) {
583 super.setActive(active
);