]> git.argeo.org Git - lgpl/argeo-commons.git/blob - editors/NodeVersionHistoryPage.java
Prepare next development cycle
[lgpl/argeo-commons.git] / editors / NodeVersionHistoryPage.java
1 package org.argeo.jcr.ui.explorer.editors;
2
3 import java.text.DateFormat;
4 import java.text.SimpleDateFormat;
5 import java.util.ArrayList;
6 import java.util.Calendar;
7 import java.util.List;
8 import java.util.Map;
9
10 import javax.jcr.Node;
11 import javax.jcr.Property;
12 import javax.jcr.PropertyType;
13 import javax.jcr.RepositoryException;
14 import javax.jcr.Value;
15 import javax.jcr.nodetype.NodeType;
16 import javax.jcr.version.Version;
17 import javax.jcr.version.VersionHistory;
18 import javax.jcr.version.VersionIterator;
19 import javax.jcr.version.VersionManager;
20
21 import org.apache.commons.logging.Log;
22 import org.apache.commons.logging.LogFactory;
23 import org.argeo.ArgeoException;
24 import org.argeo.jcr.JcrUtils;
25 import org.argeo.jcr.PropertyDiff;
26 import org.argeo.jcr.VersionDiff;
27 import org.argeo.jcr.ui.explorer.JcrExplorerConstants;
28 import org.argeo.jcr.ui.explorer.JcrExplorerPlugin;
29 import org.argeo.jcr.ui.explorer.providers.FullVersioningTreeContentProvider;
30 import org.argeo.jcr.ui.explorer.providers.VersionLabelProvider;
31 import org.argeo.jcr.ui.explorer.utils.GenericNodeDoubleClickListener;
32 import org.eclipse.jface.viewers.ITreeContentProvider;
33 import org.eclipse.jface.viewers.TreeViewer;
34 import org.eclipse.swt.SWT;
35 import org.eclipse.swt.layout.GridData;
36 import org.eclipse.swt.layout.GridLayout;
37 import org.eclipse.swt.widgets.Composite;
38 import org.eclipse.swt.widgets.Text;
39 import org.eclipse.ui.forms.AbstractFormPart;
40 import org.eclipse.ui.forms.IManagedForm;
41 import org.eclipse.ui.forms.editor.FormEditor;
42 import org.eclipse.ui.forms.editor.FormPage;
43 import org.eclipse.ui.forms.widgets.FormToolkit;
44 import org.eclipse.ui.forms.widgets.ScrolledForm;
45 import org.eclipse.ui.forms.widgets.Section;
46 import org.eclipse.ui.forms.widgets.TableWrapData;
47 import org.eclipse.ui.forms.widgets.TableWrapLayout;
48
49 /**
50 * Offers two main sections : one to display a text area with a summary of all
51 * variations between a version and its predecessor and one tree view that
52 * enable browsing
53 * */
54 public class NodeVersionHistoryPage extends FormPage implements
55 JcrExplorerConstants {
56 private final static Log log = LogFactory
57 .getLog(NodeVersionHistoryPage.class);
58
59 // Utils
60 protected DateFormat timeFormatter = new SimpleDateFormat(DATE_TIME_FORMAT);
61
62 // business objects
63 private Node currentNode;
64
65 // this page UI components
66 private FullVersioningTreeContentProvider nodeContentProvider;
67 private TreeViewer nodesViewer;
68 private FormToolkit tk;
69
70 public NodeVersionHistoryPage(FormEditor editor, String title,
71 Node currentNode) {
72 super(editor, "NodeVersionHistoryPage", title);
73 this.currentNode = currentNode;
74 }
75
76 protected void createFormContent(IManagedForm managedForm) {
77 ScrolledForm form = managedForm.getForm();
78 tk = managedForm.getToolkit();
79 GridLayout twt = new GridLayout(1, false);
80 twt.marginWidth = twt.marginHeight = 5;
81 Composite body = form.getBody();
82 body.setLayout(twt);
83
84 try {
85 if (!currentNode.isNodeType(NodeType.MIX_VERSIONABLE)) {
86 tk.createLabel(body, JcrExplorerPlugin
87 .getMessage("warningUnversionableNode"));
88 } else {
89 createHistorySection(form.getBody());
90 createTreeSection(form.getBody());
91 }
92 } catch (RepositoryException e) {
93 throw new ArgeoException(
94 "Unexpected error while checking if node is versionable", e);
95 }
96 }
97
98 protected void createTreeSection(Composite parent) {
99 // Section Layout & MetaData
100 Section section = tk.createSection(parent, Section.TWISTIE);
101 section.setLayoutData(new GridData(GridData.FILL_BOTH));
102 section.setText(JcrExplorerPlugin.getMessage("versionTreeSectionTitle"));
103
104 // Section Body
105 Composite body = tk.createComposite(section, SWT.FILL);
106 // WARNING : 2 following lines are compulsory or body won't be
107 // displayed.
108 body.setLayout(new GridLayout());
109 section.setClient(body);
110
111 body.setLayoutData(new GridData(GridData.FILL_BOTH));
112 section.setExpanded(true);
113
114 nodeContentProvider = new FullVersioningTreeContentProvider();
115 nodesViewer = createNodeViewer(body, nodeContentProvider);
116 nodesViewer.setInput(currentNode);
117 }
118
119 protected TreeViewer createNodeViewer(Composite parent,
120 final ITreeContentProvider nodeContentProvider) {
121
122 final TreeViewer tmpNodeViewer = new TreeViewer(parent, SWT.MULTI);
123
124 tmpNodeViewer.getTree().setLayoutData(
125 new GridData(SWT.FILL, SWT.FILL, true, true));
126
127 tmpNodeViewer.setContentProvider(nodeContentProvider);
128 tmpNodeViewer.setLabelProvider(new VersionLabelProvider());
129 tmpNodeViewer
130 .addDoubleClickListener(new GenericNodeDoubleClickListener(
131 tmpNodeViewer));
132 return tmpNodeViewer;
133 }
134
135 protected void createHistorySection(Composite parent) {
136
137 // Section Layout
138 Section section = tk.createSection(parent, Section.TWISTIE);
139 section.setLayoutData(new GridData(TableWrapData.FILL_GRAB));
140 TableWrapLayout twt = new TableWrapLayout();
141 section.setLayout(twt);
142
143 // Set title of the section
144 section.setText(JcrExplorerPlugin
145 .getMessage("versionHistorySectionTitle"));
146
147 final Text styledText = tk.createText(section, "", SWT.FULL_SELECTION
148 | SWT.BORDER | SWT.MULTI | SWT.WRAP | SWT.V_SCROLL);
149 styledText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
150 section.setClient(styledText);
151 refreshHistory(styledText);
152 styledText.setEditable(false);
153 section.setExpanded(false);
154
155 AbstractFormPart part = new AbstractFormPart() {
156 public void commit(boolean onSave) {
157 }
158
159 public void refresh() {
160 super.refresh();
161 refreshHistory(styledText);
162 }
163 };
164 getManagedForm().addPart(part);
165 }
166
167 protected void refreshHistory(Text styledText) {
168 try {
169 List<VersionDiff> lst = listHistoryDiff();
170 StringBuffer main = new StringBuffer("");
171
172 for (int i = lst.size() - 1; i >= 0; i--) {
173 if (i == 0)
174 main.append("Creation (");
175 else
176 main.append("Update " + i + " (");
177
178 if (lst.get(i).getUserId() != null)
179 main.append("UserId : " + lst.get(i).getUserId());
180
181 if (lst.get(i).getUserId() != null
182 && lst.get(i).getUpdateTime() != null)
183 main.append(", ");
184
185 if (lst.get(i).getUpdateTime() != null)
186 main.append("Date : "
187 + timeFormatter.format(lst.get(i).getUpdateTime()
188 .getTime()) + ")\n");
189
190 StringBuffer buf = new StringBuffer("");
191 Map<String, PropertyDiff> diffs = lst.get(i).getDiffs();
192 props: for (String prop : diffs.keySet()) {
193 PropertyDiff pd = diffs.get(prop);
194 String propName = pd.getRelPath();
195 Value refValue = pd.getReferenceValue();
196 Value newValue = pd.getNewValue();
197 String refValueStr = "";
198 String newValueStr = "";
199
200 if (refValue != null) {
201 if (refValue.getType() == PropertyType.DATE) {
202 refValueStr = timeFormatter.format(refValue
203 .getDate().getTime());
204 } else
205 refValueStr = refValue.getString();
206 }
207 if (newValue != null) {
208 if (newValue.getType() == PropertyType.DATE) {
209 newValueStr = timeFormatter.format(newValue
210 .getDate().getTime());
211 } else
212 newValueStr = newValue.getString();
213 }
214
215 if (pd.getType() == PropertyDiff.MODIFIED) {
216 buf.append(prop).append(": ");
217 buf.append(refValueStr);
218 buf.append(" > ");
219 buf.append(newValueStr);
220 buf.append("\n");
221 } else if (pd.getType() == PropertyDiff.ADDED
222 && !"".equals(newValueStr)) {
223 // we don't list property that have been added with an
224 // empty string as value
225 buf.append(prop).append(": ");
226 buf.append(" + ");
227 buf.append(newValueStr);
228 buf.append("\n");
229 } else if (pd.getType() == PropertyDiff.REMOVED) {
230 buf.append(prop).append(": ");
231 buf.append(" - ");
232 buf.append(refValueStr);
233 buf.append("\n");
234 }
235 }
236 buf.append("\n");
237 main.append(buf);
238 }
239 styledText.setText(main.toString());
240 } catch (RepositoryException e) {
241 throw new ArgeoException("Cannot generate history for node", e);
242 }
243
244 }
245
246 public List<VersionDiff> listHistoryDiff() {
247 try {
248 List<VersionDiff> res = new ArrayList<VersionDiff>();
249 VersionManager versionManager = currentNode.getSession()
250 .getWorkspace().getVersionManager();
251 VersionHistory versionHistory = versionManager
252 .getVersionHistory(currentNode.getPath());
253
254 VersionIterator vit = versionHistory.getAllLinearVersions();
255 while (vit.hasNext()) {
256 Version version = vit.nextVersion();
257 Node node = version.getFrozenNode();
258 Version predecessor = null;
259 try {
260 predecessor = version.getLinearPredecessor();
261 } catch (Exception e) {
262 // no predecessor seems to throw an exception even if it
263 // shouldn't...
264 }
265 if (predecessor == null) {// original
266 } else {
267 Map<String, PropertyDiff> diffs = JcrUtils.diffProperties(
268 predecessor.getFrozenNode(), node);
269 if (!diffs.isEmpty()) {
270 String lastUserName = null;
271 Calendar lastUpdate = null;
272 try {
273 if (currentNode
274 .isNodeType(NodeType.MIX_LAST_MODIFIED)) {
275 lastUserName = node.getProperty(
276 Property.JCR_LAST_MODIFIED_BY)
277 .getString();
278 lastUpdate = node.getProperty(
279 Property.JCR_LAST_MODIFIED).getDate();
280 } else
281 lastUpdate = version.getProperty(
282 Property.JCR_CREATED).getDate();
283
284 } catch (Exception e) {
285 // Silent that info is optionnal
286 }
287 VersionDiff vd = new VersionDiff(lastUserName,
288 lastUpdate, diffs);
289 res.add(vd);
290 }
291 }
292 }
293 return res;
294 } catch (RepositoryException e) {
295 throw new ArgeoException("Cannot generate history for node ");
296 }
297
298 }
299
300 @Override
301 public void setActive(boolean active) {
302 super.setActive(active);
303 }
304 }