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
.HashMap
;
21 import java
.util
.List
;
24 import javax
.jcr
.Node
;
25 import javax
.jcr
.NodeIterator
;
26 import javax
.jcr
.PropertyType
;
27 import javax
.jcr
.RepositoryException
;
28 import javax
.jcr
.Session
;
29 import javax
.jcr
.query
.QueryManager
;
30 import javax
.jcr
.query
.QueryResult
;
31 import javax
.jcr
.query
.qom
.Constraint
;
32 import javax
.jcr
.query
.qom
.DynamicOperand
;
33 import javax
.jcr
.query
.qom
.QueryObjectModel
;
34 import javax
.jcr
.query
.qom
.QueryObjectModelFactory
;
35 import javax
.jcr
.query
.qom
.Selector
;
36 import javax
.jcr
.query
.qom
.StaticOperand
;
38 import org
.argeo
.eclipse
.ui
.EclipseUiUtils
;
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
.commands
.OpenModuleEditor
;
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
.argeo
.slc
.repo
.maven
.MavenConventionsUtils
;
52 import org
.eclipse
.jface
.dialogs
.IMessageProvider
;
53 import org
.eclipse
.jface
.viewers
.ColumnLabelProvider
;
54 import org
.eclipse
.jface
.viewers
.DoubleClickEvent
;
55 import org
.eclipse
.jface
.viewers
.IDoubleClickListener
;
56 import org
.eclipse
.jface
.viewers
.IStructuredContentProvider
;
57 import org
.eclipse
.jface
.viewers
.IStructuredSelection
;
58 import org
.eclipse
.jface
.viewers
.TableViewer
;
59 import org
.eclipse
.jface
.viewers
.TableViewerColumn
;
60 import org
.eclipse
.jface
.viewers
.Viewer
;
61 import org
.eclipse
.swt
.SWT
;
62 import org
.eclipse
.swt
.events
.ModifyEvent
;
63 import org
.eclipse
.swt
.events
.ModifyListener
;
64 import org
.eclipse
.swt
.events
.SelectionAdapter
;
65 import org
.eclipse
.swt
.events
.SelectionEvent
;
66 import org
.eclipse
.swt
.layout
.FillLayout
;
67 import org
.eclipse
.swt
.layout
.GridData
;
68 import org
.eclipse
.swt
.layout
.GridLayout
;
69 import org
.eclipse
.swt
.widgets
.Button
;
70 import org
.eclipse
.swt
.widgets
.Composite
;
71 import org
.eclipse
.swt
.widgets
.Label
;
72 import org
.eclipse
.swt
.widgets
.Table
;
73 import org
.eclipse
.swt
.widgets
.Text
;
74 import org
.eclipse
.ui
.PlatformUI
;
75 import org
.eclipse
.ui
.browser
.IWebBrowser
;
76 import org
.eclipse
.ui
.browser
.IWorkbenchBrowserSupport
;
77 import org
.eclipse
.ui
.forms
.IManagedForm
;
78 import org
.eclipse
.ui
.forms
.editor
.FormEditor
;
79 import org
.eclipse
.ui
.forms
.editor
.FormPage
;
80 import org
.eclipse
.ui
.forms
.events
.HyperlinkEvent
;
81 import org
.eclipse
.ui
.forms
.widgets
.FormToolkit
;
82 import org
.eclipse
.ui
.forms
.widgets
.Hyperlink
;
83 import org
.eclipse
.ui
.forms
.widgets
.ScrolledForm
;
84 import org
.sonatype
.aether
.artifact
.Artifact
;
85 import org
.sonatype
.aether
.util
.artifact
.DefaultArtifact
;
88 * Show all modules contained in a given modular distribution as filter-able
91 public class ModularDistVersionOverviewPage
extends FormPage
implements
94 final static String PAGE_ID
= "ModularDistVersionOverviewPage";
97 private Node modularDistribution
;
98 // private Node modularDistributionBase;
101 private NodeViewerComparator comparator
;
102 private TableViewer viewer
;
103 private FormToolkit tk
;
104 private Text filterTxt
;
105 private final static String FILTER_HELP_MSG
= "Enter filter criterion separated by a space";
107 public ModularDistVersionOverviewPage(FormEditor formEditor
, String title
,
108 Node modularDistribution
) {
109 super(formEditor
, PAGE_ID
, title
);
110 this.modularDistribution
= modularDistribution
;
114 protected void createFormContent(IManagedForm managedForm
) {
115 // General settings for this page
116 ScrolledForm form
= managedForm
.getForm();
117 tk
= managedForm
.getToolkit();
118 Composite body
= form
.getBody();
120 GridLayout layout
= new GridLayout(1, false);
121 layout
.marginWidth
= 5;
122 layout
.marginRight
= 15;
123 layout
.verticalSpacing
= 0;
124 body
.setLayout(layout
);
126 form
.setText(modularDistribution
.hasProperty(SlcNames
.SLC_NAME
) ? modularDistribution
127 .getProperty(SlcNames
.SLC_NAME
).getString() : "");
130 .hasProperty(DistConstants
.SLC_BUNDLE_DESCRIPTION
) ? modularDistribution
131 .getProperty(DistConstants
.SLC_BUNDLE_DESCRIPTION
)
132 .getString() : "", IMessageProvider
.NONE
);
133 } catch (RepositoryException re
) {
134 throw new SlcException("Unable to get bundle name for node "
135 + modularDistribution
, re
);
139 Composite header
= tk
.createComposite(body
);
140 header
.setLayoutData(new GridData(SWT
.FILL
, SWT
.TOP
, true, false));
141 populateHeaderPart(header
);
143 Composite moduleTablePart
= tk
.createComposite(body
);
144 moduleTablePart
.setLayoutData(new GridData(SWT
.FILL
, SWT
.FILL
, true,
146 populateModuleTablePart(moduleTablePart
);
149 private void populateHeaderPart(Composite parent
) {
150 GridLayout layout
= new GridLayout(6, false);
151 layout
.horizontalSpacing
= 10;
152 parent
.setLayout(layout
);
154 // 1st Line: Category, name version
158 modularDistribution
.hasProperty(SlcNames
.SLC_CATEGORY
) ? modularDistribution
159 .getProperty(SlcNames
.SLC_CATEGORY
).getString()
164 modularDistribution
.hasProperty(SlcNames
.SLC_NAME
) ? modularDistribution
165 .getProperty(SlcNames
.SLC_NAME
).getString() : "");
169 modularDistribution
.hasProperty(SlcNames
.SLC_VERSION
) ? modularDistribution
170 .getProperty(SlcNames
.SLC_VERSION
).getString() : "");
172 // 2nd Line: Vendor, licence, sources
177 .hasProperty(DistConstants
.SLC_BUNDLE_VENDOR
) ? modularDistribution
178 .getProperty(DistConstants
.SLC_BUNDLE_VENDOR
)
179 .getString() : "N/A");
181 createHyperlink(parent
, "Licence", DistConstants
.SLC_BUNDLE_LICENCE
);
182 addSourceSourcesLink(parent
);
183 } catch (RepositoryException re
) {
184 throw new SlcException("Unable to get bundle name for node "
185 + modularDistribution
, re
);
190 private Text
createLT(Composite parent
, String labelValue
, String textValue
) {
191 Label label
= tk
.createLabel(parent
, labelValue
, SWT
.RIGHT
);
192 // label.setFont(EclipseUiUtils.getBoldFont(parent));
193 label
.setLayoutData(new GridData(SWT
.RIGHT
, SWT
.CENTER
, false, false));
195 // Add a trailing space to workaround a display glitch in RAP 1.3
196 Text text
= new Text(parent
, SWT
.LEFT
);
197 text
.setText(textValue
+ " ");
198 text
.setLayoutData(new GridData(SWT
.FILL
, SWT
.CENTER
, true, false));
199 text
.setEditable(false);
203 private void createHyperlink(Composite parent
, String label
,
204 String jcrPropName
) throws RepositoryException
{
205 tk
.createLabel(parent
, label
, SWT
.NONE
);
206 if (modularDistribution
.hasProperty(jcrPropName
)) {
207 final Hyperlink link
= tk
.createHyperlink(parent
,
208 modularDistribution
.getProperty(jcrPropName
).getString(),
210 link
.addHyperlinkListener(new AbstractHyperlinkListener() {
212 public void linkActivated(HyperlinkEvent e
) {
214 IWorkbenchBrowserSupport browserSupport
= PlatformUI
215 .getWorkbench().getBrowserSupport();
216 IWebBrowser browser
= browserSupport
218 IWorkbenchBrowserSupport
.LOCATION_BAR
219 | IWorkbenchBrowserSupport
.NAVIGATION_BAR
,
220 "SLC Distribution browser",
221 "SLC Distribution browser",
223 browser
.openURL(new URL(link
.getText()));
224 } catch (Exception ex
) {
225 throw new SlcException("error opening browser", ex
); //$NON-NLS-1$
230 tk
.createLabel(parent
, "N/A", SWT
.NONE
);
233 // helper to check if sources are available
234 private void addSourceSourcesLink(Composite parent
) {
236 String srcPath
= RepoUtils
.relatedPdeSourcePath(
237 RepoConstants
.DEFAULT_ARTIFACTS_BASE_PATH
,
238 modularDistribution
);
239 if (!modularDistribution
.getSession().nodeExists(srcPath
)) {
240 createLT(parent
, "Sources", "N/A");
242 Node sourcesNode
= modularDistribution
.getSession().getNode(
245 String srcName
= null;
246 if (sourcesNode
.hasProperty(SlcNames
.SLC_SYMBOLIC_NAME
))
247 srcName
= sourcesNode
.getProperty(
248 SlcNames
.SLC_SYMBOLIC_NAME
).getString();
250 srcName
= sourcesNode
.getName();
251 Label label
= tk
.createLabel(parent
, "Sources", SWT
.RIGHT
);
252 label
.setLayoutData(new GridData(SWT
.RIGHT
, SWT
.CENTER
, false,
254 final Hyperlink link
= tk
.createHyperlink(parent
, srcName
,
256 link
.addHyperlinkListener(new AbstractHyperlinkListener() {
258 public void linkActivated(HyperlinkEvent e
) {
260 System
.out
.println("CLICK on Sources link");
261 } catch (Exception ex
) {
262 throw new SlcException("error opening browser", ex
); //$NON-NLS-1$
268 } catch (RepositoryException e
) {
269 throw new SlcException("Unable to configure sources link for "
270 + modularDistribution
, e
);
274 private void populateModuleTablePart(Composite parent
) {
275 GridLayout layout
= new GridLayout(1, false);
276 layout
.marginWidth
= layout
.horizontalSpacing
= 0;
277 layout
.verticalSpacing
= 5;
278 layout
.marginTop
= 15;
279 parent
.setLayout(layout
);
281 Label label
= tk
.createLabel(parent
,
282 "Modules included in the current distribution", SWT
.NONE
);
283 label
.setFont(EclipseUiUtils
.getBoldFont(parent
));
285 // Add the filter section
286 Composite filterPart
= tk
.createComposite(parent
);
287 filterPart
.setLayoutData(new GridData(SWT
.FILL
, SWT
.TOP
, true, false));
288 createFilterPart(filterPart
);
291 Composite tablePart
= tk
.createComposite(parent
);
292 tablePart
.setLayoutData(new GridData(SWT
.FILL
, SWT
.FILL
, true, true));
293 createTableViewer(tablePart
);
294 // populate it on first pass.
298 private void createFilterPart(Composite parent
) {
299 GridLayout layout
= new GridLayout(2, false);
300 layout
.marginWidth
= layout
.marginHeight
= layout
.verticalSpacing
= 0;
301 layout
.horizontalSpacing
= 5;
302 parent
.setLayout(layout
);
304 // Text Area to filter
305 filterTxt
= tk
.createText(parent
, "", SWT
.BORDER
| SWT
.SINGLE
306 | SWT
.SEARCH
| SWT
.CANCEL
);
307 filterTxt
.setMessage(FILTER_HELP_MSG
);
308 filterTxt
.setLayoutData(new GridData(SWT
.FILL
, SWT
.FILL
, true, false));
309 filterTxt
.addModifyListener(new ModifyListener() {
310 public void modifyText(ModifyEvent event
) {
315 Button resetBtn
= tk
.createButton(parent
, null, SWT
.PUSH
);
316 resetBtn
.setImage(DistImages
.IMG_CLEAR
);
317 resetBtn
.addSelectionListener(new SelectionAdapter() {
318 public void widgetSelected(SelectionEvent e
) {
319 filterTxt
.setText("");
320 filterTxt
.setMessage(FILTER_HELP_MSG
);
325 private void createTableViewer(Composite parent
) {
326 parent
.setLayout(new FillLayout());
327 // helpers to enable sorting by column
328 List
<String
> propertiesList
= new ArrayList
<String
>();
329 List
<Integer
> propertyTypesList
= new ArrayList
<Integer
>();
331 // Define the TableViewer
332 viewer
= new TableViewer(parent
, SWT
.MULTI
| SWT
.H_SCROLL
333 | SWT
.V_SCROLL
| SWT
.FULL_SELECTION
| SWT
.BORDER
);
336 TableViewerColumn col
= new TableViewerColumn(viewer
, SWT
.NONE
);
337 col
.getColumn().setWidth(220);
338 col
.getColumn().setText("Category");
339 col
.setLabelProvider(new ColumnLabelProvider() {
341 public String
getText(Object element
) {
342 return JcrUtils
.get((Node
) element
, SlcNames
.SLC_CATEGORY
);
345 col
.getColumn().addSelectionListener(getSelectionAdapter(0));
346 propertiesList
.add(SlcNames
.SLC_CATEGORY
);
347 propertyTypesList
.add(PropertyType
.STRING
);
350 col
= new TableViewerColumn(viewer
, SWT
.NONE
);
351 col
.getColumn().setWidth(220);
352 col
.getColumn().setText("Name");
353 col
.setLabelProvider(new ColumnLabelProvider() {
355 public String
getText(Object element
) {
356 return JcrUtils
.get((Node
) element
, SLC_NAME
);
359 col
.getColumn().addSelectionListener(getSelectionAdapter(1));
360 propertiesList
.add(SLC_NAME
);
361 propertyTypesList
.add(PropertyType
.STRING
);
364 col
= new TableViewerColumn(viewer
, SWT
.NONE
);
365 col
.getColumn().setWidth(160);
366 col
.getColumn().setText("Version");
367 col
.setLabelProvider(new ColumnLabelProvider() {
369 public String
getText(Object element
) {
370 return JcrUtils
.get((Node
) element
, SLC_VERSION
);
373 col
.getColumn().addSelectionListener(getSelectionAdapter(2));
374 propertiesList
.add(SLC_VERSION
);
375 propertyTypesList
.add(PropertyType
.STRING
);
377 // Exists in workspace
378 col
= new TableViewerColumn(viewer
, SWT
.NONE
);
379 col
.getColumn().setWidth(160);
380 col
.getColumn().setText("Exists in workspace");
381 col
.setLabelProvider(new ColumnLabelProvider() {
383 public String
getText(Object element
) {
384 return getRealizedModule((Node
) element
) != null ?
"Yes" : "No";
385 // return JcrUtils.get((Node) element, SLC_VERSION);
388 // col.getColumn().addSelectionListener(getSelectionAdapter(2));
389 // propertiesList.add(SLC_VERSION);
390 // propertyTypesList.add(PropertyType.STRING);
392 final Table table
= viewer
.getTable();
393 table
.setHeaderVisible(true);
394 table
.setLinesVisible(true);
396 viewer
.setContentProvider(new DistributionsContentProvider());
397 getSite().setSelectionProvider(viewer
);
399 comparator
= new NodeViewerComparator(2,
400 NodeViewerComparator
.ASCENDING
, propertiesList
,
402 viewer
.setComparator(comparator
);
405 // MenuManager menuManager = new MenuManager();
406 // Menu menu = menuManager.createContextMenu(viewer.getTable());
407 // menuManager.addMenuListener(new IMenuListener() {
408 // public void menuAboutToShow(IMenuManager manager) {
409 // contextMenuAboutToShow(manager);
412 // viewer.getTable().setMenu(menu);
413 // getSite().registerContextMenu(menuManager, viewer);
416 viewer
.addDoubleClickListener(new DoubleClickListener());
419 private Node
getRealizedModule(Node moduleCoordinates
) {
421 String category
= JcrUtils
.get(moduleCoordinates
, SLC_CATEGORY
);
422 String name
= JcrUtils
.get(moduleCoordinates
, SLC_NAME
);
423 String version
= JcrUtils
.get(moduleCoordinates
, SLC_VERSION
);
424 Artifact artifact
= new DefaultArtifact(category
+ ":" + name
426 String parentPath
= MavenConventionsUtils
.artifactParentPath(
427 RepoConstants
.DEFAULT_ARTIFACTS_BASE_PATH
, artifact
);
429 Session session
= modularDistribution
.getSession();
430 if (session
.nodeExists(parentPath
)) {
431 Node parent
= session
.getNode(parentPath
);
432 NodeIterator nit
= parent
.getNodes();
433 while (nit
.hasNext()) {
434 Node currN
= nit
.nextNode();
435 if (currN
.isNodeType(SlcTypes
.SLC_ARTIFACT
))
439 } catch (RepositoryException re
) {
440 throw new SlcException(
441 "unable to retrieve realized module with coordinates "
442 + moduleCoordinates
, re
);
447 private void refresh() {
448 final List
<Node
> result
= JcrUtils
449 .nodeIteratorToList(listBundleArtifacts());
450 viewer
.setInput(result
);
453 /** Build repository request */
454 private NodeIterator
listBundleArtifacts() {
456 Session session
= modularDistribution
.getSession();
457 QueryManager queryManager
= session
.getWorkspace()
459 QueryObjectModelFactory factory
= queryManager
.getQOMFactory();
461 Selector source
= factory
.selector(SlcTypes
.SLC_MODULE_COORDINATES
,
462 SlcTypes
.SLC_MODULE_COORDINATES
);
464 // Create a dynamic operand for each property on which we want to
466 DynamicOperand catDO
= factory
.propertyValue(
467 source
.getSelectorName(), SlcNames
.SLC_CATEGORY
);
468 DynamicOperand nameDO
= factory
.propertyValue(
469 source
.getSelectorName(), SlcNames
.SLC_NAME
);
470 DynamicOperand versionDO
= factory
.propertyValue(
471 source
.getSelectorName(), SlcNames
.SLC_VERSION
);
473 String path
= modularDistribution
.getPath() + "/"
474 + SlcNames
.SLC_MODULES
;
476 // Default Constraint: correct children
477 Constraint defaultC
= factory
.descendantNode(
478 source
.getSelectorName(), path
);
480 String filter
= filterTxt
.getText();
482 // Build constraints based the textArea content
483 if (filter
!= null && !"".equals(filter
.trim())) {
485 String
[] strs
= filter
.trim().split(" ");
486 for (String token
: strs
) {
487 token
= token
.replace('*', '%');
488 StaticOperand so
= factory
.literal(session
489 .getValueFactory().createValue("%" + token
+ "%"));
491 Constraint currC
= factory
.comparison(catDO
,
492 QueryObjectModelFactory
.JCR_OPERATOR_LIKE
, so
);
493 currC
= factory
.or(currC
, factory
.comparison(versionDO
,
494 QueryObjectModelFactory
.JCR_OPERATOR_LIKE
, so
));
495 currC
= factory
.or(currC
, factory
.comparison(nameDO
,
496 QueryObjectModelFactory
.JCR_OPERATOR_LIKE
, so
));
498 defaultC
= factory
.and(defaultC
, currC
);
502 QueryObjectModel query
= factory
.createQuery(source
, defaultC
,
504 QueryResult result
= query
.execute();
505 return result
.getNodes();
506 } catch (RepositoryException re
) {
507 throw new SlcException("Unable to refresh module list for node "
508 + modularDistribution
, re
);
513 public void setFocus() {
514 viewer
.getTable().setFocus();
517 // /** Programmatically configure the context menu */
518 // protected void contextMenuAboutToShow(IMenuManager menuManager) {
519 // IWorkbenchWindow window = DistPlugin.getDefault().getWorkbench()
520 // .getActiveWorkbenchWindow();
521 // // Build conditions
522 // // Delete selected artifacts
523 // // CommandUtils.refreshCommand(menuManager, window, DeleteArtifacts.ID,
524 // // DeleteArtifacts.DEFAULT_LABEL, DeleteArtifacts.DEFAULT_ICON,
528 private SelectionAdapter
getSelectionAdapter(final int index
) {
529 SelectionAdapter selectionAdapter
= new SelectionAdapter() {
531 public void widgetSelected(SelectionEvent e
) {
532 Table table
= viewer
.getTable();
533 comparator
.setColumn(index
);
534 int dir
= table
.getSortDirection();
535 if (table
.getSortColumn() == table
.getColumn(index
)) {
536 dir
= dir
== SWT
.UP ? SWT
.DOWN
: SWT
.UP
;
540 table
.setSortDirection(dir
);
541 table
.setSortColumn(table
.getColumn(index
));
545 return selectionAdapter
;
549 private class DistributionsContentProvider
implements
550 IStructuredContentProvider
{
551 // we keep a cache of the Nodes in the content provider to be able to
552 // manage long request
553 private List
<Node
> nodes
;
555 public void dispose() {
558 // We expect a list of nodes as a new input
559 @SuppressWarnings("unchecked")
560 public void inputChanged(Viewer viewer
, Object oldInput
, Object newInput
) {
561 nodes
= (List
<Node
>) newInput
;
564 public Object
[] getElements(Object arg0
) {
565 return nodes
.toArray();
569 private class DoubleClickListener
implements IDoubleClickListener
{
571 public void doubleClick(DoubleClickEvent event
) {
572 Object obj
= ((IStructuredSelection
) event
.getSelection())
574 if (obj
instanceof Node
) {
575 Node node
= (Node
) obj
;
577 if (node
.isNodeType(SlcTypes
.SLC_MODULE_COORDINATES
)) {
578 Node realizedModule
= getRealizedModule(node
);
579 if (realizedModule
!= null) {
580 ModuleEditorInput dwip
= (ModuleEditorInput
) getEditorInput();
581 Map
<String
, String
> params
= new HashMap
<String
, String
>();
582 params
.put(OpenModuleEditor
.PARAM_REPO_NODE_PATH
,
583 dwip
.getRepoNodePath());
584 params
.put(OpenModuleEditor
.PARAM_REPO_URI
,
586 params
.put(OpenModuleEditor
.PARAM_WORKSPACE_NAME
,
587 dwip
.getWorkspaceName());
588 String path
= realizedModule
.getPath();
589 params
.put(OpenModuleEditor
.PARAM_MODULE_PATH
, path
);
590 CommandUtils
.callCommand(OpenModuleEditor
.ID
,
594 } catch (RepositoryException re
) {
595 throw new SlcException("Cannot get path for node " + node
596 + " while setting parameters for "
597 + "command OpenModuleEditor", re
);