1. Project Clover database Tue Dec 20 2016 21:24:09 CET
  2. Package org.xwiki.extension.xar.internal.job.diff

File DocumentUnifiedDiffBuilder.java

 

Coverage histogram

../../../../../../../img/srcFileCovDistChart0.png
83% of files have more coverage

Code metrics

66
96
15
1
317
225
50
0.52
6.4
15
3.33

Classes

Class Line # Actions
DocumentUnifiedDiffBuilder 54 96 0% 50 177
0.00%
 

Contributing tests

No tests hitting this source file were found.

Source view

1    /*
2    * See the NOTICE file distributed with this work for additional
3    * information regarding copyright ownership.
4    *
5    * This is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU Lesser General Public License as
7    * published by the Free Software Foundation; either version 2.1 of
8    * the License, or (at your option) any later version.
9    *
10    * This software is distributed in the hope that it will be useful,
11    * but WITHOUT ANY WARRANTY; without even the implied warranty of
12    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13    * Lesser General Public License for more details.
14    *
15    * You should have received a copy of the GNU Lesser General Public
16    * License along with this software; if not, write to the Free
17    * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18    * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
19    */
20    package org.xwiki.extension.xar.internal.job.diff;
21   
22    import java.util.List;
23    import java.util.Map;
24   
25    import javax.inject.Inject;
26    import javax.inject.Singleton;
27   
28    import org.xwiki.component.annotation.Component;
29    import org.xwiki.diff.display.UnifiedDiffBlock;
30    import org.xwiki.extension.xar.job.diff.DocumentUnifiedDiff;
31    import org.xwiki.extension.xar.job.diff.DocumentVersionReference;
32    import org.xwiki.extension.xar.job.diff.EntityUnifiedDiff;
33    import org.xwiki.model.reference.ClassPropertyReference;
34    import org.xwiki.model.reference.DocumentReference;
35    import org.xwiki.model.reference.ObjectReference;
36   
37    import com.xpn.xwiki.doc.XWikiAttachment;
38    import com.xpn.xwiki.doc.XWikiDocument;
39    import com.xpn.xwiki.objects.BaseCollection;
40    import com.xpn.xwiki.objects.BaseObject;
41    import com.xpn.xwiki.objects.BaseObjectReference;
42    import com.xpn.xwiki.objects.BaseProperty;
43    import com.xpn.xwiki.objects.classes.BaseClass;
44    import com.xpn.xwiki.objects.classes.PropertyClass;
45   
46    /**
47    * Computes the differences, in unified format, between two versions of a document.
48    *
49    * @version $Id: ce38ea019c5dfca554fe98b413824cf432533662 $
50    * @since 7.0RC1
51    */
52    @Component(roles = DocumentUnifiedDiffBuilder.class)
53    @Singleton
 
54    public class DocumentUnifiedDiffBuilder extends AbstractUnifiedDiffBuilder
55    {
56    /**
57    * The reference used to create an empty document when the passed document is null.
58    */
59    private static final DocumentReference EMPTY_DOCUMENT_REFERENCE = new DocumentReference("wiki", "Space",
60    "EmptyDocument");
61   
62    @Inject
63    private AttachmentUnifiedDiffBuilder attachmentDiffBuilder;
64   
65    /**
66    * Computes the differences, in unified format, between two versions of a document. A null document represents a
67    * deleted document.
68    *
69    * @param previousDocument the previous document version
70    * @param nextDocument the next document version
71    * @return the differences, in unified format, between the given document versions
72    */
 
73  0 toggle public DocumentUnifiedDiff diff(XWikiDocument previousDocument, XWikiDocument nextDocument)
74    {
75  0 DocumentUnifiedDiff diff =
76    new DocumentUnifiedDiff(getDocumentVersionReference(previousDocument),
77    getDocumentVersionReference(nextDocument));
78   
79  0 if (previousDocument != nextDocument) {
80  0 XWikiDocument nonNullPreviousDocument = emptyDocumentIfNull(previousDocument);
81  0 XWikiDocument nonNullNextDocument = emptyDocumentIfNull(nextDocument);
82   
83  0 addDocumentFieldDiffs(nonNullPreviousDocument, nonNullNextDocument, diff);
84  0 addAttachmentDiffs(nonNullPreviousDocument, nonNullNextDocument, diff);
85  0 addObjectDiffs(nonNullPreviousDocument, nonNullNextDocument, diff);
86  0 addClassPropertyDiffs(nonNullPreviousDocument.getXClass(), nonNullNextDocument.getXClass(), diff);
87    }
88   
89  0 return diff;
90    }
91   
 
92  0 toggle private DocumentVersionReference getDocumentVersionReference(XWikiDocument document)
93    {
94  0 if (document == null) {
95  0 return null;
96    } else {
97  0 DocumentVersionReference documentVersionReference =
98    new DocumentVersionReference(document.getDocumentReferenceWithLocale());
99  0 if (documentVersionReference.getVersion() == null) {
100  0 return new DocumentVersionReference(documentVersionReference, document.getVersion());
101    }
102  0 return documentVersionReference;
103    }
104    }
105   
 
106  0 toggle private XWikiDocument emptyDocumentIfNull(XWikiDocument document)
107    {
108  0 if (document == null) {
109  0 XWikiDocument emptyDocument = new XWikiDocument(EMPTY_DOCUMENT_REFERENCE);
110  0 emptyDocument.setSyntax(null);
111  0 return emptyDocument;
112    } else {
113  0 return document;
114    }
115    }
116   
 
117  0 toggle private boolean isNull(XWikiDocument document)
118    {
119  0 return document.getDocumentReference() == EMPTY_DOCUMENT_REFERENCE;
120    }
121   
122    /**
123    * Computes the document field differences between the given two document versions. Only the fields that the user
124    * can modify from the UI are taken into account.
125    *
126    * @param previousDocument the previous document version
127    * @param nextDocument the next document version
128    * @param documentDiff where to collect the differences
129    */
 
130  0 toggle private void addDocumentFieldDiffs(XWikiDocument previousDocument, XWikiDocument nextDocument,
131    DocumentUnifiedDiff documentDiff)
132    {
133  0 maybeAddDiff(documentDiff, "title", previousDocument.getTitle(), nextDocument.getTitle());
134  0 maybeAddDiff(documentDiff, "parent", previousDocument.getParentReference(), nextDocument.getParentReference());
135  0 maybeAddDiff(documentDiff, "hidden", isNull(previousDocument) ? null : previousDocument.isHidden(),
136  0 isNull(nextDocument) ? null : nextDocument.isHidden());
137  0 maybeAddDiff(documentDiff, "defaultLocale", previousDocument.getDefaultLocale(),
138    nextDocument.getDefaultLocale());
139  0 maybeAddDiff(documentDiff, "syntax", previousDocument.getSyntax(), nextDocument.getSyntax());
140  0 maybeAddDiff(documentDiff, CONTENT, previousDocument.getContent(), nextDocument.getContent());
141    }
142   
 
143  0 toggle private void addAttachmentDiffs(XWikiDocument previousDocument, XWikiDocument nextDocument,
144    DocumentUnifiedDiff documentDiff)
145    {
146    // Check the attachments that have been deleted of modified.
147  0 for (XWikiAttachment previousAttachment : previousDocument.getAttachmentList()) {
148  0 XWikiAttachment nextAttachment = nextDocument.getAttachment(previousAttachment.getFilename());
149  0 if (previousAttachment != nextAttachment) {
150  0 this.attachmentDiffBuilder.addAttachmentDiff(previousAttachment, nextAttachment, documentDiff);
151    }
152    }
153   
154    // Check the attachments that have been added.
155  0 for (XWikiAttachment nextAttachment : nextDocument.getAttachmentList()) {
156  0 XWikiAttachment previousAttachment = previousDocument.getAttachment(nextAttachment.getFilename());
157  0 if (previousAttachment == null) {
158  0 this.attachmentDiffBuilder.addAttachmentDiff(previousAttachment, nextAttachment, documentDiff);
159    }
160    }
161    }
162   
 
163  0 toggle private void addObjectDiffs(XWikiDocument previousDocument, XWikiDocument nextDocument,
164    DocumentUnifiedDiff documentDiff)
165    {
166    // Check the objects that have been deleted of modified.
167  0 for (List<BaseObject> previousObjects : previousDocument.getXObjects().values()) {
168  0 for (BaseObject previousObject : previousObjects) {
169    // It can be null when objects are deleted and the document is still in the cache storage.
170  0 if (previousObject != null) {
171  0 BaseObject nextObject =
172    nextDocument.getXObject(previousObject.getXClassReference(), previousObject.getNumber());
173  0 if (previousObject != nextObject) {
174  0 addObjectDiff(previousObject, nextObject, documentDiff);
175    }
176    }
177    }
178    }
179   
180    // Check the objects that have been added.
181  0 for (List<BaseObject> nextObjects : nextDocument.getXObjects().values()) {
182  0 for (BaseObject nextObject : nextObjects) {
183    // It can be null when objects are deleted and the document is still in the cache storage.
184  0 if (nextObject != null) {
185  0 BaseObject previousObject =
186    previousDocument.getXObject(nextObject.getXClassReference(), nextObject.getNumber());
187  0 if (previousObject == null) {
188  0 addObjectDiff(previousObject, nextObject, documentDiff);
189    }
190    }
191    }
192    }
193    }
194   
 
195  0 toggle private void addObjectDiff(BaseObject previousObject, BaseObject nextObject, DocumentUnifiedDiff documentDiff)
196    {
197  0 ObjectReference previousReference =
198    getObjectVersionReference(previousObject, documentDiff.getPreviousReference());
199  0 ObjectReference nextReference = getObjectVersionReference(nextObject, documentDiff.getNextReference());
200  0 EntityUnifiedDiff<ObjectReference> objectDiff = new EntityUnifiedDiff<>(previousReference, nextReference);
201   
202  0 addObjectDiff(previousObject == null ? new BaseObject() : previousObject, nextObject == null ? new BaseObject()
203    : nextObject, objectDiff);
204   
205  0 if (objectDiff.size() > 0) {
206  0 documentDiff.getObjectDiffs().add(objectDiff);
207    }
208    }
209   
 
210  0 toggle private ObjectReference getObjectVersionReference(BaseObject object,
211    DocumentVersionReference documentVersionReference)
212    {
213  0 return object == null ? null : new BaseObjectReference(object.getXClassReference(), object.getNumber(),
214    documentVersionReference);
215    }
216   
 
217  0 toggle private void addObjectDiff(BaseCollection<?> previousObject, BaseCollection<?> nextObject,
218    Map<String, List<UnifiedDiffBlock<String, Character>>> objectDiff)
219    {
220    // Check the properties that have been deleted or modified.
221  0 for (String propertyName : previousObject.getPropertyList()) {
222  0 BaseProperty<?> previousProperty = (BaseProperty<?>) previousObject.getField(propertyName);
223  0 BaseProperty<?> nextProperty = (BaseProperty<?>) nextObject.getField(propertyName);
224  0 if (previousProperty != nextProperty) {
225  0 addObjectPropertyDiff(previousProperty, nextProperty, objectDiff);
226    }
227    }
228   
229    // Check the properties that have been added.
230  0 for (String propertyName : nextObject.getPropertyList()) {
231  0 BaseProperty<?> previousProperty = (BaseProperty<?>) previousObject.getField(propertyName);
232  0 BaseProperty<?> nextProperty = (BaseProperty<?>) nextObject.getField(propertyName);
233  0 if (previousProperty == null) {
234  0 addObjectPropertyDiff(previousProperty, nextProperty, objectDiff);
235    }
236    }
237    }
238   
 
239  0 toggle private void addObjectPropertyDiff(BaseProperty<?> previousProperty, BaseProperty<?> nextProperty,
240    Map<String, List<UnifiedDiffBlock<String, Character>>> objectDiff)
241    {
242  0 String key = previousProperty == null ? nextProperty.getName() : previousProperty.getName();
243  0 Object previousValue = previousProperty == null ? null : previousProperty.getValue();
244  0 Object nextValue = nextProperty == null ? null : nextProperty.getValue();
245  0 if (maybeAddDiff(objectDiff, key, previousValue, nextValue)
246    && (isPrivateProperty(previousProperty) || isPrivateProperty(nextProperty))) {
247    // Empty the differences if the property is private.
248  0 objectDiff.get(key).clear();
249    }
250    }
251   
 
252  0 toggle private boolean isPrivateProperty(BaseProperty<?> property)
253    {
254  0 BaseCollection<?> object = property == null ? null : property.getObject();
255  0 if (object != null) {
256  0 BaseClass xclass = object.getXClass(this.xcontextProvider.get());
257  0 if (xclass != null) {
258  0 PropertyClass propertyClass = (PropertyClass) xclass.get(property.getName());
259  0 String propertyType = propertyClass == null ? null : propertyClass.getClassType();
260  0 return "Password".equals(propertyType) || "Email".equals(propertyClass);
261    }
262    }
263  0 return false;
264    }
265   
 
266  0 toggle private void addClassPropertyDiffs(BaseClass previousClass, BaseClass nextClass, DocumentUnifiedDiff documentDiff)
267    {
268    // Check the properties that have been deleted or modified.
269  0 for (String propertyName : previousClass.getPropertyList()) {
270  0 PropertyClass previousProperty = (PropertyClass) previousClass.get(propertyName);
271  0 PropertyClass nextProperty = (PropertyClass) nextClass.get(propertyName);
272  0 addClassPropertyDiff(previousProperty, nextProperty, documentDiff);
273    }
274   
275    // Check the properties that have been added.
276  0 for (String propertyName : nextClass.getPropertyList()) {
277  0 PropertyClass previousProperty = (PropertyClass) previousClass.get(propertyName);
278  0 PropertyClass nextProperty = (PropertyClass) nextClass.get(propertyName);
279  0 if (previousProperty == null) {
280  0 addClassPropertyDiff(previousProperty, nextProperty, documentDiff);
281    }
282    }
283    }
284   
 
285  0 toggle private void addClassPropertyDiff(PropertyClass previousProperty, PropertyClass nextProperty,
286    DocumentUnifiedDiff documentDiff)
287    {
288  0 ClassPropertyReference previousReference =
289    getClassPropertyVersionReference(previousProperty, documentDiff.getPreviousReference());
290  0 ClassPropertyReference nextReference =
291    getClassPropertyVersionReference(nextProperty, documentDiff.getNextReference());
292  0 EntityUnifiedDiff<ClassPropertyReference> classPropertyDiff =
293    new EntityUnifiedDiff<>(previousReference, nextReference);
294   
295    // Catch a property type change.
296  0 maybeAddDiff(classPropertyDiff, "type", previousProperty == null ? null : previousProperty.getClassType(),
297  0 nextProperty == null ? null : nextProperty.getClassType());
298   
299  0 addObjectDiff(previousProperty == null ? new PropertyClass() : previousProperty, nextProperty == null
300    ? new PropertyClass() : nextProperty, classPropertyDiff);
301   
302    // The property name is already specified by the previous / next reference.
303  0 classPropertyDiff.remove("name");
304    // This meta property is not used (there's no UI to change it).
305  0 classPropertyDiff.remove("unmodifiable");
306   
307  0 if (classPropertyDiff.size() > 0) {
308  0 documentDiff.getClassPropertyDiffs().add(classPropertyDiff);
309    }
310    }
311   
 
312  0 toggle private ClassPropertyReference getClassPropertyVersionReference(PropertyClass property,
313    DocumentVersionReference documentVersionReference)
314    {
315  0 return property == null ? null : new ClassPropertyReference(property.getName(), documentVersionReference);
316    }
317    }