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
.hasProperty(SlcNames
.SLC_NAME
) ? modularDistribution
130 .getProperty(SlcNames
.SLC_NAME
).getString() : "");
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
);
142 Composite header
= tk
.createComposite(body
);
143 header
.setLayoutData(new GridData(SWT
.FILL
, SWT
.TOP
, true, false));
144 populateHeaderPart(header
);
146 Composite moduleTablePart
= tk
.createComposite(body
);
147 moduleTablePart
.setLayoutData(new GridData(SWT
.FILL
, SWT
.FILL
, true,
149 populateModuleTablePart(moduleTablePart
);
152 private void populateHeaderPart(Composite parent
) {
153 GridLayout layout
= new GridLayout(6, false);
154 // layout.marginWidth = layout.horizontalSpacing = layout.marginHeight =
156 layout
.horizontalSpacing
= 10;
157 parent
.setLayout(layout
);
159 // 1st Line: Category, name version
163 modularDistribution
.hasProperty(SlcNames
.SLC_CATEGORY
) ? modularDistribution
164 .getProperty(SlcNames
.SLC_CATEGORY
).getString()
169 modularDistribution
.hasProperty(SlcNames
.SLC_NAME
) ? modularDistribution
170 .getProperty(SlcNames
.SLC_NAME
).getString() : "");
174 modularDistribution
.hasProperty(SlcNames
.SLC_VERSION
) ? modularDistribution
175 .getProperty(SlcNames
.SLC_VERSION
).getString() : "");
177 // 2nd Line: Vendor, licence, sources
182 .hasProperty(DistConstants
.SLC_BUNDLE_VENDOR
) ? modularDistribution
183 .getProperty(DistConstants
.SLC_BUNDLE_VENDOR
)
184 .getString() : "N/A");
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
);
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));
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));
204 text
.setEditable(false);
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(),
215 link
.addHyperlinkListener(new AbstractHyperlinkListener() {
217 public void linkActivated(HyperlinkEvent e
) {
219 IWorkbenchBrowserSupport browserSupport
= PlatformUI
220 .getWorkbench().getBrowserSupport();
221 IWebBrowser browser
= browserSupport
223 IWorkbenchBrowserSupport
.LOCATION_BAR
224 | IWorkbenchBrowserSupport
.NAVIGATION_BAR
,
225 "SLC Distribution browser",
226 "SLC Distribution browser",
228 browser
.openURL(new URL(link
.getText()));
229 } catch (Exception ex
) {
230 throw new SlcException("error opening browser", ex
); //$NON-NLS-1$
235 tk
.createLabel(parent
, "N/A", SWT
.NONE
);
238 // helper to check if sources are available
239 private void addSourceSourcesLink(Composite parent
) {
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");
247 Node sourcesNode
= modularDistribution
.getSession().getNode(
250 String srcName
= null;
251 if (sourcesNode
.hasProperty(SlcNames
.SLC_SYMBOLIC_NAME
))
252 srcName
= sourcesNode
.getProperty(
253 SlcNames
.SLC_SYMBOLIC_NAME
).getString();
255 srcName
= sourcesNode
.getName();
256 Label label
= tk
.createLabel(parent
, "Sources", SWT
.RIGHT
);
257 label
.setLayoutData(new GridData(SWT
.RIGHT
, SWT
.CENTER
, false,
259 final Hyperlink link
= tk
.createHyperlink(parent
, srcName
,
261 link
.addHyperlinkListener(new AbstractHyperlinkListener() {
263 public void linkActivated(HyperlinkEvent e
) {
265 System
.out
.println("CLICK on Sources link");
266 } catch (Exception ex
) {
267 throw new SlcException("error opening browser", ex
); //$NON-NLS-1$
273 } catch (RepositoryException e
) {
274 throw new SlcException("Unable to configure sources link for "
275 + modularDistribution
, e
);
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
);
286 Label label
= tk
.createLabel(parent
,
287 "Modules included in the current distribution", SWT
.NONE
);
288 label
.setFont(EclipseUiUtils
.getBoldFont(parent
));
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
);
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.
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
);
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
) {
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
);
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
>();
337 // Define the TableViewer
338 viewer
= new TableViewer(parent
, SWT
.MULTI
| SWT
.H_SCROLL
339 | SWT
.V_SCROLL
| SWT
.FULL_SELECTION
| SWT
.BORDER
);
342 TableViewerColumn col
= new TableViewerColumn(viewer
, SWT
.NONE
);
343 col
.getColumn().setWidth(220);
344 col
.getColumn().setText("Category");
345 col
.setLabelProvider(new ColumnLabelProvider() {
347 public String
getText(Object element
) {
348 return JcrUtils
.get((Node
) element
, SlcNames
.SLC_CATEGORY
);
351 col
.getColumn().addSelectionListener(getSelectionAdapter(0));
352 propertiesList
.add(SlcNames
.SLC_CATEGORY
);
353 propertyTypesList
.add(PropertyType
.STRING
);
356 col
= new TableViewerColumn(viewer
, SWT
.NONE
);
357 col
.getColumn().setWidth(220);
358 col
.getColumn().setText("Name");
359 col
.setLabelProvider(new ColumnLabelProvider() {
361 public String
getText(Object element
) {
362 return JcrUtils
.get((Node
) element
, SLC_NAME
);
365 col
.getColumn().addSelectionListener(getSelectionAdapter(1));
366 propertiesList
.add(SLC_NAME
);
367 propertyTypesList
.add(PropertyType
.STRING
);
370 col
= new TableViewerColumn(viewer
, SWT
.NONE
);
371 col
.getColumn().setWidth(160);
372 col
.getColumn().setText("Version");
373 col
.setLabelProvider(new ColumnLabelProvider() {
375 public String
getText(Object element
) {
376 return JcrUtils
.get((Node
) element
, SLC_VERSION
);
379 col
.getColumn().addSelectionListener(getSelectionAdapter(2));
380 propertiesList
.add(SLC_VERSION
);
381 propertyTypesList
.add(PropertyType
.STRING
);
383 final Table table
= viewer
.getTable();
384 table
.setHeaderVisible(true);
385 table
.setLinesVisible(true);
387 viewer
.setContentProvider(new DistributionsContentProvider());
388 getSite().setSelectionProvider(viewer
);
390 comparator
= new NodeViewerComparator(2,
391 NodeViewerComparator
.ASCENDING
, propertiesList
,
393 viewer
.setComparator(comparator
);
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
);
403 viewer
.getTable().setMenu(menu
);
404 getSite().registerContextMenu(menuManager
, viewer
);
407 viewer
.addDoubleClickListener(new DoubleClickListener());
410 private void refresh() {
411 final List
<Node
> result
= JcrUtils
412 .nodeIteratorToList(listBundleArtifacts());
413 viewer
.setInput(result
);
416 /** Build repository request */
417 private NodeIterator
listBundleArtifacts() {
419 Session session
= modularDistribution
.getSession();
420 QueryManager queryManager
= session
.getWorkspace()
422 QueryObjectModelFactory factory
= queryManager
.getQOMFactory();
424 Selector source
= factory
.selector(SlcTypes
.SLC_MODULE_COORDINATES
,
425 SlcTypes
.SLC_MODULE_COORDINATES
);
427 // Create a dynamic operand for each property on which we want to
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
);
436 String path
= modularDistribution
.getPath() + "/"
437 + SlcNames
.SLC_MODULES
;
439 // Default Constraint: correct children
440 Constraint defaultC
= factory
.descendantNode(
441 source
.getSelectorName(), path
);
443 String filter
= filterTxt
.getText();
445 // Build constraints based the textArea content
446 if (filter
!= null && !"".equals(filter
.trim())) {
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
+ "%"));
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
));
461 defaultC
= factory
.and(defaultC
, currC
);
465 QueryObjectModel query
= factory
.createQuery(source
, defaultC
,
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
);
476 public void setFocus() {
477 viewer
.getTable().setFocus();
480 /** Programmatically configure the context menu */
481 protected void contextMenuAboutToShow(IMenuManager menuManager
) {
482 IWorkbenchWindow window
= DistPlugin
.getDefault().getWorkbench()
483 .getActiveWorkbenchWindow();
485 // Delete selected artifacts
486 CommandUtils
.refreshCommand(menuManager
, window
, DeleteArtifacts
.ID
,
487 DeleteArtifacts
.DEFAULT_LABEL
, DeleteArtifacts
.DEFAULT_ICON
,
491 private SelectionAdapter
getSelectionAdapter(final int index
) {
492 SelectionAdapter selectionAdapter
= new SelectionAdapter() {
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
;
503 table
.setSortDirection(dir
);
504 table
.setSortColumn(table
.getColumn(index
));
508 return selectionAdapter
;
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
;
518 public void dispose() {
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
;
527 public Object
[] getElements(Object arg0
) {
528 return nodes
.toArray();
532 private class DoubleClickListener
implements IDoubleClickListener
{
534 public void doubleClick(DoubleClickEvent event
) {
535 Object obj
= ((IStructuredSelection
) event
.getSelection())
538 if (obj
instanceof Node
) {
539 Node node
= (Node
) obj
;
540 if (node
.isNodeType(SlcTypes
.SLC_BUNDLE_ARTIFACT
)) {
541 GenericBundleEditorInput gaei
= new GenericBundleEditorInput(
543 DistPlugin
.getDefault().getWorkbench()
544 .getActiveWorkbenchWindow().getActivePage()
545 .openEditor(gaei
, GenericBundleEditor
.ID
);
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",
560 // * UI Trick to put scroll bar on the table rather than on the scrollform
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);
570 // // when table height is less than 200 px, we let the scroll bar on the
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);
576 // table.setLayoutData(gd);
577 // getManagedForm().reflow(true);
581 public void setActive(boolean active
) {
582 super.setActive(active
);