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.util.ArrayList;
19  import java.util.HashMap;
20  import java.util.List;
21  import java.util.Map;
22  
23  import javax.jcr.Node;
24  import javax.jcr.NodeIterator;
25  import javax.jcr.PropertyType;
26  import javax.jcr.RepositoryException;
27  import javax.jcr.Session;
28  import javax.jcr.observation.Event;
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.argeo.cms.ui.workbench.util.CommandUtils;
39  import org.argeo.eclipse.ui.jcr.AsyncUiEventListener;
40  import org.argeo.jcr.JcrUtils;
41  import org.argeo.slc.SlcException;
42  import org.argeo.slc.SlcNames;
43  import org.argeo.slc.SlcTypes;
44  import org.argeo.slc.client.ui.dist.DistConstants;
45  import org.argeo.slc.client.ui.dist.DistImages;
46  import org.argeo.slc.client.ui.dist.DistPlugin;
47  import org.argeo.slc.client.ui.dist.commands.OpenGenerateBinariesWizard;
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.repo.RepoConstants;
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.Display;
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.forms.IManagedForm;
79  import org.eclipse.ui.forms.editor.FormEditor;
80  import org.eclipse.ui.forms.editor.FormPage;
81  import org.eclipse.ui.forms.widgets.FormToolkit;
82  import org.eclipse.ui.forms.widgets.ScrolledForm;
83  
84  /**
85   * Show all category base (currently only Aether group base) contained in a
86   * given workspace as filter-able table. Enable to definition of which of them
87   * should be managed as modular distribution
88   */
89  public class WkspCategoryBaseListPage extends FormPage implements SlcNames {
90  
91  	final static String PAGE_ID = "WkspCategoryBaseListPage";
92  
93  	// Business Objects
94  	private Session session;
95  
96  	// This page widgets
97  	private DistNodeViewerComparator comparator;
98  	private TableViewer viewer;
99  	private FormToolkit tk;
100 	private Text filterTxt;
101 	private final static String FILTER_HELP_MSG = "Enter filter criterion separated by a space";
102 
103 	// Observes changes
104 	// private final static String[] observedTypes = { SlcTypes.SLC_GROUP_BASE
105 	// };
106 	// private CategoryObserver categoriesObserver;
107 
108 	public WkspCategoryBaseListPage(FormEditor formEditor, String title,
109 			Session session) {
110 		super(formEditor, PAGE_ID, title);
111 		this.session = session;
112 	}
113 
114 	@Override
115 	protected void createFormContent(IManagedForm managedForm) {
116 		ScrolledForm form = managedForm.getForm();
117 		tk = managedForm.getToolkit();
118 
119 		form.setText("Define Relevant Categories");
120 		form.setMessage("Choose in the below list "
121 				+ "the categories that can be used as base for "
122 				+ "modular distributions maintained via the current workspace",
123 				IMessageProvider.NONE);
124 
125 		// Main Layout
126 		GridLayout layout = new GridLayout(1, false);
127 		Composite body = form.getBody();
128 		body.setLayout(layout);
129 
130 		// filter section
131 		Composite header = tk.createComposite(body);
132 		header.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
133 		createFilterPart(header);
134 
135 		// the table
136 		Composite tableCmp = tk.createComposite(body);
137 		tableCmp.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
138 		createTableViewer(tableCmp);
139 
140 		// categoriesObserver = new CategoryObserver(viewer.getTable()
141 		// .getDisplay());
142 		// try {
143 		// ObservationManager observationManager = session.getWorkspace()
144 		// .getObservationManager();
145 		// // FIXME Will not be notified if empty result is deleted
146 		// observationManager.addEventListener(categoriesObserver,
147 		// Event.PROPERTY_CHANGED, "/", true, null, observedTypes,
148 		// false);
149 		// } catch (RepositoryException e) {
150 		// throw new SlcException("Cannot register listeners", e);
151 		// }
152 
153 		refresh();
154 	}
155 
156 	private void refresh() {
157 		final List<Node> result = JcrUtils.nodeIteratorToList(listGroupBase());
158 		viewer.setInput(result);
159 	}
160 
161 	/** Build repository request */
162 	private NodeIterator listGroupBase() {
163 		try {
164 			QueryManager queryManager = session.getWorkspace()
165 					.getQueryManager();
166 			QueryObjectModelFactory factory = queryManager.getQOMFactory();
167 
168 			Selector source = factory.selector(SlcTypes.SLC_GROUP_BASE,
169 					SlcTypes.SLC_MODULE_COORDINATES);
170 
171 			// Create a dynamic operand for each property on which we want to
172 			// filter
173 			DynamicOperand catDO = factory.propertyValue(
174 					source.getSelectorName(), SlcNames.SLC_CATEGORY);
175 			DynamicOperand nameDO = factory.propertyValue(
176 					source.getSelectorName(), SlcNames.SLC_NAME);
177 
178 			String filter = filterTxt.getText();
179 
180 			Constraint defaultC = null;
181 			// Build constraints based the textArea content
182 			if (filter != null && !"".equals(filter.trim())) {
183 				// Parse the String
184 				String[] strs = filter.trim().split(" ");
185 				for (String token : strs) {
186 					token = token.replace('*', '%');
187 					StaticOperand so = factory.literal(session
188 							.getValueFactory().createValue("%" + token + "%"));
189 
190 					Constraint currC = factory.comparison(catDO,
191 							QueryObjectModelFactory.JCR_OPERATOR_LIKE, so);
192 					currC = factory.or(currC, factory.comparison(nameDO,
193 							QueryObjectModelFactory.JCR_OPERATOR_LIKE, so));
194 
195 					if (defaultC == null)
196 						defaultC = currC;
197 					else
198 						defaultC = factory.and(defaultC, currC);
199 				}
200 			}
201 
202 			QueryObjectModel query = factory.createQuery(source, defaultC,
203 					null, null);
204 			QueryResult result = query.execute();
205 			return result.getNodes();
206 		} catch (RepositoryException re) {
207 			throw new SlcException(
208 					"Unable to refresh group list for workspace "
209 							+ getEditorInput().getName(), re);
210 		}
211 	}
212 
213 	private void createFilterPart(Composite parent) {
214 		GridLayout layout = new GridLayout(2, false);
215 		layout.marginWidth = layout.marginHeight = layout.verticalSpacing = 0;
216 		layout.horizontalSpacing = 5;
217 		parent.setLayout(layout);
218 
219 		// Text Area to filter
220 		filterTxt = tk.createText(parent, "", SWT.BORDER | SWT.SINGLE
221 				| SWT.SEARCH | SWT.CANCEL);
222 		filterTxt.setMessage(FILTER_HELP_MSG);
223 		filterTxt.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
224 		filterTxt.addModifyListener(new ModifyListener() {
225 			private static final long serialVersionUID = 8727389523069041623L;
226 
227 			public void modifyText(ModifyEvent event) {
228 				refresh();
229 			}
230 		});
231 
232 		Button resetBtn = tk.createButton(parent, null, SWT.PUSH);
233 		resetBtn.setImage(DistImages.IMG_REPO_READONLY);
234 		resetBtn.addSelectionListener(new SelectionAdapter() {
235 			private static final long serialVersionUID = -6523538838444581321L;
236 
237 			public void widgetSelected(SelectionEvent e) {
238 				resetFilter();
239 			}
240 		});
241 	}
242 
243 	private void resetFilter() {
244 		filterTxt.setText("");
245 		filterTxt.setMessage(FILTER_HELP_MSG);
246 	}
247 
248 	private void createTableViewer(Composite parent) {
249 		parent.setLayout(new FillLayout());
250 		// helpers to enable sorting by column
251 		List<String> propertiesList = new ArrayList<String>();
252 		List<Integer> propertyTypesList = new ArrayList<Integer>();
253 
254 		// Define the TableViewer
255 		viewer = new TableViewer(parent, SWT.SINGLE | SWT.H_SCROLL
256 				| SWT.V_SCROLL | SWT.FULL_SELECTION | SWT.BORDER);
257 
258 		TableViewerColumn col;
259 		// Name
260 		// TableViewerColumn col = new TableViewerColumn(viewer, SWT.NONE);
261 		// col.getColumn().setWidth(220);
262 		// col.getColumn().setText("Category");
263 		// col.setLabelProvider(new ColumnLabelProvider() {
264 		// @Override
265 		// public String getText(Object element) {
266 		// return JcrUtils.get((Node) element, SlcNames.SLC_CATEGORY);
267 		// }
268 		// });
269 		// col.getColumn().addSelectionListener(getSelectionAdapter(0));
270 		// propertiesList.add(SlcNames.SLC_CATEGORY);
271 		// propertyTypesList.add(PropertyType.STRING);
272 
273 		// Group base name
274 		col = new TableViewerColumn(viewer, SWT.NONE);
275 		col.getColumn().setWidth(220);
276 		col.getColumn().setText("Group Name");
277 		col.setLabelProvider(new ColumnLabelProvider() {
278 			private static final long serialVersionUID = 6186787928630825293L;
279 
280 			@Override
281 			public String getText(Object element) {
282 				return JcrUtils.get((Node) element, SLC_GROUP_BASE_ID);
283 			}
284 		});
285 		col.getColumn().addSelectionListener(getSelectionAdapter(0));
286 		propertiesList.add(SLC_GROUP_BASE_ID);
287 		propertyTypesList.add(PropertyType.STRING);
288 
289 		// Version
290 		col = new TableViewerColumn(viewer, SWT.NONE);
291 		col.getColumn().setWidth(80);
292 		col.getColumn().setText("Has binaries");
293 		col.setLabelProvider(new ColumnLabelProvider() {
294 			private static final long serialVersionUID = -2017377132642062464L;
295 
296 			@Override
297 			public String getText(Object element) {
298 				try {
299 					Node currNode = (Node) element;
300 
301 					return currNode.hasNode(RepoConstants.BINARIES_ARTIFACT_ID) ? "Yes"
302 							: "No";
303 				} catch (RepositoryException e) {
304 					throw new SlcException("unable to check type of node "
305 							+ element, e);
306 				}
307 			}
308 		});
309 		// col.getColumn().addSelectionListener(getSelectionAdapter(2));
310 		// propertiesList.add(SLC_VERSION);
311 		// propertyTypesList.add(PropertyType.STRING);
312 
313 		final Table table = viewer.getTable();
314 		table.setHeaderVisible(true);
315 		table.setLinesVisible(true);
316 
317 		viewer.setContentProvider(new DistributionsContentProvider());
318 		getSite().setSelectionProvider(viewer);
319 
320 		comparator = new DistNodeViewerComparator(0,
321 				DistNodeViewerComparator.ASCENDING, propertiesList,
322 				propertyTypesList);
323 		viewer.setComparator(comparator);
324 
325 		// Context Menu
326 		MenuManager menuManager = new MenuManager();
327 		Menu menu = menuManager.createContextMenu(viewer.getTable());
328 		menuManager.addMenuListener(new IMenuListener() {
329 			private static final long serialVersionUID = 739004528695501335L;
330 
331 			public void menuAboutToShow(IMenuManager manager) {
332 				contextMenuAboutToShow(manager);
333 			}
334 		});
335 		viewer.getTable().setMenu(menu);
336 		getSite().registerContextMenu(menuManager, viewer);
337 
338 		// Double click
339 		viewer.addDoubleClickListener(new DoubleClickListener());
340 	}
341 
342 	@Override
343 	public void setFocus() {
344 		viewer.getTable().setFocus();
345 	}
346 
347 	/** Programmatically configure the context menu */
348 	protected void contextMenuAboutToShow(IMenuManager menuManager) {
349 		IWorkbenchWindow window = DistPlugin.getDefault().getWorkbench()
350 				.getActiveWorkbenchWindow();
351 		// Build conditions
352 		// Mark as category base
353 		Object firstElement = ((IStructuredSelection) viewer.getSelection())
354 				.getFirstElement();
355 		Node currSelected = (Node) firstElement;
356 
357 		DistWkspEditorInput input = (DistWkspEditorInput) getEditorInput();
358 
359 		Map<String, String> params = new HashMap<String, String>();
360 		params.put(OpenGenerateBinariesWizard.PARAM_REPO_NODE_PATH,
361 				input.getRepoNodePath());
362 		try {
363 			params.put(OpenGenerateBinariesWizard.PARAM_MODULE_PATH,
364 					currSelected.getPath());
365 		} catch (RepositoryException e) {
366 			throw new SlcException("Unable to get path for " + currSelected, e);
367 		}
368 		params.put(OpenGenerateBinariesWizard.PARAM_WORKSPACE_NAME,
369 				input.getWorkspaceName());
370 
371 		CommandUtils.refreshParameterizedCommand(menuManager, window,
372 				OpenGenerateBinariesWizard.ID,
373 				OpenGenerateBinariesWizard.DEFAULT_LABEL,
374 				OpenGenerateBinariesWizard.DEFAULT_ICON, true, params);
375 
376 		// boolean isRelevant = false;
377 		// try {
378 		// isRelevant = currSelected.isNodeType(SlcTypes.SLC_CATEGORY);
379 		// boolean canEdit = currSelected.canAddMixin(SlcTypes.SLC_CATEGORY);
380 		//
381 		// } catch (RepositoryException e) {
382 		// throw new SlcException("unable to check type of node "
383 		// + firstElement, e);
384 		// }
385 		// // Add
386 		// if (isRelevant) {// Remove
387 		// CommandUtils.refreshCommand(menuManager, window,
388 		// MarkAsRelevantCategory.ID,
389 		// MarkAsRelevantCategory.DEFAULT_REMOVE_LABEL,
390 		// MarkAsRelevantCategory.DEFAULT_REMOVE_ICON, true);
391 		// } else {
392 		// CommandUtils.refreshCommand(menuManager, window,
393 		// MarkAsRelevantCategory.ID,
394 		// MarkAsRelevantCategory.DEFAULT_LABEL,
395 		// MarkAsRelevantCategory.DEFAULT_ICON, true);
396 		// }
397 	}
398 
399 	private SelectionAdapter getSelectionAdapter(final int index) {
400 		SelectionAdapter selectionAdapter = new SelectionAdapter() {
401 			private static final long serialVersionUID = -1723894288128081757L;
402 
403 			@Override
404 			public void widgetSelected(SelectionEvent e) {
405 				Table table = viewer.getTable();
406 				comparator.setColumn(index);
407 				int dir = table.getSortDirection();
408 				if (table.getSortColumn() == table.getColumn(index)) {
409 					dir = dir == SWT.UP ? SWT.DOWN : SWT.UP;
410 				} else {
411 					dir = SWT.DOWN;
412 				}
413 				table.setSortDirection(dir);
414 				table.setSortColumn(table.getColumn(index));
415 				viewer.refresh();
416 			}
417 		};
418 		return selectionAdapter;
419 	}
420 
421 	/* LOCAL CLASSES */
422 	private class DistributionsContentProvider implements
423 			IStructuredContentProvider {
424 		private static final long serialVersionUID = -5939763615620837492L;
425 
426 		// we keep a cache of the Nodes in the content provider to be able to
427 		// manage long request
428 		private List<Node> nodes;
429 
430 		public void dispose() {
431 		}
432 
433 		// We expect a list of nodes as a new input
434 		@SuppressWarnings("unchecked")
435 		public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
436 			nodes = (List<Node>) newInput;
437 		}
438 
439 		public Object[] getElements(Object arg0) {
440 			return nodes.toArray();
441 		}
442 	}
443 
444 	private class DoubleClickListener implements IDoubleClickListener {
445 
446 		public void doubleClick(DoubleClickEvent event) {
447 			Object obj = ((IStructuredSelection) event.getSelection())
448 					.getFirstElement();
449 			if (obj instanceof Node) {
450 				Node node = (Node) obj;
451 				try {
452 					if (node.isNodeType(SlcTypes.SLC_ARTIFACT)) {
453 						DistWkspEditorInput dwip = (DistWkspEditorInput) getEditorInput();
454 						Map<String, String> params = new HashMap<String, String>();
455 						params.put(OpenModuleEditor.PARAM_REPO_NODE_PATH,
456 								dwip.getRepoNodePath());
457 						params.put(OpenModuleEditor.PARAM_REPO_URI,
458 								dwip.getUri());
459 						params.put(OpenModuleEditor.PARAM_WORKSPACE_NAME,
460 								dwip.getWorkspaceName());
461 						String path = node.getPath();
462 						params.put(OpenModuleEditor.PARAM_MODULE_PATH, path);
463 						CommandUtils.callCommand(OpenModuleEditor.ID, params);
464 					}
465 				} catch (RepositoryException re) {
466 					throw new SlcException("Cannot get path for node " + node
467 							+ " while setting parameters for "
468 							+ "command OpenModuleEditor", re);
469 				}
470 
471 			}
472 		}
473 	}
474 
475 	class CategoryObserver extends AsyncUiEventListener {
476 
477 		public CategoryObserver(Display display) {
478 			super(display);
479 		}
480 
481 		@Override
482 		protected Boolean willProcessInUiThread(List<Event> events)
483 				throws RepositoryException {
484 			for (Event event : events) {
485 				String path = event.getPath();
486 				if (JcrUtils.lastPathElement(path).equals(
487 						DistConstants.JCR_MIXIN_TYPES))
488 					return true;
489 			}
490 			return false;
491 		}
492 
493 		protected void onEventInUiThread(List<Event> events)
494 				throws RepositoryException {
495 			if (getLog().isTraceEnabled())
496 				getLog().trace("Refresh table");
497 			viewer.refresh();
498 		}
499 	}
500 
501 	@Override
502 	public void setActive(boolean active) {
503 		super.setActive(active);
504 		if (active) {
505 		}
506 	}
507 }