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

File DocumentMergeImporter.java

 

Coverage histogram

../../../../../../../img/srcFileCovDistChart8.png
54% of files have more coverage

Code metrics

62
129
8
1
351
251
48
0.37
16.12
8
6

Classes

Class Line # Actions
DocumentMergeImporter 53 129 0% 48 41
0.7939698779.4%
 

Contributing tests

This file is covered by 28 tests. .

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.handler.packager;
21   
22    import java.util.Date;
23   
24    import javax.inject.Inject;
25    import javax.inject.Provider;
26    import javax.inject.Singleton;
27   
28    import org.slf4j.Logger;
29    import org.xwiki.component.annotation.Component;
30    import org.xwiki.context.Execution;
31    import org.xwiki.extension.xar.question.ConflictQuestion;
32    import org.xwiki.extension.xar.question.ConflictQuestion.GlobalAction;
33    import org.xwiki.logging.LogLevel;
34    import org.xwiki.model.reference.DocumentReference;
35    import org.xwiki.model.reference.LocalDocumentReference;
36    import org.xwiki.xar.XarEntry;
37   
38    import com.xpn.xwiki.XWikiContext;
39    import com.xpn.xwiki.doc.MandatoryDocumentInitializer;
40    import com.xpn.xwiki.doc.MandatoryDocumentInitializerManager;
41    import com.xpn.xwiki.doc.XWikiAttachment;
42    import com.xpn.xwiki.doc.XWikiDocument;
43    import com.xpn.xwiki.doc.merge.MergeConfiguration;
44    import com.xpn.xwiki.doc.merge.MergeResult;
45   
46    /**
47    * Take care of properly merging and saving a document.
48    *
49    * @version $Id: 045b791632c75eac7e8ae9aaf61134bd1b74c23b $
50    */
51    @Component(roles = DocumentMergeImporter.class)
52    @Singleton
 
53    public class DocumentMergeImporter
54    {
55    static final String PROP_ALWAYS_MERGE = "extension.xar.packager.conflict.always.merge";
56   
57    static final String PROP_ALWAYS_NOMERGE = "extension.xar.packager.conflict.always.nomerge";
58   
59    @Inject
60    private Provider<XWikiContext> xcontextProvider;
61   
62    @Inject
63    private MandatoryDocumentInitializerManager initializerManager;
64   
65    @Inject
66    private Execution execution;
67   
68    @Inject
69    private Logger logger;
70   
71    /**
72    * @param comment the comment to use when/if saving the document
73    * @param previousDocument the previous version of the document
74    * @param currentDocument the current version of the document
75    * @param nextDocument the new version of the document
76    * @param configuration various setup for the import
77    * @return the result of the merge
78    * @throws Exception when failed to save the document
79    */
 
80  379 toggle public XarEntryMergeResult saveDocument(String comment, XWikiDocument previousDocument,
81    XWikiDocument currentDocument, XWikiDocument nextDocument, PackageConfiguration configuration) throws Exception
82    {
83  379 XarEntryMergeResult mergeResult = null;
84   
85    // Merge and save
86  379 if (currentDocument != null && !currentDocument.isNew()) {
87  59 if (previousDocument != null) {
88    // 3 ways merge
89  51 mergeResult = merge(comment, currentDocument, previousDocument, nextDocument, configuration);
90    } else {
91    // Check if a mandatory document initializer exists for the current document
92  8 XWikiDocument mandatoryDocument = getMandatoryDocument(nextDocument.getDocumentReference());
93   
94  8 if (mandatoryDocument != null) {
95    // 3 ways merge
96  1 mergeResult = merge(comment, currentDocument, mandatoryDocument, nextDocument, configuration);
97    } else {
98    // Already existing document in database but without previous version
99  7 if (!currentDocument.equalsData(nextDocument)) {
100  4 XWikiDocument documentToSave;
101  4 if (configuration.isInteractive()) {
102    // Indicate future author to whoever is going to answer the question
103  0 nextDocument.setCreatorReference(currentDocument.getCreatorReference());
104  0 DocumentReference userReference = configuration.getUserReference();
105  0 if (userReference != null) {
106  0 nextDocument.setAuthorReference(userReference);
107  0 nextDocument.setContentAuthorReference(userReference);
108  0 for (XWikiAttachment attachment : nextDocument.getAttachmentList()) {
109  0 attachment.setAuthor(nextDocument.getAuthor());
110    }
111    }
112   
113  0 documentToSave =
114    askDocumentToSave(currentDocument, previousDocument, nextDocument, null, configuration);
115    } else {
116  4 documentToSave = nextDocument;
117    }
118   
119  4 if (documentToSave != currentDocument) {
120  4 saveDocument(documentToSave, comment, false, configuration);
121    }
122    }
123    }
124    }
125  320 } else if (previousDocument == null) {
126  315 saveDocument(nextDocument, comment, true, configuration);
127    }
128   
129  379 return mergeResult;
130    }
131   
 
132  52 toggle private XarEntryMergeResult merge(String comment, XWikiDocument currentDocument, XWikiDocument previousDocument,
133    XWikiDocument nextDocument, PackageConfiguration configuration) throws Exception
134    {
135  52 XWikiContext xcontext = this.xcontextProvider.get();
136   
137    // 3 ways merge
138  52 XWikiDocument mergedDocument = currentDocument.clone();
139   
140  52 MergeConfiguration mergeConfiguration = new MergeConfiguration();
141  52 mergeConfiguration.setProvidedVersionsModifiables(true);
142   
143  52 MergeResult documentMergeResult;
144  52 try {
145  52 documentMergeResult = mergedDocument.merge(previousDocument, nextDocument, mergeConfiguration, xcontext);
146    } catch (Exception e) {
147    // Unexpected error, lets behave as if there was a conflict
148  0 documentMergeResult = new MergeResult();
149  0 documentMergeResult.getLog().error(
150    "Unexpected exception thrown. Usually means there is a bug in the merge.", e);
151  0 documentMergeResult.setModified(true);
152    }
153   
154  52 documentMergeResult.getLog().log(this.logger);
155   
156  52 if (configuration.isInteractive() && !documentMergeResult.getLog().getLogs(LogLevel.ERROR).isEmpty()) {
157    // Indicate future author to whoever is going to answer the question
158  11 nextDocument.setCreatorReference(currentDocument.getCreatorReference());
159  11 mergedDocument.setCreatorReference(currentDocument.getCreatorReference());
160  11 DocumentReference userReference = configuration.getUserReference();
161  11 if (userReference != null) {
162  11 nextDocument.setAuthorReference(userReference);
163  11 nextDocument.setContentAuthorReference(userReference);
164  11 for (XWikiAttachment attachment : nextDocument.getAttachmentList()) {
165  0 attachment.setAuthor(nextDocument.getAuthor());
166    }
167  11 mergedDocument.setAuthorReference(userReference);
168  11 mergedDocument.setContentAuthorReference(userReference);
169  11 for (XWikiAttachment attachment : mergedDocument.getAttachmentList()) {
170  0 if (attachment.isContentDirty()) {
171  0 attachment.setAuthor(mergedDocument.getAuthor());
172    }
173    }
174    }
175   
176  11 XWikiDocument documentToSave =
177    askDocumentToSave(currentDocument, previousDocument, nextDocument, mergedDocument, configuration);
178   
179  11 if (documentToSave != currentDocument) {
180  9 saveDocument(documentToSave, comment, false, configuration);
181    }
182  41 } else if (documentMergeResult.isModified()) {
183  12 saveDocument(mergedDocument, comment, false, configuration);
184    }
185   
186  52 return new XarEntryMergeResult(new XarEntry(new LocalDocumentReference(
187    mergedDocument.getDocumentReferenceWithLocale())), documentMergeResult);
188    }
189   
 
190  8 toggle private XWikiDocument getMandatoryDocument(DocumentReference documentReference)
191    {
192  8 MandatoryDocumentInitializer initializer =
193    this.initializerManager.getMandatoryDocumentInitializer(documentReference);
194   
195  8 XWikiDocument mandatoryDocument;
196  8 if (initializer != null) {
197    // Generate clean mandatory document
198  1 mandatoryDocument = new XWikiDocument(documentReference);
199   
200  1 if (!initializer.updateDocument(mandatoryDocument)) {
201  0 mandatoryDocument = null;
202    }
203    } else {
204  7 mandatoryDocument = null;
205    }
206   
207  8 return mandatoryDocument;
208    }
209   
 
210  11 toggle private GlobalAction getMergeConflictAnswer(XWikiDocument currentDocument, XWikiDocument previousDocument,
211    XWikiDocument nextDocument)
212    {
213  11 return (GlobalAction) this.execution.getContext().getProperty(
214  11 previousDocument != null ? PROP_ALWAYS_MERGE : PROP_ALWAYS_NOMERGE);
215    }
216   
 
217  1 toggle private void setMergeConflictAnswer(XWikiDocument currentDocument, XWikiDocument previousDocument,
218    XWikiDocument nextDocument, GlobalAction action)
219    {
220  1 this.execution.getContext().setProperty(previousDocument != null ? PROP_ALWAYS_MERGE : PROP_ALWAYS_NOMERGE,
221    action);
222    }
223   
 
224  11 toggle private XWikiDocument askDocumentToSave(XWikiDocument currentDocument, XWikiDocument previousDocument,
225    XWikiDocument nextDocument, XWikiDocument mergedDocument, PackageConfiguration configuration)
226    {
227    // Ask what to do
228  11 ConflictQuestion question =
229    new ConflictQuestion(currentDocument, previousDocument, nextDocument, mergedDocument);
230   
231  11 if (mergedDocument == null) {
232  0 question.setGlobalAction(GlobalAction.NEXT);
233    }
234   
235  11 if (configuration != null && configuration.getJobStatus() != null) {
236  11 GlobalAction contextAction = getMergeConflictAnswer(currentDocument, previousDocument, nextDocument);
237  11 if (contextAction != null) {
238  1 question.setGlobalAction(contextAction);
239    } else {
240  10 try {
241  10 configuration.getJobStatus().ask(question);
242  10 if (question.isAlways()) {
243  1 setMergeConflictAnswer(currentDocument, previousDocument, nextDocument,
244    question.getGlobalAction());
245    }
246    } catch (InterruptedException e) {
247    // TODO: log something ?
248    }
249    }
250    }
251   
252  11 XWikiDocument documentToSave;
253   
254  11 switch (question.getGlobalAction()) {
255  2 case CURRENT:
256  2 documentToSave = currentDocument;
257  2 break;
258  3 case NEXT:
259  3 documentToSave = nextDocument;
260  3 break;
261  4 case PREVIOUS:
262  4 documentToSave = previousDocument;
263  4 break;
264  0 case CUSTOM:
265  0 documentToSave = question.getCustomDocument() != null ? question.getCustomDocument() : mergedDocument;
266  0 break;
267  2 default:
268  2 documentToSave = mergedDocument;
269  2 break;
270    }
271   
272  11 return documentToSave;
273    }
274   
 
275  340 toggle private void saveDocument(XWikiDocument document, String comment, boolean setCreator,
276    PackageConfiguration configuration) throws Exception
277    {
278  340 XWikiContext xcontext = this.xcontextProvider.get();
279   
280  340 XWikiDocument currentDocument =
281    xcontext.getWiki().getDocument(document.getDocumentReferenceWithLocale(), xcontext);
282   
283  340 if (!currentDocument.isNew()) {
284  25 if (document != currentDocument) {
285  25 if (document.isNew()) {
286  4 currentDocument.loadAttachmentsContent(xcontext);
287  4 currentDocument.apply(document);
288    } else {
289  21 currentDocument = document;
290    }
291    }
292    } else {
293  315 currentDocument = document;
294    }
295   
296    // Set document authors
297  340 DocumentReference configuredUser = configuration.getUserReference();
298  340 if (configuredUser != null) {
299  323 if (setCreator) {
300  301 currentDocument.setCreatorReference(configuredUser);
301    }
302  323 currentDocument.setAuthorReference(configuredUser);
303  323 currentDocument.setContentAuthorReference(configuredUser);
304   
305    // Set attachments authors
306  323 for (XWikiAttachment attachment : currentDocument.getAttachmentList()) {
307  46 if (attachment.isContentDirty()) {
308  46 attachment.setAuthor(currentDocument.getAuthor());
309    }
310    }
311    } else {
312  17 if (document != currentDocument) {
313  1 if (setCreator) {
314  0 currentDocument.setCreatorReference(document.getCreatorReference());
315    }
316  1 currentDocument.setAuthorReference(document.getAuthorReference());
317  1 currentDocument.setContentAuthorReference(document.getContentAuthorReference());
318   
319    // Set attachments authors
320  1 for (XWikiAttachment attachment : document.getAttachmentList()) {
321  0 if (attachment.isContentDirty()) {
322  0 currentDocument.getAttachment(attachment.getFilename()).setAuthor(attachment.getAuthor());
323    }
324    }
325    }
326   
327    // Make sure to keep the content author we want
328  17 currentDocument.setContentDirty(false);
329  17 currentDocument.setContentUpdateDate(new Date());
330    }
331   
332  340 saveDocumentSetContextUser(currentDocument, comment);
333    }
334   
 
335  340 toggle private void saveDocumentSetContextUser(XWikiDocument document, String comment) throws Exception
336    {
337  340 XWikiContext xcontext = this.xcontextProvider.get();
338   
339  340 DocumentReference userReference = xcontext.getUserReference();
340   
341  340 try {
342    // Make sure to have context user corresponding to document author for badly designed listeners expecting
343    // the document to actually be saved by context user
344  340 xcontext.setUserReference(document.getAuthorReference());
345   
346  340 xcontext.getWiki().saveDocument(document, comment, false, xcontext);
347    } finally {
348  340 xcontext.setUserReference(userReference);
349    }
350    }
351    }