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

File RefactoringScriptService.java

 

Coverage histogram

../../../../img/srcFileCovDistChart10.png
0% of files have more coverage

Code metrics

8
76
38
1
600
246
43
0.57
2
38
1.13

Classes

Class Line # Actions
RefactoringScriptService 62 76 0% 43 6
0.950819795.1%
 

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.refactoring.script;
21   
22    import java.util.Arrays;
23    import java.util.Collection;
24    import java.util.Collections;
25    import java.util.Date;
26    import java.util.List;
27    import java.util.concurrent.ThreadLocalRandom;
28   
29    import javax.inject.Inject;
30    import javax.inject.Named;
31    import javax.inject.Singleton;
32   
33    import org.apache.commons.lang3.StringUtils;
34    import org.xwiki.bridge.DocumentAccessBridge;
35    import org.xwiki.component.annotation.Component;
36    import org.xwiki.context.Execution;
37    import org.xwiki.job.Job;
38    import org.xwiki.job.JobException;
39    import org.xwiki.job.JobExecutor;
40    import org.xwiki.model.EntityType;
41    import org.xwiki.model.reference.DocumentReference;
42    import org.xwiki.model.reference.EntityReference;
43    import org.xwiki.model.reference.EntityReferenceProvider;
44    import org.xwiki.model.reference.SpaceReference;
45    import org.xwiki.refactoring.job.CreateRequest;
46    import org.xwiki.refactoring.job.EntityRequest;
47    import org.xwiki.refactoring.job.MoveRequest;
48    import org.xwiki.refactoring.job.RefactoringJobs;
49    import org.xwiki.script.service.ScriptService;
50    import org.xwiki.security.authorization.ContextualAuthorizationManager;
51    import org.xwiki.security.authorization.Right;
52   
53    /**
54    * Provides refactoring-specific scripting APIs.
55    *
56    * @version $Id: 0c77daf35434ff776bb28eb32c6b0d7a10192fee $
57    * @since 7.2M1
58    */
59    @Component
60    @Named(RefactoringJobs.GROUP)
61    @Singleton
 
62    public class RefactoringScriptService implements ScriptService
63    {
64    /**
65    * The key under which the last encountered error is stored in the current execution context.
66    */
67    private static final String REFACTORING_ERROR_KEY = String.format("scriptservice.%s.error", RefactoringJobs.GROUP);
68   
69    /**
70    * Provides access to the current context.
71    */
72    @Inject
73    private Execution execution;
74   
75    /**
76    * Used to execute refactoring jobs.
77    */
78    @Inject
79    private JobExecutor jobExecutor;
80   
81    /**
82    * Used to check user rights.
83    */
84    @Inject
85    private ContextualAuthorizationManager authorization;
86   
87    /**
88    * Needed for getting the current user reference.
89    */
90    @Inject
91    private DocumentAccessBridge documentAccessBridge;
92   
93    @Inject
94    private EntityReferenceProvider defaultEntityReferenceProvider;
95   
96    /**
97    * Creates a request to move the specified source entities to the specified destination entity (which becomes their
98    * new parent).
99    *
100    * @param sources specifies the entities to be moved
101    * @param destination specifies the place where to move the entities (their new parent entity)
102    * @return the move request
103    */
 
104  1 toggle public MoveRequest createMoveRequest(Collection<EntityReference> sources, EntityReference destination)
105    {
106  1 return createMoveRequest(RefactoringJobs.MOVE, sources, destination);
107    }
108   
109    /**
110    * Creates a request to move the specified source entity to the specified destination entity (which becomes its new
111    * parent).
112    *
113    * @param source specifies the entity to be moved
114    * @param destination specifies the place where to move the source entity (its new parent entity)
115    * @return the move request
116    */
 
117  1 toggle public MoveRequest createMoveRequest(EntityReference source, EntityReference destination)
118    {
119  1 return createMoveRequest(Arrays.asList(source), destination);
120    }
121   
122    /**
123    * Creates a request to rename the entity specified by the given old reference.
124    *
125    * @param oldReference the entity to rename
126    * @param newReference the new entity reference after the rename
127    * @return the rename request
128    */
 
129  5 toggle public MoveRequest createRenameRequest(EntityReference oldReference, EntityReference newReference)
130    {
131  5 return createMoveRequest(RefactoringJobs.RENAME, Collections.singletonList(oldReference), newReference);
132    }
133   
134    /**
135    * Creates a request to rename the specified entity.
136    *
137    * @param reference the entity to rename
138    * @param newName the new entity name
139    * @return the rename request
140    */
 
141  1 toggle public MoveRequest createRenameRequest(EntityReference reference, String newName)
142    {
143  1 return createRenameRequest(reference, new EntityReference(newName, reference.getType(), reference.getParent()));
144    }
145   
146    /**
147    * Creates a request to copy the specified source entities to the specified destination entity.
148    *
149    * @param sources specifies the entities to be copied
150    * @param destination specifies the place where to copy the entities (becomes the parent of the copies)
151    * @return the copy request
152    */
 
153  1 toggle public MoveRequest createCopyRequest(Collection<EntityReference> sources, EntityReference destination)
154    {
155  1 MoveRequest request = createMoveRequest(RefactoringJobs.COPY, sources, destination);
156  1 request.setDeleteSource(false);
157  1 request.setUpdateParentField(false);
158  1 return request;
159    }
160   
161    /**
162    * Creates a request to copy the specified source entity to the specified destination entity.
163    *
164    * @param source specifies the entity to be copied
165    * @param destination specifies the place where to copy the source entity (becomes the parent of the copy)
166    * @return the copy request
167    */
 
168  1 toggle public MoveRequest createCopyRequest(EntityReference source, EntityReference destination)
169    {
170  1 return createCopyRequest(Arrays.asList(source), destination);
171    }
172   
173    /**
174    * Creates a request to copy the specified entity with a different reference.
175    *
176    * @param sourceReference the entity to copy
177    * @param copyReference the reference to use for the copy
178    * @return the copy-as request
179    */
 
180  1 toggle public MoveRequest createCopyAsRequest(EntityReference sourceReference, EntityReference copyReference)
181    {
182  1 MoveRequest request = createMoveRequest(RefactoringJobs.COPY_AS, Arrays.asList(sourceReference), copyReference);
183  1 request.setDeleteSource(false);
184  1 request.setUpdateParentField(false);
185  1 return request;
186    }
187   
188    /**
189    * Creates a request to copy the specified entity with a different name.
190    *
191    * @param reference the entity to copy
192    * @param copyName the name of the entity copy
193    * @return the copy-as request
194    */
 
195  1 toggle public MoveRequest createCopyAsRequest(EntityReference reference, String copyName)
196    {
197  1 EntityReference copyReference = new EntityReference(copyName, reference.getType(), reference.getParent());
198  1 return createCopyAsRequest(reference, copyReference);
199    }
200   
201    /**
202    * Creates a request to delete the specified entities.
203    *
204    * @param entityReferences the entities to delete
205    * @return the delete request
206    */
 
207  21 toggle public EntityRequest createDeleteRequest(Collection<EntityReference> entityReferences)
208    {
209  21 EntityRequest request = new EntityRequest();
210  21 initEntityRequest(request, RefactoringJobs.DELETE, entityReferences);
211  21 return request;
212    }
213   
214    /**
215    * Creates a request to create the specified entities.
216    *
217    * @param entityReferences the entities to create
218    * @return the create request
219    * @since 7.4M2
220    */
 
221  18 toggle public CreateRequest createCreateRequest(Collection<EntityReference> entityReferences)
222    {
223  18 CreateRequest request = new CreateRequest();
224  18 initEntityRequest(request, RefactoringJobs.CREATE, entityReferences);
225    // Set deep create by default, to copy (if possible) any existing hierarchy of a specified template document.
226    // TODO: expose this in the create UI to advanced users to allow them to opt-out?
227  18 request.setDeep(true);
228  18 return request;
229    }
230   
 
231  47 toggle private void initEntityRequest(EntityRequest request, String type, Collection<EntityReference> entityReferences)
232    {
233  47 request.setId(generateJobId(type));
234  47 request.setJobType(type);
235  47 request.setEntityReferences(entityReferences);
236  47 setRightsProperties(request);
237    }
238   
 
239  8 toggle private MoveRequest createMoveRequest(String type, Collection<EntityReference> sources, EntityReference destination)
240    {
241  8 MoveRequest request = new MoveRequest();
242  8 initEntityRequest(request, type, sources);
243  8 request.setDestination(destination);
244  8 request.setUpdateLinks(true);
245  8 request.setAutoRedirect(true);
246  8 request.setUpdateParentField(true);
247  8 return request;
248    }
249   
250    /**
251    * @param type the type of refactoring
252    * @return an id for a job to perform the specified type of refactoring
253    */
 
254  47 toggle private List<String> generateJobId(String type)
255    {
256  47 String suffix = new Date().getTime() + "-" + ThreadLocalRandom.current().nextInt(100, 1000);
257  47 return getJobId(type, suffix);
258    }
259   
260    /**
261    * @param type the type of refactoring
262    * @param suffix uniquely identifies the job among those of the specified type
263    * @return an id for a job to perform the specified type of refactoring
264    */
 
265  47 toggle private List<String> getJobId(String type, String suffix)
266    {
267  47 return Arrays
268    .asList(RefactoringJobs.GROUP, StringUtils.removeStart(type, RefactoringJobs.GROUP_PREFIX), suffix);
269    }
270   
271    /**
272    * Schedules an asynchronous job to perform the given move request.
273    *
274    * @param request the move request to perform
275    * @return the job that has been scheduled and that can be used to monitor the progress of the operation,
276    * {@code null} in case of failure
277    */
 
278  4 toggle public Job move(MoveRequest request)
279    {
280  4 return execute(RefactoringJobs.MOVE, request);
281    }
282   
283    /**
284    * Schedules an asynchronous job to move the specified source entities to the specified destination entity (which
285    * becomes their new parent).
286    *
287    * @param sources specifies the entities to be moved
288    * @param destination specifies the place where to move the entities (their new parent entity)
289    * @return the job that has been scheduled and that can be used to monitor the progress of the operation,
290    * {@code null} in case of failure
291    */
 
292  0 toggle public Job move(Collection<EntityReference> sources, EntityReference destination)
293    {
294  0 return move(createMoveRequest(sources, destination));
295    }
296   
297    /**
298    * Schedules an asynchronous job to move the specified source entity to the specified destination entity (which
299    * becomes its new parent).
300    *
301    * @param source specifies the entity to be moved
302    * @param destination specifies the place where to move the entity (its new parent entity)
303    * @return the job that has been scheduled and that can be used to monitor the progress of the operation,
304    * {@code null} in case of failure
305    */
 
306  1 toggle public Job move(EntityReference source, EntityReference destination)
307    {
308  1 return move(createMoveRequest(source, destination));
309    }
310   
311    /**
312    * Schedules an asynchronous job to perform the given rename request.
313    *
314    * @param request the rename request to perform
315    * @return the job that has been scheduled and that can be used to monitor the progress of the operation,
316    * {@code null} in case of failure
317    */
 
318  5 toggle public Job rename(MoveRequest request)
319    {
320  5 return execute(RefactoringJobs.RENAME, request);
321    }
322   
323    /**
324    * Schedules an asynchronous job to rename the specified entity.
325    *
326    * @param oldReference the entity to rename
327    * @param newReference the new entity reference after the rename
328    * @return the job that has been scheduled and that can be used to monitor the progress of the operation,
329    * {@code null} in case of failure
330    */
 
331  2 toggle public Job rename(EntityReference oldReference, EntityReference newReference)
332    {
333  2 return rename(createRenameRequest(oldReference, newReference));
334    }
335   
336    /**
337    * Schedules an asynchronous job to rename the specified entity.
338    *
339    * @param reference the entity to rename
340    * @param newName the new entity name
341    * @return the job that has been scheduled and that can be used to monitor the progress of the operation,
342    * {@code null} in case of failure
343    */
 
344  1 toggle public Job rename(EntityReference reference, String newName)
345    {
346  1 return rename(createRenameRequest(reference, newName));
347    }
348   
349    /**
350    * Schedules an asynchronous job to convert the specified terminal document to a nested document (that can have
351    * child documents). E.g. the document {@code Space1.Space2.Name} is converted to {@code Space1.Space2.Name.WebHome}
352    * .
353    *
354    * @param documentReference the terminal document to convert to a nested document (that can have child documents)
355    * @return the job that has been scheduled and that can be used to monitor the progress of the operation,
356    * {@code null} in case of failure
357    */
 
358  2 toggle public Job convertToNestedDocument(DocumentReference documentReference)
359    {
360  2 String defaultDocName = this.defaultEntityReferenceProvider.getDefaultReference(EntityType.DOCUMENT).getName();
361  2 if (!documentReference.getName().equals(defaultDocName)) {
362  1 SpaceReference spaceReference =
363    new SpaceReference(documentReference.getName(), documentReference.getParent());
364  1 return rename(documentReference, new DocumentReference(defaultDocName, spaceReference));
365    }
366    // The specified document is already a nested document.
367  1 return null;
368    }
369   
370    /**
371    * Schedules an asynchronous job to convert the specified nested document to a terminal document (that can't have
372    * child documents). E.g. the document {@code One.Two.WebHome} is converted to {@code One.Two} .
373    *
374    * @param documentReference the nested document to convert to a terminal document (that can't have child documents)
375    * @return the job that has been scheduled and that can be used to monitor the progress of the operation,
376    * {@code null} in case of failure
377    */
 
378  3 toggle public Job convertToTerminalDocument(DocumentReference documentReference)
379    {
380  3 if (documentReference.getName().equals(
381    this.defaultEntityReferenceProvider.getDefaultReference(documentReference.getType()).getName())) {
382  2 EntityReference parentReference = documentReference.getParent();
383  2 if (parentReference.getParent().getType() == EntityType.SPACE) {
384    // There has to be at least 2 levels of nested spaces in order to be able to convert a nested document
385    // into a terminal document. We cannot convert a root document like Main.WebHome into a terminal
386    // document.
387  1 DocumentReference terminalDocumentReference =
388    new DocumentReference(parentReference.getName(), new SpaceReference(parentReference.getParent()));
389  1 return rename(documentReference, terminalDocumentReference);
390    }
391    }
392    // The specified document is already a terminal document or cannot be converted to a terminal document.
393  2 return null;
394    }
395   
396    /**
397    * Schedules an asynchronous job to perform the given copy request.
398    *
399    * @param request the copy request to perform
400    * @return the job that has been scheduled and that can be used to monitor the progress of the operation,
401    * {@code null} in case of failure
402    */
 
403  1 toggle public Job copy(MoveRequest request)
404    {
405    // The MOVE job can perform a COPY too.
406  1 return execute(RefactoringJobs.MOVE, request);
407    }
408   
409    /**
410    * Schedules an asynchronous job to copy the specified source entities to the specified destination entity.
411    *
412    * @param sources specifies the entities to be copied
413    * @param destination specifies the place where to copy the entities
414    * @return the job that has been scheduled and that can be used to monitor the progress of the operation,
415    * {@code null} in case of failure
416    */
 
417  0 toggle public Job copy(Collection<EntityReference> sources, EntityReference destination)
418    {
419  0 return copy(createCopyRequest(sources, destination));
420    }
421   
422    /**
423    * Schedules an asynchronous job to copy the specified source entity to the specified destination entity.
424    *
425    * @param source specifies the entity to be copied
426    * @param destination specifies the place where to copy the entity
427    * @return the job that has been scheduled and that can be used to monitor the progress of the operation,
428    * {@code null} in case of failure
429    */
 
430  1 toggle public Job copy(EntityReference source, EntityReference destination)
431    {
432  1 return copy(createCopyRequest(source, destination));
433    }
434   
435    /**
436    * Schedules an asynchronous job to perform the given copy-as request.
437    *
438    * @param request the copy-as request to perform
439    * @return the job that has been scheduled and that can be used to monitor the progress of the operation,
440    * {@code null} in case of failure
441    */
 
442  1 toggle public Job copyAs(MoveRequest request)
443    {
444    // The RENAME job can perform a COPY too.
445  1 return execute(RefactoringJobs.RENAME, request);
446    }
447   
448    /**
449    * Schedules an asynchronous job to copy the specified entity with a different reference.
450    *
451    * @param sourceReference the entity to copy
452    * @param copyReference the reference to use for the copy
453    * @return the job that has been scheduled and that can be used to monitor the progress of the operation,
454    * {@code null} in case of failure
455    */
 
456  0 toggle public Job copyAs(EntityReference sourceReference, EntityReference copyReference)
457    {
458  0 return copyAs(createCopyAsRequest(sourceReference, copyReference));
459    }
460   
461    /**
462    * Schedules an asynchronous job to copy the specified entity with a different name.
463    *
464    * @param reference the entity to copy
465    * @param copyName the name to use for the copy
466    * @return the job that has been scheduled and that can be used to monitor the progress of the operation,
467    * {@code null} in case of failure
468    */
 
469  1 toggle public Job copyAs(EntityReference reference, String copyName)
470    {
471  1 return copyAs(createCopyAsRequest(reference, copyName));
472    }
473   
474    /**
475    * Schedules an asynchronous job to perform the given delete request.
476    *
477    * @param request the delete request to perform
478    * @return the job that has been scheduled and that can be used to monitor the progress of the operation,
479    * {@code null} in case of failure
480    */
 
481  21 toggle public Job delete(EntityRequest request)
482    {
483  21 return execute(RefactoringJobs.DELETE, request);
484    }
485   
486    /**
487    * Schedules an asynchronous job to delete the specified entities.
488    *
489    * @param entityReferences the entities to delete
490    * @return the job that has been scheduled and that can be used to monitor the progress of the operation,
491    * {@code null} in case of failure
492    */
 
493  21 toggle public Job delete(Collection<EntityReference> entityReferences)
494    {
495  21 return delete(createDeleteRequest(entityReferences));
496    }
497   
498    /**
499    * Schedules an asynchronous job to delete the specified entity.
500    *
501    * @param entityReference the entity to delete
502    * @return the job that has been scheduled and that can be used to monitor the progress of the operation,
503    * {@code null} in case of failure
504    */
 
505  21 toggle public Job delete(EntityReference entityReference)
506    {
507  21 return delete(Arrays.asList(entityReference));
508    }
509   
510    /**
511    * Schedules an asynchronous job to perform the given create request.
512    *
513    * @param request the create request to perform
514    * @return the job that has been scheduled and that can be used to monitor the progress of the operation,
515    * {@code null} in case of failure
516    * @since 7.4M2
517    */
 
518  18 toggle public Job create(CreateRequest request)
519    {
520  18 return execute(RefactoringJobs.CREATE, request);
521    }
522   
523    /**
524    * Schedules an asynchronous job to create the specified entities.
525    *
526    * @param entityReferences the entities to create
527    * @return the job that has been scheduled and that can be used to monitor the progress of the operation,
528    * {@code null} in case of failure
529    * @since 7.4M2
530    */
 
531  1 toggle public Job create(Collection<EntityReference> entityReferences)
532    {
533  1 return create(createCreateRequest(entityReferences));
534    }
535   
536    /**
537    * Schedules an asynchronous job to create the specified entity.
538    *
539    * @param entityReference the entity to create
540    * @return the job that has been scheduled and that can be used to monitor the progress of the operation,
541    * {@code null} in case of failure
542    * @since 7.4M2
543    */
 
544  1 toggle public Job create(EntityReference entityReference)
545    {
546  1 return create(Arrays.asList(entityReference));
547    }
548   
549    /**
550    * Executes a refactoring request.
551    *
552    * @param type the type of refactoring to execute
553    * @param request the refactoring request to execute
554    * @return the job that has been scheduled and that can be used to monitor the progress of the operation,
555    * {@code null} in case of failure
556    */
 
557  50 toggle private Job execute(String type, EntityRequest request)
558    {
559  50 setError(null);
560   
561    // Make sure that only the PR users can change the rights and context properties from the request.
562  50 if (!this.authorization.hasAccess(Right.PROGRAM)) {
563  36 setRightsProperties(request);
564    }
565   
566  50 try {
567  50 return this.jobExecutor.execute(type, request);
568    } catch (JobException e) {
569  1 setError(e);
570  1 return null;
571    }
572    }
573   
 
574  83 toggle private <T extends EntityRequest> void setRightsProperties(T request)
575    {
576  83 request.setCheckRights(true);
577  83 request.setUserReference(this.documentAccessBridge.getCurrentUserReference());
578    }
579   
580    /**
581    * Get the error generated while performing the previously called action.
582    *
583    * @return an eventual exception or {@code null} if no exception was thrown
584    */
 
585  1 toggle public Exception getLastError()
586    {
587  1 return (Exception) this.execution.getContext().getProperty(REFACTORING_ERROR_KEY);
588    }
589   
590    /**
591    * Store a caught exception in the context, so that it can be later retrieved using {@link #getLastError()}.
592    *
593    * @param e the exception to store, can be {@code null} to clear the previously stored exception
594    * @see #getLastError()
595    */
 
596  51 toggle private void setError(Exception e)
597    {
598  51 this.execution.getContext().setProperty(REFACTORING_ERROR_KEY, e);
599    }
600    }