View Javadoc
1   package org.argeo.connect.ui.widgets;
2   
3   import static org.argeo.eclipse.ui.jcr.JcrUiUtils.getNodeSelectionAdapter;
4   
5   import java.util.ArrayList;
6   import java.util.List;
7   
8   import javax.jcr.Node;
9   import javax.jcr.NodeIterator;
10  import javax.jcr.Property;
11  import javax.jcr.PropertyType;
12  import javax.jcr.RepositoryException;
13  import javax.jcr.Session;
14  import javax.jcr.nodetype.NodeType;
15  import javax.jcr.query.Query;
16  import javax.jcr.query.QueryResult;
17  
18  import org.argeo.connect.ConnectException;
19  import org.argeo.connect.util.XPathUtils;
20  import org.argeo.eclipse.ui.EclipseUiUtils;
21  import org.argeo.eclipse.ui.jcr.lists.JcrColumnDefinition;
22  import org.argeo.eclipse.ui.jcr.lists.NodeViewerComparator;
23  import org.argeo.eclipse.ui.jcr.lists.SimpleJcrNodeLabelProvider;
24  import org.argeo.eclipse.ui.specific.EclipseUiSpecificUtils;
25  import org.argeo.eclipse.ui.utils.ViewerUtils;
26  import org.argeo.jcr.JcrUtils;
27  import org.eclipse.jface.viewers.CheckboxTableViewer;
28  import org.eclipse.jface.viewers.ColumnLabelProvider;
29  import org.eclipse.jface.viewers.IStructuredContentProvider;
30  import org.eclipse.jface.viewers.TableViewer;
31  import org.eclipse.jface.viewers.TableViewerColumn;
32  import org.eclipse.jface.viewers.Viewer;
33  import org.eclipse.swt.SWT;
34  import org.eclipse.swt.events.ModifyEvent;
35  import org.eclipse.swt.events.ModifyListener;
36  import org.eclipse.swt.events.SelectionAdapter;
37  import org.eclipse.swt.events.SelectionEvent;
38  import org.eclipse.swt.layout.GridData;
39  import org.eclipse.swt.layout.GridLayout;
40  import org.eclipse.swt.widgets.Composite;
41  import org.eclipse.swt.widgets.Table;
42  import org.eclipse.swt.widgets.Text;
43  
44  /** Canonical implementation of a table that display Jcr nodes */
45  public class SimpleJcrTableComposite extends Composite {
46  
47  	private static final long serialVersionUID = 3476100511651849356L;
48  	private TableViewer tableViewer;
49  	private Text filterTxt;
50  	private final static String FILTER_HELP_MSG = "Type filter criterion " + "separated by a space";
51  	private Session session;
52  
53  	private int tableStyle;
54  
55  	private boolean hasFilter = false;
56  	private boolean hasSelectionColumn = false;
57  
58  	private List<JcrColumnDefinition> colDefs = new ArrayList<JcrColumnDefinition>();
59  	{ // By default, it displays only title
60  		colDefs.add(new JcrColumnDefinition(null, Property.JCR_TITLE, PropertyType.STRING, "Name", 300));
61  	};
62  	private String nodeType = NodeType.NT_UNSTRUCTURED;
63  	private String parentPath = "/";
64  
65  	public List<JcrColumnDefinition> getColumnsDef() {
66  		return colDefs;
67  	}
68  
69  	// CONSTRUCTORS
70  
71  	/**
72  	 * Default table with no filter and no selection column that only display
73  	 * JCR_TITLE.
74  	 * 
75  	 * Default selector is NodeType.NT_UNSTRUCTURED, default parent path is the
76  	 * root path
77  	 * 
78  	 * @param parent
79  	 * @param style
80  	 *            the style of the table
81  	 * @param session
82  	 */
83  	public SimpleJcrTableComposite(Composite parent, int style, Session session) {
84  		super(parent, SWT.NONE);
85  		this.tableStyle = style;
86  		colDefs = new ArrayList<JcrColumnDefinition>();
87  		this.session = session;
88  		populate();
89  	}
90  
91  	/**
92  	 * 
93  	 * Caller might define column that are displayed, choose to add a filter and
94  	 * a selection column, define a parent path and/or a type for nodes that has
95  	 * to be listed
96  	 * 
97  	 * if some parameters are null, we use default values instead
98  	 * 
99  	 * @param parent
100 	 * @param style
101 	 *            the style of the table
102 	 * @param session
103 	 * @param colDefs
104 	 * @param addFilter
105 	 * @param addSelection
106 	 */
107 	public SimpleJcrTableComposite(Composite parent, int style, Session session, String parentPath, String nodeType,
108 			List<JcrColumnDefinition> colDefs, boolean addFilter, boolean addSelection) {
109 		super(parent, SWT.NONE);
110 		this.tableStyle = style;
111 		this.session = session;
112 		if (colDefs != null)
113 			this.colDefs = colDefs;
114 		if (nodeType != null)
115 			this.nodeType = nodeType;
116 		if (parentPath != null)
117 			this.parentPath = parentPath;
118 
119 		this.hasFilter = addFilter;
120 		this.hasSelectionColumn = addSelection;
121 		populate();
122 	}
123 
124 	protected void populate() {
125 		// initialization
126 		Composite parent = this;
127 		// Main Layout
128 		GridLayout gl = EclipseUiUtils.noSpaceGridLayout();
129 		gl.verticalSpacing = 5;
130 		this.setLayout(gl);
131 		if (hasFilter)
132 			createFilterPart(parent);
133 		tableViewer = createTableViewer(parent);
134 		EclipseUiSpecificUtils.enableToolTipSupport(tableViewer);
135 		tableViewer.setContentProvider(new MyTableContentProvider());
136 		refreshFilteredList();
137 
138 		if (hasFilter)
139 			filterTxt.setFocus();
140 	}
141 
142 	public List<Node> getSelectedNodes() {
143 		if (hasSelectionColumn) {
144 			Object[] elements = ((CheckboxTableViewer) tableViewer).getCheckedElements();
145 
146 			List<Node> result = new ArrayList<Node>();
147 			for (Object obj : elements) {
148 				result.add((Node) obj);
149 			}
150 			return result;
151 		} else
152 			throw new ConnectException(
153 					"Unvalid request: no selection column " + "has been created for the current table");
154 	}
155 
156 	/** Returns the User table viewer, typically to add doubleclick listener */
157 	public TableViewer getTableViewer() {
158 		return tableViewer;
159 	}
160 
161 	private TableViewer createTableViewer(final Composite parent) {
162 		// TODO make it virtual
163 		// tableStyle = tableStyle | SWT.VIRTUAL;
164 
165 		if (hasSelectionColumn)
166 			tableStyle = tableStyle | SWT.CHECK;
167 
168 		Table table = new Table(parent, tableStyle);
169 		table.setLayoutData(EclipseUiUtils.fillAll());
170 
171 		TableViewer viewer;
172 		if (hasSelectionColumn)
173 			viewer = new CheckboxTableViewer(table);
174 		else
175 			viewer = new TableViewer(table);
176 		table.setLinesVisible(true);
177 		table.setHeaderVisible(true);
178 
179 		TableViewerColumn column;
180 		int offset = 0;
181 		if (hasSelectionColumn) {
182 			offset = 1;
183 			column = ViewerUtils.createTableViewerColumn(viewer, "", SWT.NONE, 25);
184 			column.setLabelProvider(new ColumnLabelProvider() {
185 				private static final long serialVersionUID = 1L;
186 
187 				@Override
188 				public String getText(Object element) {
189 					return null;
190 				}
191 			});
192 			SelectionAdapter selectionAdapter = new SelectionAdapter() {
193 				private static final long serialVersionUID = 1L;
194 				boolean allSelected = false;
195 
196 				@Override
197 				public void widgetSelected(SelectionEvent e) {
198 					allSelected = !allSelected;
199 					((CheckboxTableViewer) tableViewer).setAllChecked(allSelected);
200 				}
201 			};
202 			column.getColumn().addSelectionListener(selectionAdapter);
203 		}
204 
205 		NodeViewerComparator comparator = new NodeViewerComparator();
206 		int i = offset;
207 		for (JcrColumnDefinition colDef : colDefs) {
208 			column = ViewerUtils.createTableViewerColumn(viewer, colDef.getHeaderLabel(), SWT.NONE,
209 					colDef.getColumnSize());
210 			column.setLabelProvider(new CLProvider(colDef.getPropertyName()));
211 			column.getColumn().addSelectionListener(
212 					getNodeSelectionAdapter(i, colDef.getPropertyType(), colDef.getPropertyName(), comparator, viewer));
213 			i++;
214 		}
215 
216 		// IMPORTANT: initialize comparator before setting it
217 		JcrColumnDefinition firstCol = colDefs.get(0);
218 		comparator.setColumn(firstCol.getPropertyType(), firstCol.getPropertyName());
219 		viewer.setComparator(comparator);
220 
221 		return viewer;
222 	}
223 
224 	private class CLProvider extends SimpleJcrNodeLabelProvider {
225 		private static final long serialVersionUID = 1L;
226 
227 		public CLProvider(String propertyName) {
228 			super(propertyName);
229 		}
230 
231 		public String getToolTipText(Object element) {
232 			return getText(element);
233 		}
234 	}
235 
236 	@Override
237 	public boolean setFocus() {
238 		tableViewer.getTable().setFocus();
239 		return true;
240 	}
241 
242 	@Override
243 	public void dispose() {
244 		super.dispose();
245 	}
246 
247 	public void refresh() {
248 		refreshFilteredList();
249 	}
250 
251 	private class MyTableContentProvider implements IStructuredContentProvider {
252 		private static final long serialVersionUID = 5933809732799991342L;
253 
254 		public Object[] getElements(Object inputElement) {
255 			return (Object[]) inputElement;
256 		}
257 
258 		public void dispose() {
259 		}
260 
261 		public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
262 		}
263 	}
264 
265 	/* MANAGE FILTER */
266 	private void createFilterPart(Composite parent) {
267 		// Text Area for the filter
268 		filterTxt = new Text(parent, SWT.BORDER | SWT.SEARCH | SWT.ICON_SEARCH | SWT.ICON_CANCEL);
269 		filterTxt.setMessage(FILTER_HELP_MSG);
270 		filterTxt.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL | GridData.HORIZONTAL_ALIGN_FILL));
271 		filterTxt.addModifyListener(new ModifyListener() {
272 			private static final long serialVersionUID = 1L;
273 
274 			public void modifyText(ModifyEvent event) {
275 				refreshFilteredList();
276 			}
277 		});
278 	}
279 
280 	/**
281 	 * Refresh the list: caller might overwrite in order to display a subset of
282 	 * all nodes
283 	 */
284 	protected void refreshFilteredList() {
285 		List<Node> nodes;
286 		try {
287 			nodes = JcrUtils.nodeIteratorToList(listFilteredElements(session, hasFilter ? filterTxt.getText() : null));
288 			tableViewer.setInput(nodes.toArray());
289 		} catch (RepositoryException e) {
290 			throw new ConnectException("Unable to list users", e);
291 		}
292 	}
293 
294 	/**
295 	 * Build repository request : caller might overwrite in order to display a
296 	 * subset
297 	 */
298 	protected NodeIterator listFilteredElements(Session session, String filter) throws RepositoryException {
299 		String xpathQueryStr = XPathUtils.descendantFrom(parentPath) + "//element(*, " + nodeType + ")";
300 		String attrQuery = XPathUtils.getFreeTextConstraint(filter);
301 		if (EclipseUiUtils.notEmpty(attrQuery))
302 			xpathQueryStr += "[" + attrQuery + "]";
303 		Query xpathQuery = XPathUtils.createQuery(session, xpathQueryStr);
304 		QueryResult result = xpathQuery.execute();
305 		return result.getNodes();
306 	}
307 }