Clover Coverage Report - XWiki Rendering - Parent POM 4.0-SNAPSHOT (Aggregated)
Coverage timestamp: Mon Mar 12 2012 18:03:13 CET
../../../../img/srcFileCovDistChart9.png 55% of files have more coverage
326   955   152   6.52
152   619   0.47   50
50     3.04  
1    
 
  AbstractBlock       Line # 42 326 0% 152 57 89.2% 0.89204544
 
  (988)
 
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.rendering.block;
21   
22    import java.security.InvalidParameterException;
23    import java.util.ArrayList;
24    import java.util.Collections;
25    import java.util.LinkedHashMap;
26    import java.util.List;
27    import java.util.Map;
28   
29    import org.apache.commons.lang3.builder.EqualsBuilder;
30    import org.apache.commons.lang3.builder.HashCodeBuilder;
31    import org.xwiki.rendering.block.match.BlockMatcher;
32    import org.xwiki.rendering.block.match.ClassBlockMatcher;
33    import org.xwiki.rendering.listener.Listener;
34   
35    /**
36    * Implementation for Block operations. All blocks should extend this class. Supports the notion of generic parameters
37    * which can be added to a block (see {@link #getParameter(String)} for more details.
38    *
39    * @version $Id: b94fa7aa931fe5f7ada643ec55861149ce487b4a $
40    * @since 1.5M2
41    */
 
42    public abstract class AbstractBlock implements Block
43    {
44    /**
45    * Store parameters, see {@link #getParameter(String)} for more explanations on what parameters are.
46    */
47    private Map<String, String> parameters;
48   
49    /**
50    * The Blocks this Block contains.
51    */
52    private List<Block> childrenBlocks;
53   
54    /**
55    * The Block containing this Block.
56    */
57    private Block parentBlock;
58   
59    /**
60    * The next Sibling Block or null if no next sibling exists.
61    */
62    private Block nextSiblingBlock;
63   
64    /**
65    * The previous Sibling Block or null if no previous sibling exists.
66    */
67    private Block previousSiblingBlock;
68   
69    /**
70    * Empty constructor to construct an empty block.
71    */
 
72  8319 toggle public AbstractBlock()
73    {
74    // Nothing to do
75    }
76   
77    /**
78    * Construct a block with parameters.
79    *
80    * @param parameters the parameters to set
81    */
 
82  8596 toggle public AbstractBlock(Map<String, String> parameters)
83    {
84  8596 setParameters(parameters);
85    }
86   
87    /**
88    * Constructs a block with a child block.
89    *
90    * @param childBlock the child block of this block
91    * @since 3.0M1
92    */
 
93  0 toggle public AbstractBlock(Block childBlock)
94    {
95  0 this(childBlock, Collections.<String, String> emptyMap());
96    }
97   
98    /**
99    * Constructs a block with children blocks.
100    *
101    * @param childrenBlocks the list of children blocks of the block to construct
102    * @since 3.0M1
103    */
 
104  2804 toggle public AbstractBlock(List< ? extends Block> childrenBlocks)
105    {
106  2804 this(childrenBlocks, Collections.<String, String> emptyMap());
107    }
108   
109    /**
110    * Construct a block with a child block and parameters.
111    *
112    * @param childBlock the child block of this block
113    * @param parameters the parameters to set
114    * @since 3.0M1
115    */
 
116  0 toggle public AbstractBlock(Block childBlock, Map<String, String> parameters)
117    {
118  0 this(parameters);
119   
120  0 addChild(childBlock);
121    }
122   
123    /**
124    * Construct a block with children blocks and parameters.
125    *
126    * @param childrenBlocks the list of children blocks of the block to construct
127    * @param parameters the parameters to set
128    * @since 3.0M1
129    */
 
130  7002 toggle public AbstractBlock(List< ? extends Block> childrenBlocks, Map<String, String> parameters)
131    {
132  7002 this(parameters);
133   
134  7002 addChildren(childrenBlocks);
135    }
136   
 
137  14546 toggle @Override
138    public void addChild(Block blockToAdd)
139    {
140  14546 insertChildAfter(blockToAdd, null);
141    }
142   
 
143  7171 toggle @Override
144    public void addChildren(List< ? extends Block> blocksToAdd)
145    {
146  7171 if (!blocksToAdd.isEmpty()) {
147  6804 if (this.childrenBlocks == null) {
148    // Create the list with just the exact required size
149  6650 this.childrenBlocks = new ArrayList<Block>(blocksToAdd.size());
150    }
151   
152  6804 for (Block blockToAdd : blocksToAdd) {
153  14204 addChild(blockToAdd);
154    }
155    }
156    }
157   
 
158  12 toggle @Override
159    public void setChildren(List< ? extends Block> children)
160    {
161  12 if (children.isEmpty()) {
162  0 if (this.childrenBlocks != null) {
163  0 this.childrenBlocks.clear();
164    }
165    } else {
166  12 if (this.childrenBlocks != null) {
167  12 this.childrenBlocks.clear();
168    }
169   
170  12 addChildren(children);
171    }
172    }
173   
 
174  24631 toggle @Override
175    public void setNextSiblingBlock(Block nextSiblingBlock)
176    {
177  24631 this.nextSiblingBlock = nextSiblingBlock;
178    }
179   
 
180  17038 toggle @Override
181    public void setPreviousSiblingBlock(Block previousSiblingBlock)
182    {
183  17038 this.previousSiblingBlock = previousSiblingBlock;
184    }
185   
 
186  14548 toggle @Override
187    public void insertChildBefore(Block blockToInsert, Block nextBlock)
188    {
189  14548 blockToInsert.setParent(this);
190   
191  14548 if (nextBlock == null) {
192    // Last block becomes last but one
193  14546 if (this.childrenBlocks != null && !this.childrenBlocks.isEmpty()) {
194  7584 Block lastBlock = this.childrenBlocks.get(this.childrenBlocks.size() - 1);
195  7584 blockToInsert.setPreviousSiblingBlock(lastBlock);
196  7584 lastBlock.setNextSiblingBlock(blockToInsert);
197    } else {
198  6962 blockToInsert.setPreviousSiblingBlock(null);
199   
200  6962 if (this.childrenBlocks == null) {
201  254 this.childrenBlocks = new ArrayList<Block>(1);
202    }
203    }
204  14546 blockToInsert.setNextSiblingBlock(null);
205  14546 this.childrenBlocks.add(blockToInsert);
206    } else {
207    // If there's a previous block to nextBlock then get it to set its next sibling
208  2 Block previousBlock = nextBlock.getPreviousSibling();
209  2 if (previousBlock != null) {
210  1 previousBlock.setNextSiblingBlock(blockToInsert);
211  1 blockToInsert.setPreviousSiblingBlock(previousBlock);
212    } else {
213  1 blockToInsert.setPreviousSiblingBlock(null);
214    }
215  2 blockToInsert.setNextSiblingBlock(nextBlock);
216  2 nextBlock.setPreviousSiblingBlock(blockToInsert);
217  2 if (this.childrenBlocks == null || this.childrenBlocks.isEmpty()) {
218  0 this.childrenBlocks = new ArrayList<Block>(1);
219  0 this.childrenBlocks.add(blockToInsert);
220    } else {
221  2 this.childrenBlocks.add(indexOfChild(nextBlock), blockToInsert);
222    }
223    }
224    }
225   
 
226  14548 toggle @Override
227    public void insertChildAfter(Block blockToInsert, Block previousBlock)
228    {
229  14548 if (previousBlock == null) {
230  14546 insertChildBefore(blockToInsert, null);
231    } else {
232    // If there's a next block to previousBlock then get it to set its previous sibling
233  2 Block nextBlock = previousBlock.getNextSibling();
234  2 if (nextBlock != null) {
235  1 nextBlock.setPreviousSiblingBlock(blockToInsert);
236  1 blockToInsert.setNextSiblingBlock(nextBlock);
237    } else {
238  1 blockToInsert.setNextSiblingBlock(null);
239    }
240  2 blockToInsert.setPreviousSiblingBlock(previousBlock);
241  2 previousBlock.setNextSiblingBlock(blockToInsert);
242  2 if (this.childrenBlocks == null) {
243  0 this.childrenBlocks = new ArrayList<Block>(1);
244    }
245  2 this.childrenBlocks.add(indexOfChild(previousBlock) + 1, blockToInsert);
246    }
247    }
248   
 
249  1168 toggle @Override
250    public void replaceChild(Block newBlock, Block oldBlock)
251    {
252  1168 replaceChild(Collections.singletonList(newBlock), oldBlock);
253    }
254   
 
255  1170 toggle @Override
256    public void replaceChild(List<Block> newBlocks, Block oldBlock)
257    {
258  1170 int position = indexOfChild(oldBlock);
259   
260  1170 if (position == -1) {
261  1 throw new InvalidParameterException("Provided Block to replace is not a child");
262    }
263   
264  1169 List<Block> blocks = getChildren();
265   
266    // Remove old child
267  1169 blocks.remove(position);
268  1169 oldBlock.setParent(null);
269   
270    // Insert new children
271  1169 Block previousBlock = oldBlock.getPreviousSibling();
272  1169 if (newBlocks.isEmpty()) {
273  1 previousBlock.setNextSiblingBlock(oldBlock.getNextSibling());
274    }
275  1169 Block lastBlock = null;
276  1169 for (Block block : newBlocks) {
277  1169 block.setParent(this);
278  1169 block.setPreviousSiblingBlock(previousBlock);
279  1169 if (previousBlock != null) {
280  92 previousBlock.setNextSiblingBlock(block);
281    }
282  1169 previousBlock = block;
283  1169 lastBlock = block;
284    }
285  1169 Block nextBlock = oldBlock.getNextSibling();
286  1169 if (nextBlock != null) {
287  84 nextBlock.setPreviousSiblingBlock(lastBlock);
288    }
289  1169 if (lastBlock != null) {
290  1168 lastBlock.setNextSiblingBlock(nextBlock);
291    }
292   
293  1169 blocks.addAll(position, newBlocks);
294   
295  1169 oldBlock.setNextSiblingBlock(null);
296  1169 oldBlock.setPreviousSiblingBlock(null);
297    }
298   
299    /**
300    * Get the position of the provided block in the list of children.
301    * <p>
302    * Can't use {@link List#indexOf(Object)} since it's using {@link Object#equals(Object)} internally which is not
303    * what we want since two WordBlock with the same text or two spaces are equals for example but we want to be able
304    * to target one specific Block.
305    *
306    * @param block the block
307    * @return the position of the block, -1 if the block can't be found
308    */
 
309  1174 toggle private int indexOfChild(Block block)
310    {
311  1174 return indexOfBlock(block, getChildren());
312    }
313   
314    /**
315    * Get the position of the provided block in the provided list of blocks.
316    * <p>
317    * Can't use {@link List#indexOf(Object)} since it's using {@link Object#equals(Object)} internally which is not
318    * what we want since two WordBlock with the same text or two spaces are equals for example but we want to be able
319    * to target one specific Block.
320    *
321    * @param block the block for which to find the position
322    * @param blocks the list of blocks in which to look for the passed block
323    * @return the position of the block, -1 if the block can't be found
324    */
 
325  1181 toggle private int indexOfBlock(Block block, List<Block> blocks)
326    {
327  1181 int position = 0;
328   
329  1181 for (Block child : blocks) {
330  1578 if (child == block) {
331  1180 return position;
332    }
333  398 ++position;
334    }
335   
336  1 return -1;
337    }
338   
 
339  1018967 toggle @Override
340    public List<Block> getChildren()
341    {
342  1018967 return this.childrenBlocks == null ? Collections.<Block> emptyList() : this.childrenBlocks;
343    }
344   
 
345  1465 toggle @Override
346    public Block getParent()
347    {
348  1465 return this.parentBlock;
349    }
350   
 
351  11092 toggle @Override
352    public Map<String, String> getParameters()
353    {
354  11092 return this.parameters == null ? Collections.<String, String> emptyMap() : Collections
355    .unmodifiableMap(this.parameters);
356    }
357   
 
358  0 toggle @Override
359    public String getParameter(String name)
360    {
361  0 return this.parameters == null ? null : this.parameters.get(name);
362    }
363   
 
364  16 toggle @Override
365    public void setParameter(String name, String value)
366    {
367  16 if (this.parameters == null) {
368  0 this.parameters = new LinkedHashMap<String, String>(1);
369    }
370   
371  16 this.parameters.put(name, value);
372    }
373   
 
374  8601 toggle @Override
375    public void setParameters(Map<String, String> parameters)
376    {
377  8601 if (this.parameters == null) {
378  8596 this.parameters = new LinkedHashMap<String, String>(parameters);
379    } else {
380  5 this.parameters.clear();
381  5 this.parameters.putAll(parameters);
382    }
383    }
384   
 
385  16897 toggle @Override
386    public void setParent(Block parentBlock)
387    {
388  16897 this.parentBlock = parentBlock;
389    }
390   
 
391  13 toggle @Override
392    public Block getRoot()
393    {
394  13 Block block = this;
395   
396  62 while (block.getParent() != null) {
397  49 block = block.getParent();
398    }
399   
400  13 return block;
401    }
402   
 
403  1476 toggle @Override
404    public Block getNextSibling()
405    {
406  1476 return this.nextSiblingBlock;
407    }
408   
 
409  1222 toggle @Override
410    public Block getPreviousSibling()
411    {
412  1222 return this.previousSiblingBlock;
413    }
414   
 
415  33 toggle @Override
416    public void removeBlock(Block childBlockToRemove)
417    {
418  33 getChildren().remove(childBlockToRemove);
419  33 if (childBlockToRemove != null) {
420  33 Block previousBlock = childBlockToRemove.getPreviousSibling();
421  33 if (previousBlock != null) {
422  31 previousBlock.setNextSiblingBlock(childBlockToRemove.getNextSibling());
423    }
424  33 Block nextBlock = childBlockToRemove.getNextSibling();
425  33 if (nextBlock != null) {
426  30 nextBlock.setPreviousSiblingBlock(previousBlock);
427    }
428  33 childBlockToRemove.setNextSiblingBlock(null);
429  33 childBlockToRemove.setPreviousSiblingBlock(null);
430    }
431    }
432   
 
433  674 toggle @Override
434    public boolean equals(Object obj)
435    {
436  674 return EqualsBuilder.reflectionEquals(this, obj);
437    }
438   
 
439  0 toggle @Override
440    public int hashCode()
441    {
442  0 return HashCodeBuilder.reflectionHashCode(this);
443    }
444   
 
445  23 toggle @Override
446    public Block clone()
447    {
448  23 return clone(null);
449    }
450   
451    /**
452    * {@inheritDoc}
453    *
454    * @since 1.8RC2
455    */
 
456  199 toggle @Override
457    public Block clone(BlockFilter blockFilter)
458    {
459  199 Block block;
460  199 try {
461  199 block = (AbstractBlock) super.clone();
462    } catch (CloneNotSupportedException e) {
463    // Should never happen
464  0 throw new RuntimeException("Failed to clone object", e);
465    }
466   
467  199 if (this.parameters != null) {
468  66 ((AbstractBlock) block).parameters = new LinkedHashMap<String, String>(this.parameters);
469    }
470   
471  199 if (this.childrenBlocks != null) {
472  46 ((AbstractBlock) block).childrenBlocks = new ArrayList<Block>(this.childrenBlocks.size());
473  46 for (Block childBlock : this.childrenBlocks) {
474  138 if (blockFilter != null) {
475  134 Block clonedChildBlocks = childBlock.clone(blockFilter);
476   
477  134 List<Block> filteredBlocks = blockFilter.filter(clonedChildBlocks);
478   
479  134 if (filteredBlocks.size() == 0) {
480  2 filteredBlocks = clonedChildBlocks.getChildren();
481    }
482   
483  134 block.addChildren(filteredBlocks);
484    } else {
485  4 block.addChild(childBlock.clone());
486    }
487    }
488    }
489   
490  199 return block;
491    }
492   
 
493  5722 toggle @Override
494    public void traverse(Listener listener)
495    {
496  5722 before(listener);
497   
498  5722 for (Block block : getChildren()) {
499  12253 block.traverse(listener);
500    }
501   
502  5722 after(listener);
503    }
504   
505    /**
506    * Send {@link org.xwiki.rendering.listener.Listener} events corresponding to the start of the block. For example
507    * for a Bold block, this allows an XHTML Listener (aka a Renderer) to output <code>&lt;b&gt;</code>.
508    *
509    * @param listener the listener that will receive the events sent by this block before its children blocks have
510    * emitted their own events.
511    */
 
512  0 toggle public void before(Listener listener)
513    {
514    // Do nothing by default, should be overridden by extending Blocks
515    }
516   
517    /**
518    * Send {@link Listener} events corresponding to the end of the block. For example for a Bold block, this allows an
519    * XHTML Listener (aka a Renderer) to output <code>&lt;/b&gt;</code>.
520    *
521    * @param listener the listener that will receive the events sent by this block before its children blocks have
522    * emitted their own events.
523    */
 
524  0 toggle public void after(Listener listener)
525    {
526    // Do nothing by default, should be overridden by extending Blocks
527    }
528   
 
529  1009122 toggle @Override
530    public <T extends Block> List<T> getBlocks(BlockMatcher matcher, Axes axes)
531    {
532  1009122 List<T> blocks = null;
533   
534  1009122 if (axes == Axes.SELF) {
535  2 blocks = addBlock(this, matcher, blocks);
536  1009120 } else if (axes.compareTo(Axes.ANCESTOR_OR_SELF) <= 0) {
537  7 blocks = getAncestorBlocks(matcher, axes);
538  1009113 } else if (axes.compareTo(Axes.DESCENDANT_OR_SELF) <= 0) {
539  1009101 blocks = getDescendantBlocks(matcher, axes);
540    } else {
541  12 blocks = getSiblingBlocks(matcher, axes);
542    }
543   
544  1009122 return blocks != null ? blocks : Collections.<T> emptyList();
545    }
546   
547    /**
548    * Get all blocks following provided {@link BlockMatcher} and ancestor {@link Axes}.
549    *
550    * @param <T> the class of the Blocks to return
551    * @param matcher filter the blocks to return
552    * @param axes indicate the search axes
553    * @return the matched {@link Block}s, empty list of none was found
554    */
 
555  7 toggle private <T extends Block> List<T> getAncestorBlocks(BlockMatcher matcher, Axes axes)
556    {
557  7 List<T> blocks = null;
558   
559  7 T nextBlock = (T) getParent();
560  7 Axes nextAxes = axes;
561   
562  7 switch (axes) {
563  5 case ANCESTOR_OR_SELF:
564  5 blocks = addBlock(this, matcher, blocks);
565  5 break;
566  1 case ANCESTOR:
567  1 nextAxes = Axes.ANCESTOR_OR_SELF;
568  1 break;
569  1 case PARENT:
570  1 nextAxes = Axes.SELF;
571  1 break;
572  0 default:
573  0 break;
574    }
575   
576  7 if (nextBlock != null) {
577  5 blocks = getBlocks(nextBlock, matcher, nextAxes, blocks);
578    }
579   
580  7 return blocks != null ? blocks : Collections.<T> emptyList();
581    }
582   
583    /**
584    * Get all blocks following provided {@link BlockMatcher} and descendant {@link Axes}.
585    *
586    * @param <T> the class of the Blocks to return
587    * @param matcher filter the blocks to return
588    * @param axes indicate the search axes
589    * @return the matched {@link Block}s, empty list of none was found
590    */
 
591  1009101 toggle private <T extends Block> List<T> getDescendantBlocks(BlockMatcher matcher, Axes axes)
592    {
593  1009101 List<T> blocks = null;
594   
595  1009101 T nextBlock = null;
596  1009101 Axes nextAxes = axes;
597   
598  1009101 switch (axes) {
599  3 case CHILD:
600  3 if (!getChildren().isEmpty()) {
601  3 nextBlock = (T) getChildren().get(0);
602  3 nextAxes = Axes.FOLLOWING_SIBLING;
603  3 blocks = addBlock(nextBlock, matcher, blocks);
604    }
605  3 break;
606  1006594 case DESCENDANT_OR_SELF:
607  1006594 blocks = addBlock(this, matcher, blocks);
608  1006594 blocks = getBlocks((List) getChildren(), matcher, Axes.DESCENDANT_OR_SELF, blocks);
609  1006594 break;
610  2504 case DESCENDANT:
611  2504 blocks = getBlocks((List) getChildren(), matcher, Axes.DESCENDANT_OR_SELF, blocks);
612  2504 break;
613  0 default:
614  0 break;
615    }
616   
617  1009101 if (nextBlock != null) {
618  3 blocks = getBlocks(nextBlock, matcher, nextAxes, blocks);
619    }
620   
621  1009101 return blocks != null ? blocks : Collections.<T> emptyList();
622    }
623   
624    /**
625    * Get all blocks following provided {@link BlockMatcher} and following/preceding sibling {@link Axes}.
626    *
627    * @param <T> the class of the Blocks to return
628    * @param matcher filter the blocks to return
629    * @param axes indicate the search axes
630    * @return the matched {@link Block}s, empty list of none was found
631    */
 
632  12 toggle private <T extends Block> List<T> getSiblingBlocks(BlockMatcher matcher, Axes axes)
633    {
634  12 List<T> blocks = null;
635   
636  12 T nextBlock = null;
637  12 Axes nextAxes = axes;
638   
639  12 switch (axes) {
640    // FOLLOWING
641  8 case FOLLOWING_SIBLING:
642  8 nextBlock = (T) getNextSibling();
643  8 blocks = addBlock(nextBlock, matcher, blocks);
644  8 break;
645  1 case FOLLOWING:
646  2 for (Block nextSibling = getNextSibling(); nextSibling != null; nextSibling =
647    nextSibling.getNextSibling()) {
648  1 blocks = getBlocks((T) nextSibling, matcher, Axes.DESCENDANT_OR_SELF, blocks);
649    }
650  1 break;
651    // PRECEDING
652  2 case PRECEDING_SIBLING:
653  2 nextBlock = (T) getPreviousSibling();
654  2 blocks = addBlock(nextBlock, matcher, blocks);
655  2 break;
656  1 case PRECEDING:
657  2 for (Block previousSibling = getPreviousSibling(); previousSibling != null; previousSibling =
658    previousSibling.getPreviousSibling()) {
659  1 blocks = getBlocks((T) previousSibling, matcher, Axes.DESCENDANT_OR_SELF, blocks);
660    }
661  1 break;
662  0 default:
663  0 break;
664    }
665   
666  12 if (nextBlock != null) {
667  5 blocks = getBlocks(nextBlock, matcher, nextAxes, blocks);
668    }
669   
670  12 return blocks != null ? blocks : Collections.<T> emptyList();
671    }
672   
673    /**
674    * Add provided {@link Block} to provided list (or create list of null) if block validate the provided
675    * {@link BlockMatcher}.
676    *
677    * @param <T> the class of the Blocks to return
678    * @param block the block
679    * @param matcher the matcher
680    * @param blocks the list of blocks to fill
681    * @return the modified list, null if provided list is null and provided {@link Block} does not validate provided
682    * {@link BlockMatcher}
683    */
 
684  1006614 toggle private <T extends Block> List<T> addBlock(Block block, BlockMatcher matcher, List<T> blocks)
685    {
686  1006614 List<T> newBlocks = blocks;
687   
688  1006614 if (block != null && matcher.match(block)) {
689  2487 if (newBlocks == null) {
690  2487 newBlocks = new ArrayList<T>();
691    }
692  2487 newBlocks.add((T) block);
693    }
694   
695  1006614 return newBlocks;
696    }
697   
698    /**
699    * Add all blocks following provided {@link BlockMatcher} and {@link Axes} in the provide list (or create a new list
700    * of provided list is null).
701    *
702    * @param <T> the class of the Blocks to return
703    * @param blocks the blocks from where to search
704    * @param matcher the block matcher
705    * @param axes the axes
706    * @param blocksOut the list of blocks to fill
707    * @return the modified list, null if provided list is null and provided {@link Block} does not validate provided
708    * {@link BlockMatcher}
709    */
 
710  1009098 toggle private <T extends Block> List<T> getBlocks(List<T> blocks, BlockMatcher matcher, Axes axes, List<T> blocksOut)
711    {
712  1009098 List<T> newBlocks = blocksOut;
713   
714  1009098 for (T child : blocks) {
715  1006591 newBlocks = getBlocks(child, matcher, axes, newBlocks);
716    }
717   
718  1009098 return newBlocks;
719    }
720   
721    /**
722    * Add all blocks following provided {@link BlockMatcher} and {@link Axes} in the provide list (or create a new list
723    * of provided list is null).
724    *
725    * @param <T> the class of the Blocks to return
726    * @param block the block from where to search
727    * @param matcher the block matcher
728    * @param axes the axes
729    * @param blocksOut the list of blocks to fill
730    * @return the modified list, null if provided list is null and provided {@link Block} does not validate provided
731    * {@link BlockMatcher}
732    */
 
733  1006606 toggle private <T extends Block> List<T> getBlocks(T block, BlockMatcher matcher, Axes axes, List<T> blocksOut)
734    {
735  1006606 List<T> newBlocks = blocksOut;
736   
737  1006606 List<T> nextBlocks = block.getBlocks(matcher, axes);
738  1006606 if (!nextBlocks.isEmpty()) {
739  1002746 if (newBlocks == null) {
740  1002620 newBlocks = nextBlocks;
741    } else {
742  126 newBlocks.addAll(nextBlocks);
743    }
744    }
745   
746  1006606 return newBlocks;
747    }
748   
 
749  196 toggle @Override
750    public <T extends Block> T getFirstBlock(BlockMatcher matcher, Axes axes)
751    {
752  196 T block = null;
753   
754  196 if (axes == Axes.SELF) {
755  4 if (matcher.match(this)) {
756  2 block = (T) this;
757    }
758  192 } else if (axes.compareTo(Axes.ANCESTOR_OR_SELF) <= 0) {
759  107 block = (T) getFirstAncestorBlock(matcher, axes);
760  85 } else if (axes.compareTo(Axes.DESCENDANT_OR_SELF) <= 0) {
761  74 block = (T) getFirstDescendantBlock(matcher, axes);
762  11 } else if (axes.compareTo(Axes.FOLLOWING_SIBLING) <= 0) {
763  6 block = (T) getFirstFollowingSiblingBlock(matcher, axes);
764    } else {
765  5 block = (T) getFirstPrecedingSiblingBlock(matcher, axes);
766    }
767   
768  196 return block;
769    }
770   
771    /**
772    * Get the first matched block in the provided ancestor {@link Axes}.
773    *
774    * @param matcher the block matcher
775    * @param axes the axes
776    * @return the matched {@link Block}, null if none was found
777    */
 
778  107 toggle private Block getFirstAncestorBlock(BlockMatcher matcher, Axes axes)
779    {
780  107 Block nextBlock = null;
781  107 Axes nextAxes = axes;
782   
783  107 switch (axes) {
784  93 case ANCESTOR_OR_SELF:
785  93 if (matcher.match(this)) {
786  37 return this;
787    }
788  12 case ANCESTOR:
789  2 case PARENT:
790  70 nextAxes = axes == Axes.PARENT ? Axes.SELF : Axes.ANCESTOR_OR_SELF;
791  70 nextBlock = getParent();
792  70 break;
793  0 default:
794  0 break;
795    }
796   
797  70 return nextBlock != null ? nextBlock.getFirstBlock(matcher, nextAxes) : null;
798    }
799   
800    /**
801    * Get the first matched block in the provided descendant {@link Axes}.
802    *
803    * @param matcher the block matcher
804    * @param axes the axes
805    * @return the matched {@link Block}, null if none was found
806    */
 
807  74 toggle private Block getFirstDescendantBlock(BlockMatcher matcher, Axes axes)
808    {
809  74 Block nextBlock = null;
810  74 Axes nextAxes = axes;
811   
812  74 switch (axes) {
813  2 case CHILD:
814  2 if (!getChildren().isEmpty()) {
815  2 nextBlock = this.childrenBlocks.get(0);
816  2 nextAxes = Axes.FOLLOWING_SIBLING;
817  2 if (matcher.match(nextBlock)) {
818  1 return nextBlock;
819    }
820    }
821  1 break;
822  68 case DESCENDANT_OR_SELF:
823  68 if (matcher.match(this)) {
824  8 return this;
825    }
826  4 case DESCENDANT:
827  64 for (Block child : getChildren()) {
828  62 Block matchedBlock = child.getFirstBlock(matcher, Axes.DESCENDANT_OR_SELF);
829  62 if (matchedBlock != null) {
830  7 return matchedBlock;
831    }
832    }
833  57 break;
834  0 default:
835  0 break;
836    }
837   
838  58 return nextBlock != null ? nextBlock.getFirstBlock(matcher, nextAxes) : null;
839    }
840   
841    /**
842    * Get the first matched block in the provided following sibling {@link Axes}.
843    *
844    * @param matcher the block matcher
845    * @param axes the axes
846    * @return the matched {@link Block}, null if none was found
847    */
 
848  6 toggle private Block getFirstFollowingSiblingBlock(BlockMatcher matcher, Axes axes)
849    {
850  6 Block nextBlock = null;
851  6 Axes nextAxes = axes;
852   
853  6 switch (axes) {
854  4 case FOLLOWING_SIBLING:
855  4 nextBlock = getNextSibling();
856  4 if (nextBlock != null && matcher.match(nextBlock)) {
857  2 return nextBlock;
858    }
859  2 break;
860  2 case FOLLOWING:
861  2 for (Block nextSibling = getNextSibling(); nextSibling != null; nextSibling =
862    nextSibling.getNextSibling()) {
863  2 Block matchedBlock = nextSibling.getFirstBlock(matcher, Axes.DESCENDANT_OR_SELF);
864  2 if (matchedBlock != null) {
865  2 return matchedBlock;
866    }
867    }
868  0 break;
869  0 default:
870  0 break;
871    }
872   
873  2 return nextBlock != null ? nextBlock.getFirstBlock(matcher, nextAxes) : null;
874    }
875   
876    /**
877    * Get the first matched block in the provided preceding sibling {@link Axes}.
878    *
879    * @param matcher the block matcher
880    * @param axes the axes
881    * @return the matched {@link Block}, null if none was found
882    */
 
883  5 toggle private Block getFirstPrecedingSiblingBlock(BlockMatcher matcher, Axes axes)
884    {
885  5 Block nextBlock = null;
886  5 Axes nextAxes = axes;
887   
888  5 switch (axes) {
889  3 case PRECEDING_SIBLING:
890  3 nextBlock = getPreviousSibling();
891  3 if (nextBlock != null && matcher.match(nextBlock)) {
892  1 return nextBlock;
893    }
894  2 break;
895  2 case PRECEDING:
896  2 for (Block previousSibling = getPreviousSibling(); previousSibling != null; previousSibling =
897    previousSibling.getPreviousSibling()) {
898  2 Block matchedBlock = previousSibling.getFirstBlock(matcher, Axes.DESCENDANT_OR_SELF);
899  2 if (matchedBlock != null) {
900  2 return matchedBlock;
901    }
902    }
903  0 break;
904  0 default:
905  0 break;
906    }
907   
908  2 return nextBlock != null ? nextBlock.getFirstBlock(matcher, nextAxes) : null;
909    }
910   
 
911  2473 toggle @Deprecated
912    @Override
913    public <T extends Block> List<T> getChildrenByType(Class<T> blockClass, boolean recurse)
914    {
915  2473 return getBlocks(new ClassBlockMatcher(blockClass), recurse ? Axes.DESCENDANT : Axes.CHILD);
916    }
917   
 
918  7 toggle @Deprecated
919    @Override
920    public <T extends Block> T getPreviousBlockByType(Class<T> blockClass, boolean recurse)
921    {
922    // Don't use #getFirstBlock(BlockMatcher, Axes) for retro-compatibility because it's a bit different:
923    // #getFirstBlock follows XPATH axes specifications and does not include "ancestors" in "preceding" axis
924   
925  7 if (getParent() == null) {
926  0 return null;
927    }
928   
929  7 int index = indexOfBlock(this, getParent().getChildren());
930   
931    // test previous brothers
932  7 List<Block> blocks = getParent().getChildren();
933  12 for (int i = index - 1; i >= 0; --i) {
934  7 Block previousBlock = blocks.get(i);
935  7 if (blockClass.isAssignableFrom(previousBlock.getClass())) {
936  2 return blockClass.cast(previousBlock);
937    }
938    }
939   
940    // test parent
941  5 if (blockClass.isAssignableFrom(getParent().getClass())) {
942  2 return blockClass.cast(getParent());
943    }
944   
945    // recurse
946  3 return recurse ? getParent().getPreviousBlockByType(blockClass, true) : null;
947    }
948   
 
949  0 toggle @Deprecated
950    @Override
951    public <T extends Block> T getParentBlockByType(Class<T> blockClass)
952    {
953  0 return blockClass.cast(getFirstBlock(new ClassBlockMatcher(blockClass), Axes.ANCESTOR));
954    }
955    }