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
;
18 import java
.util
.ArrayList
;
19 import java
.util
.List
;
21 import javax
.jcr
.Node
;
22 import javax
.jcr
.NodeIterator
;
23 import javax
.jcr
.PropertyType
;
24 import javax
.jcr
.RepositoryException
;
25 import javax
.jcr
.Session
;
26 import javax
.jcr
.observation
.Event
;
27 import javax
.jcr
.observation
.ObservationManager
;
28 import javax
.jcr
.query
.QueryManager
;
29 import javax
.jcr
.query
.QueryResult
;
30 import javax
.jcr
.query
.qom
.Constraint
;
31 import javax
.jcr
.query
.qom
.DynamicOperand
;
32 import javax
.jcr
.query
.qom
.QueryObjectModel
;
33 import javax
.jcr
.query
.qom
.QueryObjectModelFactory
;
34 import javax
.jcr
.query
.qom
.Selector
;
35 import javax
.jcr
.query
.qom
.StaticOperand
;
37 import org
.argeo
.ArgeoException
;
38 import org
.argeo
.eclipse
.ui
.jcr
.AsyncUiEventListener
;
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
.DistPlugin
;
45 import org
.argeo
.slc
.client
.ui
.dist
.commands
.MarkAsRelevantCategory
;
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
.eclipse
.jface
.action
.IMenuListener
;
50 import org
.eclipse
.jface
.action
.IMenuManager
;
51 import org
.eclipse
.jface
.action
.MenuManager
;
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
.Display
;
72 import org
.eclipse
.swt
.widgets
.Menu
;
73 import org
.eclipse
.swt
.widgets
.Table
;
74 import org
.eclipse
.swt
.widgets
.Text
;
75 import org
.eclipse
.ui
.IWorkbenchWindow
;
76 import org
.eclipse
.ui
.PartInitException
;
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
.widgets
.FormToolkit
;
81 import org
.eclipse
.ui
.forms
.widgets
.ScrolledForm
;
84 * Show all category base (currently only Aether group base) contained in a
85 * given workspace as filter-able table. Enable to definition of which of them
86 * should be managed as modular distribution
88 public class WkspCategoryBaseListPage
extends FormPage
implements SlcNames
{
90 final static String PAGE_ID
= "WkspCategoryBaseListPage";
93 private Session session
;
96 private NodeViewerComparator comparator
;
97 private TableViewer viewer
;
98 private FormToolkit tk
;
99 private Text filterTxt
;
100 private final static String FILTER_HELP_MSG
= "Enter filter criterion separated by a space";
103 private final static String
[] observedTypes
= { SlcTypes
.SLC_GROUP_BASE
};
104 private CategoryObserver categoriesObserver
;
106 public WkspCategoryBaseListPage(FormEditor formEditor
, String title
,
108 super(formEditor
, PAGE_ID
, title
);
109 this.session
= session
;
113 protected void createFormContent(IManagedForm managedForm
) {
114 ScrolledForm form
= managedForm
.getForm();
115 tk
= managedForm
.getToolkit();
117 form
.setText("Define Relevant Categories");
118 form
.setMessage("Choose in the below list "
119 + "the categories that can be used as base for "
120 + "modular distributions maintained via the current workspace",
121 IMessageProvider
.NONE
);
124 GridLayout layout
= new GridLayout(1, false);
125 Composite body
= form
.getBody();
126 body
.setLayout(layout
);
129 Composite header
= tk
.createComposite(body
);
130 header
.setLayoutData(new GridData(SWT
.FILL
, SWT
.TOP
, true, false));
131 createFilterPart(header
);
134 Composite tableCmp
= tk
.createComposite(body
);
135 tableCmp
.setLayoutData(new GridData(SWT
.FILL
, SWT
.FILL
, true, true));
136 createTableViewer(tableCmp
);
138 categoriesObserver
= new CategoryObserver(viewer
.getTable()
141 ObservationManager observationManager
= session
.getWorkspace()
142 .getObservationManager();
143 // FIXME Will not be notified if empty result is deleted
144 observationManager
.addEventListener(categoriesObserver
,
145 Event
.PROPERTY_CHANGED
, "/", true, null, observedTypes
,
147 } catch (RepositoryException e
) {
148 throw new SlcException("Cannot register listeners", e
);
154 private void refresh() {
155 final List
<Node
> result
= JcrUtils
.nodeIteratorToList(listGroupBase());
156 viewer
.setInput(result
);
159 /** Build repository request */
160 private NodeIterator
listGroupBase() {
162 QueryManager queryManager
= session
.getWorkspace()
164 QueryObjectModelFactory factory
= queryManager
.getQOMFactory();
166 Selector source
= factory
.selector(SlcTypes
.SLC_GROUP_BASE
,
167 SlcTypes
.SLC_MODULE_COORDINATES
);
169 // Create a dynamic operand for each property on which we want to
171 DynamicOperand catDO
= factory
.propertyValue(
172 source
.getSelectorName(), SlcNames
.SLC_CATEGORY
);
173 DynamicOperand nameDO
= factory
.propertyValue(
174 source
.getSelectorName(), SlcNames
.SLC_NAME
);
176 String filter
= filterTxt
.getText();
178 Constraint defaultC
= null;
179 // Build constraints based the textArea content
180 if (filter
!= null && !"".equals(filter
.trim())) {
182 String
[] strs
= filter
.trim().split(" ");
183 for (String token
: strs
) {
184 token
= token
.replace('*', '%');
185 StaticOperand so
= factory
.literal(session
186 .getValueFactory().createValue("%" + token
+ "%"));
188 Constraint currC
= factory
.comparison(catDO
,
189 QueryObjectModelFactory
.JCR_OPERATOR_LIKE
, so
);
190 currC
= factory
.or(currC
, factory
.comparison(nameDO
,
191 QueryObjectModelFactory
.JCR_OPERATOR_LIKE
, so
));
193 if (defaultC
== null)
196 defaultC
= factory
.and(defaultC
, currC
);
200 QueryObjectModel query
= factory
.createQuery(source
, defaultC
,
202 QueryResult result
= query
.execute();
203 return result
.getNodes();
204 } catch (RepositoryException re
) {
205 throw new SlcException(
206 "Unable to refresh group list for workspace "
207 + getEditorInput().getName(), re
);
211 private void createFilterPart(Composite parent
) {
212 GridLayout layout
= new GridLayout(2, false);
213 layout
.marginWidth
= layout
.marginHeight
= layout
.verticalSpacing
= 0;
214 layout
.horizontalSpacing
= 5;
215 parent
.setLayout(layout
);
217 // Text Area to filter
218 filterTxt
= tk
.createText(parent
, "", SWT
.BORDER
| SWT
.SINGLE
219 | SWT
.SEARCH
| SWT
.CANCEL
);
220 filterTxt
.setMessage(FILTER_HELP_MSG
);
221 filterTxt
.setLayoutData(new GridData(SWT
.FILL
, SWT
.TOP
, true, false));
222 filterTxt
.addModifyListener(new ModifyListener() {
223 public void modifyText(ModifyEvent event
) {
228 Button resetBtn
= tk
.createButton(parent
, null, SWT
.PUSH
);
229 resetBtn
.setImage(DistImages
.IMG_REPO_READONLY
);
230 resetBtn
.addSelectionListener(new SelectionAdapter() {
231 public void widgetSelected(SelectionEvent e
) {
237 private void resetFilter() {
238 filterTxt
.setText("");
239 filterTxt
.setMessage(FILTER_HELP_MSG
);
242 private void createTableViewer(Composite parent
) {
243 parent
.setLayout(new FillLayout());
244 // helpers to enable sorting by column
245 List
<String
> propertiesList
= new ArrayList
<String
>();
246 List
<Integer
> propertyTypesList
= new ArrayList
<Integer
>();
248 // Define the TableViewer
249 viewer
= new TableViewer(parent
, SWT
.SINGLE
| SWT
.H_SCROLL
250 | SWT
.V_SCROLL
| SWT
.FULL_SELECTION
| SWT
.BORDER
);
252 TableViewerColumn col
;
254 // TableViewerColumn col = new TableViewerColumn(viewer, SWT.NONE);
255 // col.getColumn().setWidth(220);
256 // col.getColumn().setText("Category");
257 // col.setLabelProvider(new ColumnLabelProvider() {
259 // public String getText(Object element) {
260 // return JcrUtils.get((Node) element, SlcNames.SLC_CATEGORY);
263 // col.getColumn().addSelectionListener(getSelectionAdapter(0));
264 // propertiesList.add(SlcNames.SLC_CATEGORY);
265 // propertyTypesList.add(PropertyType.STRING);
268 col
= new TableViewerColumn(viewer
, SWT
.NONE
);
269 col
.getColumn().setWidth(220);
270 col
.getColumn().setText("Group Name");
271 col
.setLabelProvider(new ColumnLabelProvider() {
273 public String
getText(Object element
) {
274 return JcrUtils
.get((Node
) element
, SLC_GROUP_BASE_ID
);
277 col
.getColumn().addSelectionListener(getSelectionAdapter(0));
278 propertiesList
.add(SLC_GROUP_BASE_ID
);
279 propertyTypesList
.add(PropertyType
.STRING
);
282 col
= new TableViewerColumn(viewer
, SWT
.NONE
);
283 col
.getColumn().setWidth(80);
284 col
.getColumn().setText("Relevant");
285 col
.setLabelProvider(new ColumnLabelProvider() {
287 public String
getText(Object element
) {
289 return ((Node
) element
)
290 .isNodeType(SlcTypes
.SLC_RELEVANT_CATEGORY
) ?
"Yes"
292 } catch (RepositoryException e
) {
293 throw new SlcException("unable to check type of node "
298 // col.getColumn().addSelectionListener(getSelectionAdapter(2));
299 // propertiesList.add(SLC_VERSION);
300 // propertyTypesList.add(PropertyType.STRING);
302 final Table table
= viewer
.getTable();
303 table
.setHeaderVisible(true);
304 table
.setLinesVisible(true);
306 viewer
.setContentProvider(new DistributionsContentProvider());
307 getSite().setSelectionProvider(viewer
);
309 comparator
= new NodeViewerComparator(0,
310 NodeViewerComparator
.ASCENDING
, propertiesList
,
312 viewer
.setComparator(comparator
);
315 MenuManager menuManager
= new MenuManager();
316 Menu menu
= menuManager
.createContextMenu(viewer
.getTable());
317 menuManager
.addMenuListener(new IMenuListener() {
318 public void menuAboutToShow(IMenuManager manager
) {
319 contextMenuAboutToShow(manager
);
322 viewer
.getTable().setMenu(menu
);
323 getSite().registerContextMenu(menuManager
, viewer
);
326 viewer
.addDoubleClickListener(new DoubleClickListener());
330 public void setFocus() {
331 viewer
.getTable().setFocus();
334 /** Programmatically configure the context menu */
335 protected void contextMenuAboutToShow(IMenuManager menuManager
) {
336 IWorkbenchWindow window
= DistPlugin
.getDefault().getWorkbench()
337 .getActiveWorkbenchWindow();
339 // Mark as category base
340 Object firstElement
= ((IStructuredSelection
) viewer
.getSelection())
342 Node currSelected
= (Node
) firstElement
;
344 boolean isRelevant
= false;
346 isRelevant
= currSelected
347 .isNodeType(SlcTypes
.SLC_RELEVANT_CATEGORY
);
348 boolean canEdit
= currSelected
349 .canAddMixin(SlcTypes
.SLC_RELEVANT_CATEGORY
);
351 } catch (RepositoryException e
) {
352 throw new SlcException("unable to check type of node "
356 if (isRelevant
) {// Remove
357 CommandUtils
.refreshCommand(menuManager
, window
,
358 MarkAsRelevantCategory
.ID
,
359 MarkAsRelevantCategory
.DEFAULT_REMOVE_LABEL
,
360 MarkAsRelevantCategory
.DEFAULT_REMOVE_ICON
, true);
362 CommandUtils
.refreshCommand(menuManager
, window
,
363 MarkAsRelevantCategory
.ID
,
364 MarkAsRelevantCategory
.DEFAULT_LABEL
,
365 MarkAsRelevantCategory
.DEFAULT_ICON
, true);
369 private SelectionAdapter
getSelectionAdapter(final int index
) {
370 SelectionAdapter selectionAdapter
= new SelectionAdapter() {
372 public void widgetSelected(SelectionEvent e
) {
373 Table table
= viewer
.getTable();
374 comparator
.setColumn(index
);
375 int dir
= table
.getSortDirection();
376 if (table
.getSortColumn() == table
.getColumn(index
)) {
377 dir
= dir
== SWT
.UP ? SWT
.DOWN
: SWT
.UP
;
381 table
.setSortDirection(dir
);
382 table
.setSortColumn(table
.getColumn(index
));
386 return selectionAdapter
;
390 private class DistributionsContentProvider
implements
391 IStructuredContentProvider
{
392 // we keep a cache of the Nodes in the content provider to be able to
393 // manage long request
394 private List
<Node
> nodes
;
396 public void dispose() {
399 // We expect a list of nodes as a new input
400 @SuppressWarnings("unchecked")
401 public void inputChanged(Viewer viewer
, Object oldInput
, Object newInput
) {
402 nodes
= (List
<Node
>) newInput
;
405 public Object
[] getElements(Object arg0
) {
406 return nodes
.toArray();
410 private class DoubleClickListener
implements IDoubleClickListener
{
412 public void doubleClick(DoubleClickEvent event
) {
413 Object obj
= ((IStructuredSelection
) event
.getSelection())
416 if (obj
instanceof Node
) {
417 Node node
= (Node
) obj
;
418 if (node
.isNodeType(SlcTypes
.SLC_BUNDLE_ARTIFACT
)) {
419 GenericBundleEditorInput gaei
= new GenericBundleEditorInput(
421 DistPlugin
.getDefault().getWorkbench()
422 .getActiveWorkbenchWindow().getActivePage()
423 .openEditor(gaei
, GenericBundleEditor
.ID
);
426 } catch (RepositoryException re
) {
427 throw new ArgeoException(
428 "Repository error while getting node info", re
);
429 } catch (PartInitException pie
) {
430 throw new ArgeoException(
431 "Unexepected exception while opening artifact editor",
437 class CategoryObserver
extends AsyncUiEventListener
{
439 public CategoryObserver(Display display
) {
444 protected Boolean
willProcessInUiThread(List
<Event
> events
)
445 throws RepositoryException
{
446 for (Event event
: events
) {
447 String path
= event
.getPath();
448 if (JcrUtils
.lastPathElement(path
).equals(
449 DistConstants
.JCR_MIXIN_TYPES
))
455 protected void onEventInUiThread(List
<Event
> events
)
456 throws RepositoryException
{
457 if (getLog().isTraceEnabled())
458 getLog().trace("Refresh table");
464 public void setActive(boolean active
) {
465 super.setActive(active
);