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

File MoveJob.java

 

Coverage histogram

../../../../../img/srcFileCovDistChart9.png
38% of files have more coverage

Code metrics

46
100
16
1
300
217
53
0.53
6.25
16
3.31

Classes

Class Line # Actions
MoveJob 48 100 0% 53 24
0.851851985.2%
 

Contributing tests

This file is covered by 17 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.refactoring.internal.job;
21   
22    import java.util.LinkedList;
23    import java.util.List;
24   
25    import javax.inject.Inject;
26    import javax.inject.Named;
27   
28    import org.xwiki.component.annotation.Component;
29    import org.xwiki.model.EntityType;
30    import org.xwiki.model.reference.DocumentReference;
31    import org.xwiki.model.reference.EntityReference;
32    import org.xwiki.model.reference.SpaceReference;
33    import org.xwiki.refactoring.internal.LinkRefactoring;
34    import org.xwiki.refactoring.job.EntityJobStatus;
35    import org.xwiki.refactoring.job.MoveRequest;
36    import org.xwiki.refactoring.job.OverwriteQuestion;
37    import org.xwiki.refactoring.job.RefactoringJobs;
38    import org.xwiki.security.authorization.Right;
39   
40    /**
41    * A job that can move entities to a new parent within the hierarchy.
42    *
43    * @version $Id: 2f10d832ac3d7847428877bd6e2f4f0c35460266 $
44    * @since 7.2M1
45    */
46    @Component
47    @Named(RefactoringJobs.MOVE)
 
48    public class MoveJob extends AbstractEntityJob<MoveRequest, EntityJobStatus<MoveRequest>>
49    {
50    /**
51    * Specifies whether all entities with the same name are to be overwritten on not. When {@code true} all entities
52    * with the same name are overwritten. When {@code false} all entities with the same name are skipped. If
53    * {@code null} then a question is asked for each entity.
54    */
55    private Boolean overwriteAll;
56   
57    /**
58    * The component used to refactor document links after a document is rename or moved.
59    */
60    @Inject
61    private LinkRefactoring linkRefactoring;
62   
 
63  65 toggle @Override
64    public String getType()
65    {
66  65 return RefactoringJobs.MOVE;
67    }
68   
 
69  21 toggle @Override
70    protected EntityJobStatus<MoveRequest> createNewStatus(MoveRequest request)
71    {
72  21 return new EntityJobStatus<MoveRequest>(request, this.observationManager, this.loggerManager, null);
73    }
74   
 
75  21 toggle @Override
76    protected void runInternal() throws Exception
77    {
78  21 if (this.request.getDestination() != null) {
79  21 super.runInternal();
80    }
81    }
82   
 
83  19 toggle @Override
84    protected void process(EntityReference source)
85    {
86    // Perform generic checks that don't depend on the source/destination type.
87   
88  19 EntityReference destination = this.request.getDestination();
89  19 if (isDescendantOrSelf(destination, source)) {
90  1 this.logger.error("Cannot make [{}] a descendant of itself.", source);
91  1 return;
92    }
93   
94    // Dispatch the move operation based on the source entity type.
95   
96  18 switch (source.getType()) {
97  14 case DOCUMENT:
98  14 process(new DocumentReference(source), destination);
99  13 break;
100  3 case SPACE:
101  3 process(new SpaceReference(source), destination);
102  2 break;
103  1 default:
104  1 this.logger.error("Unsupported source entity type [{}].", source.getType());
105    }
106    }
107   
 
108  19 toggle private boolean isDescendantOrSelf(EntityReference alice, EntityReference bob)
109    {
110  19 EntityReference parent = alice;
111  64 while (parent != null && !parent.equals(bob)) {
112  45 parent = parent.getParent();
113    }
114  19 return parent != null;
115    }
116   
 
117  9 toggle protected void process(DocumentReference source, EntityReference destination)
118    {
119  9 if (this.request.isDeep() && isSpaceHomeReference(source)) {
120  1 process(source.getLastSpaceReference(), destination);
121  8 } else if (destination.getType() == EntityType.SPACE) {
122  5 maybeMove(source, new DocumentReference(source.getName(), new SpaceReference(destination)));
123  3 } else if (destination.getType() == EntityType.DOCUMENT
124    && isSpaceHomeReference(new DocumentReference(destination))) {
125  1 maybeMove(source, new DocumentReference(source.getName(), new SpaceReference(destination.getParent())));
126    } else {
127  2 this.logger.error("Unsupported destination entity type [{}] for a document.", destination.getType());
128    }
129    }
130   
 
131  3 toggle protected void process(SpaceReference source, EntityReference destination)
132    {
133  3 if (destination.getType() == EntityType.SPACE || destination.getType() == EntityType.WIKI) {
134  1 process(source, new SpaceReference(source.getName(), destination));
135  2 } else if (destination.getType() == EntityType.DOCUMENT
136    && isSpaceHomeReference(new DocumentReference(destination))) {
137  1 process(source, new SpaceReference(source.getName(), destination.getParent()));
138    } else {
139  1 this.logger.error("Unsupported destination entity type [{}] for a space.", destination.getType());
140    }
141    }
142   
 
143  5 toggle protected void process(final SpaceReference source, final SpaceReference destination)
144    {
145  5 visitDocuments(source, new Visitor<DocumentReference>()
146    {
 
147  4 toggle @Override
148    public void visit(DocumentReference oldChildReference)
149    {
150  4 DocumentReference newChildReference = oldChildReference.replaceParent(source, destination);
151  4 maybeMove(oldChildReference, newChildReference);
152    }
153    });
154    }
155   
 
156  12 toggle protected void maybeMove(DocumentReference oldReference, DocumentReference newReference)
157    {
158    // Perform checks that are specific to the document source/destination type.
159   
160  12 if (!this.modelBridge.exists(oldReference)) {
161  1 this.logger.warn("Skipping [{}] because it doesn't exist.", oldReference);
162  11 } else if (this.request.isDeleteSource() && !hasAccess(Right.DELETE, oldReference)) {
163    // The move operation is implemented as Copy + Delete.
164  1 this.logger.error("You are not allowed to delete [{}].", oldReference);
165  10 } else if (!hasAccess(Right.VIEW, newReference) || !hasAccess(Right.EDIT, newReference)
166    || (this.modelBridge.exists(newReference) && !hasAccess(Right.DELETE, newReference))) {
167  1 this.logger
168    .error("You don't have sufficient permissions over the destination document [{}].", newReference);
169    } else {
170  9 move(oldReference, newReference);
171    }
172    }
173   
 
174  9 toggle private void move(DocumentReference oldReference, DocumentReference newReference)
175    {
176  9 this.progressManager.pushLevelProgress(7, this);
177   
178  9 try {
179    // Step 1: Delete the destination document if needed.
180  9 this.progressManager.startStep(this);
181  9 if (this.modelBridge.exists(newReference)) {
182  1 if (this.request.isInteractive() && !confirmOverwrite(oldReference, newReference)) {
183  0 this.logger.warn(
184    "Skipping [{}] because [{}] already exists and the user doesn't want to overwrite it.",
185    oldReference, newReference);
186  0 return;
187  1 } else if (!this.modelBridge.delete(newReference)) {
188  0 return;
189    }
190    }
191   
192    // Step 2: Copy the source document to the destination.
193  9 this.progressManager.startStep(this);
194  9 if (!this.modelBridge.copy(oldReference, newReference)) {
195  3 return;
196    }
197   
198    // Step 3: Update the destination document based on the source document parameters.
199  6 this.progressManager.startStep(this);
200  6 this.modelBridge.update(newReference, this.request.getEntityParameters(oldReference));
201   
202    // Step 4 + 5: Update other documents that might be affected by this move.
203  6 updateDocuments(oldReference, newReference);
204   
205    // Step 6: Delete the source document.
206  6 this.progressManager.startStep(this);
207  6 if (this.request.isDeleteSource()) {
208  5 this.modelBridge.delete(oldReference);
209    }
210   
211    // Step 7: Create an automatic redirect.
212  6 this.progressManager.startStep(this);
213  6 if (this.request.isDeleteSource() && this.request.isAutoRedirect()) {
214  4 this.modelBridge.createRedirect(oldReference, newReference);
215    }
216    } finally {
217  9 this.progressManager.popLevelProgress(this);
218    }
219    }
220   
 
221  6 toggle private void updateDocuments(DocumentReference oldReference, DocumentReference newReference)
222    {
223    // Step 3: Update the links.
224  6 this.progressManager.startStep(this);
225  6 if (this.request.isUpdateLinks()) {
226  6 updateLinks(oldReference, newReference);
227    }
228   
229    // Step 4: (legacy) Preserve existing parent-child relationships by updating the parent field of documents
230    // having the moved document as parent.
231  6 this.progressManager.startStep(this);
232  6 if (this.request.isUpdateParentField()) {
233  6 this.modelBridge.updateParentField(oldReference, newReference);
234    }
235    }
236   
 
237  0 toggle private boolean confirmOverwrite(EntityReference source, EntityReference destination)
238    {
239  0 if (this.overwriteAll == null) {
240  0 OverwriteQuestion question = new OverwriteQuestion(source, destination);
241  0 try {
242  0 this.status.ask(question);
243  0 if (!question.isAskAgain()) {
244    // Use the same answer for the following overwrite questions.
245  0 this.overwriteAll = question.isOverwrite();
246    }
247  0 return question.isOverwrite();
248    } catch (InterruptedException e) {
249  0 this.logger.warn("Overwrite question has been interrupted.");
250  0 return false;
251    }
252    } else {
253  0 return this.overwriteAll;
254    }
255    }
256   
 
257  6 toggle private void updateLinks(DocumentReference oldReference, DocumentReference newReference)
258    {
259  6 this.progressManager.pushLevelProgress(2, this);
260   
261  6 try {
262    // Step 1: Update the links that target the old reference to point to the new reference.
263  6 this.progressManager.startStep(this);
264  6 if (this.request.isDeleteSource()) {
265  5 updateBackLinks(oldReference, newReference);
266    }
267   
268    // Step 2: Update the relative links from the document content.
269  6 this.progressManager.startStep(this);
270  6 this.linkRefactoring.updateRelativeLinks(oldReference, newReference);
271    } finally {
272  6 this.progressManager.popLevelProgress(this);
273    }
274    }
275   
 
276  5 toggle private void updateBackLinks(DocumentReference oldReference, DocumentReference newReference)
277    {
278  5 List<DocumentReference> backlinkDocumentReferences = this.modelBridge.getBackLinkedReferences(oldReference);
279  5 this.progressManager.pushLevelProgress(backlinkDocumentReferences.size(), this);
280   
281  5 try {
282  5 for (DocumentReference backlinkDocumentReference : backlinkDocumentReferences) {
283  2 this.progressManager.startStep(this);
284  2 if (hasAccess(Right.EDIT, backlinkDocumentReference)) {
285  2 this.linkRefactoring.renameLinks(backlinkDocumentReference, oldReference, newReference);
286    }
287    }
288    } finally {
289  5 this.progressManager.popLevelProgress(this);
290    }
291    }
292   
 
293  21 toggle @Override
294    protected String getTargetWiki()
295    {
296  21 List<EntityReference> entityReferences = new LinkedList<>(this.request.getEntityReferences());
297  21 entityReferences.add(this.request.getDestination());
298  21 return getTargetWiki(entityReferences);
299    }
300    }