View Javadoc
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.HashMap;
21  import java.util.List;
22  import java.util.Map;
23  
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;
37  
38  import org.apache.commons.logging.Log;
39  import org.apache.commons.logging.LogFactory;
40  import org.argeo.cms.ui.workbench.util.CommandUtils;
41  import org.argeo.eclipse.ui.EclipseUiUtils;
42  import org.argeo.jcr.JcrUtils;
43  import org.argeo.slc.SlcException;
44  import org.argeo.slc.SlcNames;
45  import org.argeo.slc.SlcTypes;
46  import org.argeo.slc.client.ui.dist.DistConstants;
47  import org.argeo.slc.client.ui.dist.DistImages;
48  import org.argeo.slc.client.ui.dist.commands.OpenModuleEditor;
49  import org.argeo.slc.client.ui.dist.utils.DistNodeViewerComparator;
50  import org.argeo.slc.client.ui.dist.utils.HyperlinkAdapter;
51  import org.argeo.slc.repo.RepoConstants;
52  import org.argeo.slc.repo.RepoUtils;
53  import org.argeo.slc.repo.maven.MavenConventionsUtils;
54  import org.eclipse.aether.artifact.Artifact;
55  import org.eclipse.aether.artifact.DefaultArtifact;
56  import org.eclipse.jface.dialogs.IMessageProvider;
57  import org.eclipse.jface.viewers.ColumnLabelProvider;
58  import org.eclipse.jface.viewers.DoubleClickEvent;
59  import org.eclipse.jface.viewers.IDoubleClickListener;
60  import org.eclipse.jface.viewers.IStructuredContentProvider;
61  import org.eclipse.jface.viewers.IStructuredSelection;
62  import org.eclipse.jface.viewers.TableViewer;
63  import org.eclipse.jface.viewers.TableViewerColumn;
64  import org.eclipse.jface.viewers.Viewer;
65  import org.eclipse.swt.SWT;
66  import org.eclipse.swt.events.ModifyEvent;
67  import org.eclipse.swt.events.ModifyListener;
68  import org.eclipse.swt.events.SelectionAdapter;
69  import org.eclipse.swt.events.SelectionEvent;
70  import org.eclipse.swt.layout.FillLayout;
71  import org.eclipse.swt.layout.GridData;
72  import org.eclipse.swt.layout.GridLayout;
73  import org.eclipse.swt.widgets.Button;
74  import org.eclipse.swt.widgets.Composite;
75  import org.eclipse.swt.widgets.Label;
76  import org.eclipse.swt.widgets.Table;
77  import org.eclipse.swt.widgets.Text;
78  import org.eclipse.ui.PlatformUI;
79  import org.eclipse.ui.browser.IWebBrowser;
80  import org.eclipse.ui.browser.IWorkbenchBrowserSupport;
81  import org.eclipse.ui.forms.IManagedForm;
82  import org.eclipse.ui.forms.editor.FormEditor;
83  import org.eclipse.ui.forms.editor.FormPage;
84  import org.eclipse.ui.forms.events.HyperlinkEvent;
85  import org.eclipse.ui.forms.widgets.FormToolkit;
86  import org.eclipse.ui.forms.widgets.Hyperlink;
87  import org.eclipse.ui.forms.widgets.ScrolledForm;
88  
89  /**
90   * Show all modules contained in a given modular distribution as filter-able
91   * table
92   */
93  public class ModularDistVersionOverviewPage extends FormPage implements
94  		SlcNames {
95  
96  	private final static Log log = LogFactory
97  			.getLog(ModularDistVersionOverviewPage.class);
98  
99  	final static String PAGE_ID = "ModularDistVersionOverviewPage";
100 
101 	// Business Objects
102 	private Node modularDistribution;
103 	// private Node modularDistributionBase;
104 
105 	// This page widgets
106 	private DistNodeViewerComparator comparator;
107 	private TableViewer viewer;
108 	private FormToolkit tk;
109 	private Text filterTxt;
110 	private final static String FILTER_HELP_MSG = "Enter filter criterion separated by a space";
111 
112 	public ModularDistVersionOverviewPage(FormEditor formEditor, String title,
113 			Node modularDistribution) {
114 		super(formEditor, PAGE_ID, title);
115 		this.modularDistribution = modularDistribution;
116 	}
117 
118 	@Override
119 	protected void createFormContent(IManagedForm managedForm) {
120 		// General settings for this page
121 		ScrolledForm form = managedForm.getForm();
122 		tk = managedForm.getToolkit();
123 		Composite body = form.getBody();
124 
125 		GridLayout layout = new GridLayout(1, false);
126 		layout.marginWidth = 5;
127 		layout.marginRight = 15;
128 		layout.verticalSpacing = 0;
129 		body.setLayout(layout);
130 		try {
131 			form.setText(modularDistribution.hasProperty(SlcNames.SLC_NAME) ? modularDistribution
132 					.getProperty(SlcNames.SLC_NAME).getString() : "");
133 			form.setMessage(
134 					modularDistribution
135 							.hasProperty(DistConstants.SLC_BUNDLE_DESCRIPTION) ? modularDistribution
136 							.getProperty(DistConstants.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.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 = new Text(parent, SWT.LEFT);
202 		text.setText(textValue + " ");
203 		text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
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 HyperlinkAdapter() {
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 				final Node sourcesNode = modularDistribution.getSession()
248 						.getNode(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 				Hyperlink link = tk.createHyperlink(parent, srcName, SWT.NONE);
260 				link.addHyperlinkListener(new OpenFileLinkListener(sourcesNode
261 						.getPath()));
262 			}
263 		} catch (RepositoryException e) {
264 			throw new SlcException("Unable to configure sources link for "
265 					+ modularDistribution, e);
266 		}
267 	}
268 
269 	private class OpenFileLinkListener extends HyperlinkAdapter {
270 		final private String path;
271 
272 		public OpenFileLinkListener(String path) {
273 			this.path = path;
274 		}
275 
276 		@Override
277 		public void linkActivated(HyperlinkEvent e) {
278 			log.warn("File download must be implemented. Cannot provide access to "
279 					+ path);
280 
281 			// try {
282 			// ModuleEditorInput editorInput = (ModuleEditorInput)
283 			// getEditorInput();
284 			// Map<String, String> params = new HashMap<String, String>();
285 			// params.put(OpenJcrFile.PARAM_REPO_NODE_PATH,
286 			// editorInput.getRepoNodePath());
287 			// params.put(OpenJcrFile.PARAM_REPO_URI, editorInput.getUri());
288 			// params.put(OpenJcrFile.PARAM_WORKSPACE_NAME,
289 			// editorInput.getWorkspaceName());
290 			// params.put(OpenJcrFile.PARAM_FILE_PATH, path);
291 			//
292 			// String cmdId = (new OpenJcrFileCmdId()).getCmdId();
293 			// CommandUtils.callCommand(cmdId, params);
294 			// } catch (Exception ex) {
295 			//				throw new SlcException("error opening browser", ex); //$NON-NLS-1$
296 			// }
297 		}
298 	}
299 
300 	private void populateModuleTablePart(Composite parent) {
301 		GridLayout layout = new GridLayout(1, false);
302 		layout.marginWidth = layout.horizontalSpacing = 0;
303 		layout.verticalSpacing = 5;
304 		layout.marginTop = 15;
305 		parent.setLayout(layout);
306 		// A sub title
307 		Label label = tk.createLabel(parent,
308 				"Modules included in the current distribution", SWT.NONE);
309 		label.setFont(EclipseUiUtils.getBoldFont(parent));
310 
311 		// Add the filter section
312 		Composite filterPart = tk.createComposite(parent);
313 		filterPart.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
314 		createFilterPart(filterPart);
315 
316 		// Add the table
317 		Composite tablePart = tk.createComposite(parent);
318 		tablePart.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
319 		createTableViewer(tablePart);
320 		// populate it on first pass.
321 		refresh();
322 	}
323 
324 	private void createFilterPart(Composite parent) {
325 		GridLayout layout = new GridLayout(2, false);
326 		layout.marginWidth = layout.marginHeight = layout.verticalSpacing = 0;
327 		layout.horizontalSpacing = 5;
328 		parent.setLayout(layout);
329 
330 		// Text Area to filter
331 		filterTxt = tk.createText(parent, "", SWT.BORDER | SWT.SINGLE
332 				| SWT.SEARCH | SWT.CANCEL);
333 		filterTxt.setMessage(FILTER_HELP_MSG);
334 		filterTxt.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
335 		filterTxt.addModifyListener(new ModifyListener() {
336 			private static final long serialVersionUID = -276152321986407726L;
337 
338 			public void modifyText(ModifyEvent event) {
339 				refresh();
340 			}
341 		});
342 
343 		Button resetBtn = tk.createButton(parent, null, SWT.PUSH);
344 		resetBtn.setImage(DistImages.IMG_CLEAR);
345 		resetBtn.addSelectionListener(new SelectionAdapter() {
346 			private static final long serialVersionUID = -3549303742841670919L;
347 
348 			public void widgetSelected(SelectionEvent e) {
349 				filterTxt.setText("");
350 				filterTxt.setMessage(FILTER_HELP_MSG);
351 			}
352 		});
353 	}
354 
355 	private void createTableViewer(Composite parent) {
356 		parent.setLayout(new FillLayout());
357 		// helpers to enable sorting by column
358 		List<String> propertiesList = new ArrayList<String>();
359 		List<Integer> propertyTypesList = new ArrayList<Integer>();
360 
361 		// Define the TableViewer
362 		viewer = new TableViewer(parent, SWT.MULTI | SWT.H_SCROLL
363 				| SWT.V_SCROLL | SWT.FULL_SELECTION | SWT.BORDER);
364 
365 		// Name
366 		TableViewerColumn col = new TableViewerColumn(viewer, SWT.NONE);
367 		col.getColumn().setWidth(220);
368 		col.getColumn().setText("Category");
369 		col.setLabelProvider(new ColumnLabelProvider() {
370 			private static final long serialVersionUID = 5875398301711336875L;
371 
372 			@Override
373 			public String getText(Object element) {
374 				return JcrUtils.get((Node) element, SlcNames.SLC_CATEGORY);
375 			}
376 		});
377 		col.getColumn().addSelectionListener(getSelectionAdapter(0));
378 		propertiesList.add(SlcNames.SLC_CATEGORY);
379 		propertyTypesList.add(PropertyType.STRING);
380 
381 		// Symbolic name
382 		col = new TableViewerColumn(viewer, SWT.NONE);
383 		col.getColumn().setWidth(220);
384 		col.getColumn().setText("Name");
385 		col.setLabelProvider(new ColumnLabelProvider() {
386 			private static final long serialVersionUID = 3880240676256465072L;
387 
388 			@Override
389 			public String getText(Object element) {
390 				return JcrUtils.get((Node) element, SLC_NAME);
391 			}
392 		});
393 		col.getColumn().addSelectionListener(getSelectionAdapter(1));
394 		propertiesList.add(SLC_NAME);
395 		propertyTypesList.add(PropertyType.STRING);
396 
397 		// Version
398 		col = new TableViewerColumn(viewer, SWT.NONE);
399 		col.getColumn().setWidth(160);
400 		col.getColumn().setText("Version");
401 		col.setLabelProvider(new ColumnLabelProvider() {
402 			private static final long serialVersionUID = -4706438113850571784L;
403 
404 			@Override
405 			public String getText(Object element) {
406 				return JcrUtils.get((Node) element, SLC_VERSION);
407 			}
408 		});
409 		col.getColumn().addSelectionListener(getSelectionAdapter(2));
410 		propertiesList.add(SLC_VERSION);
411 		propertyTypesList.add(DistNodeViewerComparator.VERSION_TYPE);
412 
413 		// Exists in workspace
414 		col = new TableViewerColumn(viewer, SWT.NONE);
415 		col.getColumn().setWidth(160);
416 		col.getColumn().setText("Exists in workspace");
417 		col.setLabelProvider(new ColumnLabelProvider() {
418 			private static final long serialVersionUID = 8190063212920414300L;
419 
420 			@Override
421 			public String getText(Object element) {
422 				return getRealizedModule((Node) element) != null ? "Yes" : "No";
423 				// return JcrUtils.get((Node) element, SLC_VERSION);
424 			}
425 		});
426 		// col.getColumn().addSelectionListener(getSelectionAdapter(2));
427 		// propertiesList.add(SLC_VERSION);
428 		// propertyTypesList.add(PropertyType.STRING);
429 
430 		final Table table = viewer.getTable();
431 		table.setHeaderVisible(true);
432 		table.setLinesVisible(true);
433 
434 		viewer.setContentProvider(new DistributionsContentProvider());
435 		getSite().setSelectionProvider(viewer);
436 
437 		comparator = new DistNodeViewerComparator(2,
438 				DistNodeViewerComparator.ASCENDING, propertiesList,
439 				propertyTypesList);
440 		viewer.setComparator(comparator);
441 
442 		// // Context Menu
443 		// MenuManager menuManager = new MenuManager();
444 		// Menu menu = menuManager.createContextMenu(viewer.getTable());
445 		// menuManager.addMenuListener(new IMenuListener() {
446 		// public void menuAboutToShow(IMenuManager manager) {
447 		// contextMenuAboutToShow(manager);
448 		// }
449 		// });
450 		// viewer.getTable().setMenu(menu);
451 		// getSite().registerContextMenu(menuManager, viewer);
452 
453 		// Double click
454 		viewer.addDoubleClickListener(new DoubleClickListener());
455 	}
456 
457 	private Node getRealizedModule(Node moduleCoordinates) {
458 		try {
459 			String category = JcrUtils.get(moduleCoordinates, SLC_CATEGORY);
460 			String name = JcrUtils.get(moduleCoordinates, SLC_NAME);
461 			String version = JcrUtils.get(moduleCoordinates, SLC_VERSION);
462 			Artifact artifact = new DefaultArtifact(category + ":" + name + ":"
463 					+ version);
464 			String parentPath = MavenConventionsUtils.artifactParentPath(
465 					RepoConstants.DEFAULT_ARTIFACTS_BASE_PATH, artifact);
466 
467 			Session session = modularDistribution.getSession();
468 			if (session.nodeExists(parentPath)) {
469 				Node parent = session.getNode(parentPath);
470 				NodeIterator nit = parent.getNodes();
471 				while (nit.hasNext()) {
472 					Node currN = nit.nextNode();
473 					if (currN.isNodeType(SlcTypes.SLC_ARTIFACT))
474 						return currN;
475 				}
476 			}
477 		} catch (RepositoryException re) {
478 			throw new SlcException(
479 					"unable to retrieve realized module with coordinates "
480 							+ moduleCoordinates, re);
481 		}
482 		return null;
483 	}
484 
485 	private void refresh() {
486 		final List<Node> result = JcrUtils
487 				.nodeIteratorToList(listBundleArtifacts());
488 		viewer.setInput(result);
489 	}
490 
491 	/** Build repository request */
492 	private NodeIterator listBundleArtifacts() {
493 		try {
494 			Session session = modularDistribution.getSession();
495 			QueryManager queryManager = session.getWorkspace()
496 					.getQueryManager();
497 			QueryObjectModelFactory factory = queryManager.getQOMFactory();
498 
499 			Selector source = factory.selector(SlcTypes.SLC_MODULE_COORDINATES,
500 					SlcTypes.SLC_MODULE_COORDINATES);
501 
502 			// Create a dynamic operand for each property on which we want to
503 			// filter
504 			DynamicOperand catDO = factory.propertyValue(
505 					source.getSelectorName(), SlcNames.SLC_CATEGORY);
506 			DynamicOperand nameDO = factory.propertyValue(
507 					source.getSelectorName(), SlcNames.SLC_NAME);
508 			DynamicOperand versionDO = factory.propertyValue(
509 					source.getSelectorName(), SlcNames.SLC_VERSION);
510 
511 			String path = modularDistribution.getPath() + "/"
512 					+ SlcNames.SLC_MODULES;
513 
514 			// Default Constraint: correct children
515 			Constraint defaultC = factory.descendantNode(
516 					source.getSelectorName(), path);
517 
518 			String filter = filterTxt.getText();
519 
520 			// Build constraints based the textArea content
521 			if (filter != null && !"".equals(filter.trim())) {
522 				// Parse the String
523 				String[] strs = filter.trim().split(" ");
524 				for (String token : strs) {
525 					token = token.replace('*', '%');
526 					StaticOperand so = factory.literal(session
527 							.getValueFactory().createValue("%" + token + "%"));
528 
529 					Constraint currC = factory.comparison(catDO,
530 							QueryObjectModelFactory.JCR_OPERATOR_LIKE, so);
531 					currC = factory.or(currC, factory.comparison(versionDO,
532 							QueryObjectModelFactory.JCR_OPERATOR_LIKE, so));
533 					currC = factory.or(currC, factory.comparison(nameDO,
534 							QueryObjectModelFactory.JCR_OPERATOR_LIKE, so));
535 
536 					defaultC = factory.and(defaultC, currC);
537 				}
538 			}
539 
540 			QueryObjectModel query = factory.createQuery(source, defaultC,
541 					null, null);
542 			QueryResult result = query.execute();
543 			return result.getNodes();
544 		} catch (RepositoryException re) {
545 			throw new SlcException("Unable to refresh module list for node "
546 					+ modularDistribution, re);
547 		}
548 	}
549 
550 	@Override
551 	public void setFocus() {
552 		viewer.getTable().setFocus();
553 	}
554 
555 	// /** Programmatically configure the context menu */
556 	// protected void contextMenuAboutToShow(IMenuManager menuManager) {
557 	// IWorkbenchWindow window = DistPlugin.getDefault().getWorkbench()
558 	// .getActiveWorkbenchWindow();
559 	// // Build conditions
560 	// // Delete selected artifacts
561 	// // CommandUtils.refreshCommand(menuManager, window, DeleteArtifacts.ID,
562 	// // DeleteArtifacts.DEFAULT_LABEL, DeleteArtifacts.DEFAULT_ICON,
563 	// // true);
564 	// }
565 
566 	private SelectionAdapter getSelectionAdapter(final int index) {
567 		SelectionAdapter selectionAdapter = new SelectionAdapter() {
568 			private static final long serialVersionUID = 1260801795934660840L;
569 
570 			@Override
571 			public void widgetSelected(SelectionEvent e) {
572 				Table table = viewer.getTable();
573 				comparator.setColumn(index);
574 				int dir = table.getSortDirection();
575 				if (table.getSortColumn() == table.getColumn(index)) {
576 					dir = dir == SWT.UP ? SWT.DOWN : SWT.UP;
577 				} else {
578 					dir = SWT.DOWN;
579 				}
580 				table.setSortDirection(dir);
581 				table.setSortColumn(table.getColumn(index));
582 				viewer.refresh();
583 			}
584 		};
585 		return selectionAdapter;
586 	}
587 
588 	/* LOCAL CLASSES */
589 	private class DistributionsContentProvider implements
590 			IStructuredContentProvider {
591 		private static final long serialVersionUID = 8385338190908823791L;
592 		// we keep a cache of the Nodes in the content provider to be able to
593 		// manage long request
594 		private List<Node> nodes;
595 
596 		public void dispose() {
597 		}
598 
599 		// We expect a list of nodes as a new input
600 		@SuppressWarnings("unchecked")
601 		public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
602 			nodes = (List<Node>) newInput;
603 		}
604 
605 		public Object[] getElements(Object arg0) {
606 			return nodes.toArray();
607 		}
608 	}
609 
610 	private class DoubleClickListener implements IDoubleClickListener {
611 
612 		public void doubleClick(DoubleClickEvent event) {
613 			Object obj = ((IStructuredSelection) event.getSelection())
614 					.getFirstElement();
615 			if (obj instanceof Node) {
616 				Node node = (Node) obj;
617 				try {
618 					if (node.isNodeType(SlcTypes.SLC_MODULE_COORDINATES)) {
619 						Node realizedModule = getRealizedModule(node);
620 						if (realizedModule != null) {
621 							ModuleEditorInput dwip = (ModuleEditorInput) getEditorInput();
622 							Map<String, String> params = new HashMap<String, String>();
623 							params.put(OpenModuleEditor.PARAM_REPO_NODE_PATH,
624 									dwip.getRepoNodePath());
625 							params.put(OpenModuleEditor.PARAM_REPO_URI,
626 									dwip.getUri());
627 							params.put(OpenModuleEditor.PARAM_WORKSPACE_NAME,
628 									dwip.getWorkspaceName());
629 							String path = realizedModule.getPath();
630 							params.put(OpenModuleEditor.PARAM_MODULE_PATH, path);
631 							CommandUtils.callCommand(OpenModuleEditor.ID,
632 									params);
633 						}
634 					}
635 				} catch (RepositoryException re) {
636 					throw new SlcException("Cannot get path for node " + node
637 							+ " while setting parameters for "
638 							+ "command OpenModuleEditor", re);
639 				}
640 			}
641 		}
642 	}
643 }