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.cms.ui.workbench.jcr;
17  
18  import java.util.List;
19  
20  import javax.jcr.Property;
21  import javax.jcr.PropertyType;
22  import javax.jcr.Repository;
23  import javax.jcr.RepositoryException;
24  import javax.jcr.RepositoryFactory;
25  import javax.jcr.Session;
26  import javax.jcr.Value;
27  import javax.jcr.observation.Event;
28  import javax.jcr.observation.EventListener;
29  import javax.jcr.observation.ObservationManager;
30  
31  import org.argeo.cms.ui.jcr.DefaultRepositoryRegister;
32  import org.argeo.cms.ui.jcr.JcrBrowserUtils;
33  import org.argeo.cms.ui.jcr.JcrDClickListener;
34  import org.argeo.cms.ui.jcr.NodeContentProvider;
35  import org.argeo.cms.ui.jcr.NodeLabelProvider;
36  import org.argeo.cms.ui.jcr.PropertiesContentProvider;
37  import org.argeo.cms.ui.jcr.RepositoryRegister;
38  import org.argeo.cms.ui.jcr.model.SingleJcrNodeElem;
39  import org.argeo.cms.ui.workbench.WorkbenchUiPlugin;
40  import org.argeo.eclipse.ui.EclipseUiException;
41  import org.argeo.eclipse.ui.TreeParent;
42  import org.argeo.eclipse.ui.jcr.AsyncUiEventListener;
43  import org.argeo.eclipse.ui.jcr.utils.NodeViewerComparer;
44  import org.argeo.node.security.Keyring;
45  import org.eclipse.jface.action.MenuManager;
46  import org.eclipse.jface.viewers.ColumnLabelProvider;
47  import org.eclipse.jface.viewers.ISelectionChangedListener;
48  import org.eclipse.jface.viewers.IStructuredSelection;
49  import org.eclipse.jface.viewers.ITreeContentProvider;
50  import org.eclipse.jface.viewers.SelectionChangedEvent;
51  import org.eclipse.jface.viewers.StructuredSelection;
52  import org.eclipse.jface.viewers.TableViewer;
53  import org.eclipse.jface.viewers.TableViewerColumn;
54  import org.eclipse.jface.viewers.TreeViewer;
55  import org.eclipse.swt.SWT;
56  import org.eclipse.swt.custom.SashForm;
57  import org.eclipse.swt.layout.FillLayout;
58  import org.eclipse.swt.layout.GridData;
59  import org.eclipse.swt.layout.GridLayout;
60  import org.eclipse.swt.widgets.Composite;
61  import org.eclipse.swt.widgets.Display;
62  import org.eclipse.swt.widgets.Menu;
63  import org.eclipse.ui.part.ViewPart;
64  
65  /**
66   * Basic View to display a sash form to browse a JCR compliant multiple
67   * repository environment
68   */
69  public class JcrBrowserView extends ViewPart {
70  	public final static String ID = WorkbenchUiPlugin.PLUGIN_ID + ".jcrBrowserView";
71  	private boolean sortChildNodes = true;
72  
73  	/* DEPENDENCY INJECTION */
74  	private Keyring keyring;
75  	private RepositoryRegister repositoryRegister = new DefaultRepositoryRegister();
76  	private RepositoryFactory repositoryFactory;
77  	private Repository nodeRepository;
78  
79  	// Current user session on the "Argeo node" default workspace
80  	private Session userSession;
81  
82  	// This page widgets
83  	private TreeViewer nodesViewer;
84  	private NodeContentProvider nodeContentProvider;
85  	private TableViewer propertiesViewer;
86  	private EventListener resultsObserver;
87  
88  	@Override
89  	public void createPartControl(Composite parent) {
90  		parent.setLayout(new FillLayout());
91  		SashForm sashForm = new SashForm(parent, SWT.VERTICAL);
92  		sashForm.setSashWidth(4);
93  		sashForm.setLayout(new FillLayout());
94  
95  		// Create the tree on top of the view
96  		Composite top = new Composite(sashForm, SWT.NONE);
97  		GridLayout gl = new GridLayout(1, false);
98  		top.setLayout(gl);
99  
100 		try {
101 			this.userSession = this.nodeRepository.login();
102 		} catch (RepositoryException e) {
103 			throw new EclipseUiException("Cannot open user session", e);
104 		}
105 
106 		nodeContentProvider = new NodeContentProvider(userSession, keyring, repositoryRegister, repositoryFactory,
107 				sortChildNodes);
108 
109 		// nodes viewer
110 		nodesViewer = createNodeViewer(top, nodeContentProvider);
111 
112 		// context menu : it is completely defined in the plugin.xml file.
113 		MenuManager menuManager = new MenuManager();
114 		Menu menu = menuManager.createContextMenu(nodesViewer.getTree());
115 
116 		nodesViewer.getTree().setMenu(menu);
117 		getSite().registerContextMenu(menuManager, nodesViewer);
118 		getSite().setSelectionProvider(nodesViewer);
119 
120 		nodesViewer.setInput(getViewSite());
121 
122 		// Create the property viewer on the bottom
123 		Composite bottom = new Composite(sashForm, SWT.NONE);
124 		bottom.setLayout(new GridLayout(1, false));
125 		propertiesViewer = createPropertiesViewer(bottom);
126 
127 		sashForm.setWeights(getWeights());
128 		nodesViewer.setComparer(new NodeViewerComparer());
129 	}
130 
131 	public void refresh(Object obj) {
132 		// Enable full refresh from a command when no element of the tree is
133 		// selected
134 		if (obj == null) {
135 			Object[] elements = nodeContentProvider.getElements(null);
136 			for (Object el : elements) {
137 				if (el instanceof TreeParent)
138 					JcrBrowserUtils.forceRefreshIfNeeded((TreeParent) el);
139 				getNodeViewer().refresh(el);
140 			}
141 		} else
142 			getNodeViewer().refresh(obj);
143 	}
144 
145 	/**
146 	 * To be overridden to adapt size of form and result frames.
147 	 */
148 	protected int[] getWeights() {
149 		return new int[] { 70, 30 };
150 	}
151 
152 	protected TreeViewer createNodeViewer(Composite parent, final ITreeContentProvider nodeContentProvider) {
153 
154 		final TreeViewer tmpNodeViewer = new TreeViewer(parent, SWT.MULTI);
155 
156 		tmpNodeViewer.getTree().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
157 
158 		tmpNodeViewer.setContentProvider(nodeContentProvider);
159 		tmpNodeViewer.setLabelProvider(new NodeLabelProvider());
160 		tmpNodeViewer.addSelectionChangedListener(new ISelectionChangedListener() {
161 			public void selectionChanged(SelectionChangedEvent event) {
162 				if (!event.getSelection().isEmpty()) {
163 					IStructuredSelection sel = (IStructuredSelection) event.getSelection();
164 					Object firstItem = sel.getFirstElement();
165 					if (firstItem instanceof SingleJcrNodeElem)
166 						propertiesViewer.setInput(((SingleJcrNodeElem) firstItem).getNode());
167 				} else {
168 					propertiesViewer.setInput(getViewSite());
169 				}
170 			}
171 		});
172 
173 		resultsObserver = new TreeObserver(tmpNodeViewer.getTree().getDisplay());
174 		if (keyring != null)
175 			try {
176 				ObservationManager observationManager = userSession.getWorkspace().getObservationManager();
177 				observationManager.addEventListener(resultsObserver, Event.PROPERTY_ADDED | Event.PROPERTY_CHANGED, "/",
178 						true, null, null, false);
179 			} catch (RepositoryException e) {
180 				throw new EclipseUiException("Cannot register listeners", e);
181 			}
182 
183 		tmpNodeViewer.addDoubleClickListener(new JcrDClickListener(tmpNodeViewer));
184 		return tmpNodeViewer;
185 	}
186 
187 	protected TableViewer createPropertiesViewer(Composite parent) {
188 		propertiesViewer = new TableViewer(parent);
189 		propertiesViewer.getTable().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
190 		propertiesViewer.getTable().setHeaderVisible(true);
191 		propertiesViewer.setContentProvider(new PropertiesContentProvider());
192 		TableViewerColumn col = new TableViewerColumn(propertiesViewer, SWT.NONE);
193 		col.getColumn().setText("Name");
194 		col.getColumn().setWidth(200);
195 		col.setLabelProvider(new ColumnLabelProvider() {
196 			private static final long serialVersionUID = -6684361063107478595L;
197 
198 			public String getText(Object element) {
199 				try {
200 					return ((Property) element).getName();
201 				} catch (RepositoryException e) {
202 					throw new EclipseUiException("Unexpected exception in label provider", e);
203 				}
204 			}
205 		});
206 		col = new TableViewerColumn(propertiesViewer, SWT.NONE);
207 		col.getColumn().setText("Value");
208 		col.getColumn().setWidth(400);
209 		col.setLabelProvider(new ColumnLabelProvider() {
210 			private static final long serialVersionUID = -8201994187693336657L;
211 
212 			public String getText(Object element) {
213 				try {
214 					Property property = (Property) element;
215 					if (property.getType() == PropertyType.BINARY)
216 						return "<binary>";
217 					else if (property.isMultiple()) {
218 						StringBuffer buf = new StringBuffer("[");
219 						Value[] values = property.getValues();
220 						for (int i = 0; i < values.length; i++) {
221 							if (i != 0)
222 								buf.append(", ");
223 							buf.append(values[i].getString());
224 						}
225 						buf.append(']');
226 						return buf.toString();
227 					} else
228 						return property.getValue().getString();
229 				} catch (RepositoryException e) {
230 					throw new EclipseUiException("Unexpected exception in label provider", e);
231 				}
232 			}
233 		});
234 		col = new TableViewerColumn(propertiesViewer, SWT.NONE);
235 		col.getColumn().setText("Type");
236 		col.getColumn().setWidth(200);
237 		col.setLabelProvider(new ColumnLabelProvider() {
238 			private static final long serialVersionUID = -6009599998150286070L;
239 
240 			public String getText(Object element) {
241 				return JcrBrowserUtils.getPropertyTypeAsString((Property) element);
242 			}
243 		});
244 		propertiesViewer.setInput(getViewSite());
245 		return propertiesViewer;
246 	}
247 
248 	@Override
249 	public void dispose() {
250 		super.dispose();
251 	}
252 
253 	protected TreeViewer getNodeViewer() {
254 		return nodesViewer;
255 	}
256 
257 	/**
258 	 * Resets the tree content provider
259 	 * 
260 	 * @param sortChildNodes
261 	 *            if true the content provider will use a comparer to sort nodes
262 	 *            that might slow down the display
263 	 */
264 	public void setSortChildNodes(boolean sortChildNodes) {
265 		this.sortChildNodes = sortChildNodes;
266 		((NodeContentProvider) nodesViewer.getContentProvider()).setSortChildren(sortChildNodes);
267 		nodesViewer.setInput(getViewSite());
268 	}
269 
270 	/** Notifies the current view that a node has been added */
271 	public void nodeAdded(TreeParent parentNode) {
272 		// insure that Ui objects have been correctly created:
273 		JcrBrowserUtils.forceRefreshIfNeeded(parentNode);
274 		getNodeViewer().refresh(parentNode);
275 		getNodeViewer().expandToLevel(parentNode, 1);
276 	}
277 
278 	/** Notifies the current view that a node has been removed */
279 	public void nodeRemoved(TreeParent parentNode) {
280 		IStructuredSelection newSel = new StructuredSelection(parentNode);
281 		getNodeViewer().setSelection(newSel, true);
282 		// Force refresh
283 		IStructuredSelection tmpSel = (IStructuredSelection) getNodeViewer().getSelection();
284 		getNodeViewer().refresh(tmpSel.getFirstElement());
285 	}
286 
287 	class TreeObserver extends AsyncUiEventListener {
288 
289 		public TreeObserver(Display display) {
290 			super(display);
291 		}
292 
293 		@Override
294 		protected Boolean willProcessInUiThread(List<Event> events) throws RepositoryException {
295 			for (Event event : events) {
296 				if (getLog().isTraceEnabled())
297 					getLog().debug("Received event " + event);
298 				String path = event.getPath();
299 				int index = path.lastIndexOf('/');
300 				String propertyName = path.substring(index + 1);
301 				if (getLog().isTraceEnabled())
302 					getLog().debug("Concerned property " + propertyName);
303 			}
304 			return false;
305 		}
306 
307 		protected void onEventInUiThread(List<Event> events) throws RepositoryException {
308 			if (getLog().isTraceEnabled())
309 				getLog().trace("Refresh result list");
310 			nodesViewer.refresh();
311 		}
312 
313 	}
314 
315 	public boolean getSortChildNodes() {
316 		return sortChildNodes;
317 	}
318 
319 	@Override
320 	public void setFocus() {
321 		getNodeViewer().getTree().setFocus();
322 	}
323 
324 	/* DEPENDENCY INJECTION */
325 	public void setRepositoryRegister(RepositoryRegister repositoryRegister) {
326 		this.repositoryRegister = repositoryRegister;
327 	}
328 
329 	public void setKeyring(Keyring keyring) {
330 		this.keyring = keyring;
331 	}
332 
333 	public void setRepositoryFactory(RepositoryFactory repositoryFactory) {
334 		this.repositoryFactory = repositoryFactory;
335 	}
336 
337 	public void setNodeRepository(Repository nodeRepository) {
338 		this.nodeRepository = nodeRepository;
339 	}
340 }