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

File DefaultDocumentSplitter.java

 

Coverage histogram

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

Code metrics

20
53
7
1
219
134
24
0.45
7.57
7
3.43

Classes

Class Line # Actions
DefaultDocumentSplitter 61 53 0% 24 8
0.990%
 

Contributing tests

This file is covered by 3 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.splitter;
21   
22    import java.util.ArrayList;
23    import java.util.HashMap;
24    import java.util.List;
25    import java.util.ListIterator;
26    import java.util.Map;
27   
28    import javax.inject.Singleton;
29   
30    import org.apache.commons.lang3.StringUtils;
31    import org.xwiki.component.annotation.Component;
32    import org.xwiki.refactoring.WikiDocument;
33    import org.xwiki.refactoring.splitter.DocumentSplitter;
34    import org.xwiki.refactoring.splitter.criterion.SplittingCriterion;
35    import org.xwiki.refactoring.splitter.criterion.naming.NamingCriterion;
36    import org.xwiki.rendering.block.Block;
37    import org.xwiki.rendering.block.Block.Axes;
38    import org.xwiki.rendering.block.BlockFilter;
39    import org.xwiki.rendering.block.HeaderBlock;
40    import org.xwiki.rendering.block.IdBlock;
41    import org.xwiki.rendering.block.LinkBlock;
42    import org.xwiki.rendering.block.NewLineBlock;
43    import org.xwiki.rendering.block.SectionBlock;
44    import org.xwiki.rendering.block.SpaceBlock;
45    import org.xwiki.rendering.block.SpecialSymbolBlock;
46    import org.xwiki.rendering.block.WordBlock;
47    import org.xwiki.rendering.block.XDOM;
48    import org.xwiki.rendering.block.match.ClassBlockMatcher;
49    import org.xwiki.rendering.listener.reference.DocumentResourceReference;
50    import org.xwiki.rendering.listener.reference.ResourceReference;
51    import org.xwiki.rendering.listener.reference.ResourceType;
52   
53    /**
54    * Default implementation of {@link DocumentSplitter}.
55    *
56    * @version $Id: fc0fdd10dbe96c433b09736622c62de752a4bce5 $
57    * @since 1.9M1
58    */
59    @Component
60    @Singleton
 
61    public class DefaultDocumentSplitter implements DocumentSplitter
62    {
63    /**
64    * The name of the anchor link parameter.
65    */
66    private static final String ANCHOR_PARAMETER = "anchor";
67   
 
68  3 toggle @Override
69    public List<WikiDocument> split(WikiDocument rootDoc, SplittingCriterion splittingCriterion,
70    NamingCriterion namingCriterion)
71    {
72  3 List<WikiDocument> result = new ArrayList<WikiDocument>();
73    // Add the rootDoc into the result
74  3 result.add(rootDoc);
75    // Recursively split the root document.
76  3 split(rootDoc, rootDoc.getXdom().getChildren(), 1, result, splittingCriterion, namingCriterion);
77  3 updateAnchors(result);
78  3 return result;
79    }
80   
81    /**
82    * A recursive method for traversing the xdom of the root document and splitting it into sub documents.
83    *
84    * @param parentDoc the parent {@link WikiDocument} under which the given list of children reside.
85    * @param children current list of blocks being traversed.
86    * @param depth the depth from the root xdom to current list of children.
87    * @param result space for storing the resulting documents.
88    * @param splittingCriterion the {@link SplittingCriterion}.
89    * @param namingCriterion the {@link NamingCriterion}.
90    */
 
91  8 toggle private void split(WikiDocument parentDoc, List<Block> children, int depth, List<WikiDocument> result,
92    SplittingCriterion splittingCriterion, NamingCriterion namingCriterion)
93    {
94  8 ListIterator<Block> it = children.listIterator();
95  27 while (it.hasNext()) {
96  19 Block block = it.next();
97  19 if (splittingCriterion.shouldSplit(block, depth)) {
98    // Split a new document and add it to the results list.
99  8 XDOM xdom = new XDOM(block.getChildren());
100  8 String newDocumentName = namingCriterion.getDocumentName(xdom);
101  8 WikiDocument newDoc = new WikiDocument(newDocumentName, xdom, parentDoc);
102  8 result.add(newDoc);
103    // Remove the original block from the parent document.
104  8 it.remove();
105    // Place a link from the parent to child.
106  8 it.add(new NewLineBlock());
107  8 it.add(createLink(block, newDocumentName));
108    // Check whether this node should be further traversed.
109  8 if (splittingCriterion.shouldIterate(block, depth)) {
110  5 split(newDoc, newDoc.getXdom().getChildren(), depth + 1, result, splittingCriterion,
111    namingCriterion);
112    }
113  11 } else if (splittingCriterion.shouldIterate(block, depth)) {
114  0 split(parentDoc, block.getChildren(), depth + 1, result, splittingCriterion, namingCriterion);
115    }
116    }
117    }
118   
119    /**
120    * Creates a {@link LinkBlock} suitable to be placed in the parent document.
121    *
122    * @param block the {@link Block} that has just been split into a separate document.
123    * @param target name of the target wiki document.
124    * @return a {@link LinkBlock} representing the link from the parent document to new document.
125    */
 
126  8 toggle private LinkBlock createLink(Block block, String target)
127    {
128  8 Block firstBlock = block.getChildren().get(0);
129  8 if (firstBlock instanceof HeaderBlock) {
130  8 DocumentResourceReference reference = new DocumentResourceReference(target);
131    // Clone the header block and remove any unwanted stuff
132  8 Block clonedHeaderBlock = firstBlock.clone(new BlockFilter()
133    {
 
134  9 toggle @Override
135    public List<Block> filter(Block block)
136    {
137  9 List<Block> blocks = new ArrayList<Block>();
138  9 if (block instanceof WordBlock || block instanceof SpaceBlock
139    || block instanceof SpecialSymbolBlock) {
140  8 blocks.add(block);
141    }
142  9 return blocks;
143    }
144    });
145  8 return new LinkBlock(clonedHeaderBlock.getChildren(), reference, false);
146  0 } else if (firstBlock instanceof SectionBlock) {
147  0 return createLink(firstBlock, target);
148    } else {
149  0 throw new IllegalArgumentException(
150    "A SectionBlock should either begin with a HeaderBlock or another SectionBlock.");
151    }
152    }
153   
154    /**
155    * Update the links to internal document fragments after those fragments have been moved as a result of the split.
156    * For instance the "#Chapter1" anchor will be updated to "ChildDocument#Chapter1" if the document fragment
157    * identified by "Chapter1" has been moved to "ChildDocument" as a result of the split.
158    *
159    * @param documents the list of documents whose anchors to update
160    */
 
161  3 toggle private void updateAnchors(List<WikiDocument> documents)
162    {
163    // First we need to collect all the document fragments and map them to their new location.
164  3 Map<String, String> fragments = collectDocumentFragments(documents);
165   
166    // Update the anchors.
167  3 for (WikiDocument document : documents) {
168  11 updateAnchors(document, fragments);
169    }
170    }
171   
172    /**
173    * @param document the document whose anchors to update
174    * @param fragments see {@link #collectDocumentFragments(List)}
175    */
 
176  11 toggle private void updateAnchors(WikiDocument document, Map<String, String> fragments)
177    {
178  11 for (LinkBlock linkBlock : document.getXdom().<LinkBlock> getBlocks(new ClassBlockMatcher(LinkBlock.class),
179    Axes.DESCENDANT)) {
180  10 ResourceReference reference = linkBlock.getReference();
181  10 ResourceType resoureceType = reference.getType();
182  10 String fragment = null;
183  10 if ((ResourceType.DOCUMENT.equals(resoureceType) || ResourceType.SPACE.equals(resoureceType))
184    && StringUtils.isEmpty(reference.getReference())) {
185  1 fragment = reference.getParameter(ANCHOR_PARAMETER);
186  9 } else if (StringUtils.startsWith(reference.getReference(), "#")
187    && (ResourceType.PATH.equals(resoureceType) || ResourceType.URL.equals(resoureceType))) {
188  1 fragment = reference.getReference().substring(1);
189    }
190   
191  10 String targetDocument = fragments.get(fragment);
192  10 if (targetDocument != null && !targetDocument.equals(document.getFullName())) {
193    // The fragment has been moved so we need to update the link.
194  2 reference.setType(ResourceType.DOCUMENT);
195  2 reference.setReference(targetDocument);
196  2 reference.setParameter(ANCHOR_PARAMETER, fragment);
197    }
198    }
199    }
200   
201    /**
202    * Looks for document fragments in the given documents. A document fragment is identified by an {@link IdBlock} for
203    * instance.
204    *
205    * @param documents the list of documents whose fragments to collect
206    * @return the collection of document fragments mapped to the document that contains them
207    */
 
208  3 toggle private Map<String, String> collectDocumentFragments(List<WikiDocument> documents)
209    {
210  3 Map<String, String> fragments = new HashMap<String, String>();
211  3 for (WikiDocument document : documents) {
212  11 for (IdBlock idBlock : document.getXdom().<IdBlock> getBlocks(new ClassBlockMatcher(IdBlock.class),
213    Axes.DESCENDANT)) {
214  1 fragments.put(idBlock.getName(), document.getFullName());
215    }
216    }
217  3 return fragments;
218    }
219    }