All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.xwiki.rendering.block.match.BlockNavigator Maven / Gradle / Ivy

There is a newer version: 16.9.0
Show newest version
/*
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.xwiki.rendering.block.match;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.xwiki.rendering.block.Block;
import org.xwiki.rendering.block.Block.Axes;

/**
 * Tool to navigate in a tree of blocks and extract them based on configurable criteria.
 *
 * @version $Id: e26c8beac34b7eb222b6cb8b1274e0cf20c98223 $
 * @since 5.0M1
 */
public class BlockNavigator
{
    /**
     * Used to filter the result of the various methods.
     */
    private BlockMatcher matcher;

    /**
     * The default matcher does not filter anything.
     */
    public BlockNavigator()
    {
        this.matcher = AnyBlockMatcher.ANYBLOCKMATCHER;
    }

    /**
     * @param matcher used to filter the result of the various methods
     */
    public BlockNavigator(BlockMatcher matcher)
    {
        this.matcher = matcher;
    }

    // Blocks

    /**
     * Get all blocks following provided {@link BlockMatcher} and {@link Axes}.
     *
     * @param  the class of the Blocks to return
     * @param currentBlock the block to start searching from
     * @param currentAxes indicate the search axes
     * @return the matched {@link Block}s, empty list of none was found
     */
    public  List getBlocks(Block currentBlock, Axes currentAxes)
    {
        List blocks = new ArrayList();

        Block block = currentBlock;
        Axes axes = currentAxes;

        while (block != null) {
            Block nextBlock = null;

            switch (axes) {
            // SELF
                case SELF:
                    addBlock(block, blocks);
                    break;
                // ANCESTOR
                case ANCESTOR_OR_SELF:
                    addBlock(block, blocks);
                    nextBlock = block.getParent();
                    break;
                case ANCESTOR:
                    nextBlock = block.getParent();
                    axes = Axes.ANCESTOR_OR_SELF;
                    break;
                case PARENT:
                    nextBlock = block.getParent();
                    axes = Axes.SELF;
                    break;
                // DESCENDANT
                case CHILD:
                    if (!block.getChildren().isEmpty()) {
                        nextBlock = block.getChildren().get(0);
                        axes = Axes.FOLLOWING_SIBLING;
                        addBlock(nextBlock, blocks);
                    }
                    break;
                case DESCENDANT_OR_SELF:
                    addBlock(block, blocks);
                    blocks = getBlocks(block.getChildren(), Axes.DESCENDANT_OR_SELF, blocks);
                    break;
                case DESCENDANT:
                    blocks = getBlocks(block.getChildren(), Axes.DESCENDANT_OR_SELF, blocks);
                    break;
                // FOLLOWING
                case FOLLOWING_SIBLING:
                    nextBlock = block.getNextSibling();
                    addBlock(nextBlock, blocks);
                    break;
                case FOLLOWING:
                    for (Block nextSibling = block.getNextSibling(); nextSibling != null;
                        nextSibling = nextSibling.getNextSibling()) {
                        blocks = getBlocks(nextSibling, Axes.DESCENDANT_OR_SELF, blocks);
                    }
                    break;
                // PRECEDING
                case PRECEDING_SIBLING:
                    nextBlock = block.getPreviousSibling();
                    addBlock(nextBlock, blocks);
                    break;
                case PRECEDING:
                    for (Block previousSibling = block.getPreviousSibling(); previousSibling != null;
                        previousSibling = previousSibling.getPreviousSibling()) {
                        blocks = getBlocks(previousSibling, Axes.DESCENDANT_OR_SELF, blocks);
                    }
                    break;
                default:
                    break;
            }

            block = nextBlock;
        }

        return blocks != null ? blocks : Collections.emptyList();
    }

    /**
     * Add provided {@link Block} to provided list (or create list of null) if block validate the provided
     * {@link BlockMatcher}.
     *
     * @param  the class of the Blocks to return
     * @param currentBlock the block to search from
     * @param blocks the list of blocks to fill
     */
    private  void addBlock(Block currentBlock, List blocks)
    {
        if (currentBlock != null && this.matcher.match(currentBlock)) {
            blocks.add((T) currentBlock);
        }
    }

    /**
     * Add all blocks following provided {@link BlockMatcher} and {@link Axes} in the provide list (or create a new list
     * of provided list is null).
     *
     * @param  the class of the Blocks to return
     * @param blocks the blocks from where to search
     * @param axes the axes
     * @param blocksOut the list of blocks to fill
     * @return the modified list, null if provided list is null and provided {@link Block} does not validate provided
     *         {@link BlockMatcher}
     */
    private  List getBlocks(List blocks, Axes axes, List blocksOut)
    {
        List newBlocks = blocksOut;

        for (Block block : blocks) {
            newBlocks = getBlocks(block, axes, newBlocks);
        }

        return newBlocks;
    }

    /**
     * Add all blocks following provided {@link BlockMatcher} and {@link Axes} in the provide list (or create a new list
     * of provided list is null).
     *
     * @param  the class of the Blocks to return
     * @param currentBlock the block to search from
     * @param axes the axes
     * @param blocksOut the list of blocks to fill
     * @return the modified list, null if provided list is null and provided {@link Block} does not validate provided
     *         {@link BlockMatcher}
     */
    private  List getBlocks(Block currentBlock, Axes axes, List blocksOut)
    {
        List newBlocks = blocksOut;

        List nextBlocks = getBlocks(currentBlock, axes);
        if (!nextBlocks.isEmpty()) {
            if (newBlocks == null) {
                newBlocks = nextBlocks;
            } else {
                newBlocks.addAll(nextBlocks);
            }
        }

        return newBlocks;
    }

    // First block

    /**
     * Get the first matched block in the provided {@link Axes}.
     *
     * @param  the class of the Block to return
     * @param currentBlock the block to start searching from
     * @param currentAxes indicate the search axes
     * @return the matched {@link Block}, null if none was found
     */
    public  T getFirstBlock(Block currentBlock, Axes currentAxes)
    {
        Block block = currentBlock;
        Axes axes = currentAxes;

        while (block != null) {
            Block nextBlock = null;
            switch (axes) {
            // SELF
                case SELF:
                    if (this.matcher.match(block)) {
                        return (T) block;
                    }
                    break;
                // ANCESTOR
                case ANCESTOR_OR_SELF:
                    if (this.matcher.match(block)) {
                        return (T) block;
                    }
                case ANCESTOR:
                case PARENT:
                    axes = axes == Axes.PARENT ? Axes.SELF : Axes.ANCESTOR_OR_SELF;
                    nextBlock = block.getParent();
                    break;
                // DESCENDANT
                case CHILD:
                    List children = block.getChildren();
                    if (!children.isEmpty()) {
                        nextBlock = children.get(0);
                        axes = Axes.FOLLOWING_SIBLING;
                        if (this.matcher.match(nextBlock)) {
                            return (T) nextBlock;
                        }
                    }
                    break;
                case DESCENDANT_OR_SELF:
                    if (this.matcher.match(block)) {
                        return (T) block;
                    }
                case DESCENDANT:
                    for (Block child : block.getChildren()) {
                        Block matchedBlock = getFirstBlock(child, Axes.DESCENDANT_OR_SELF);
                        if (matchedBlock != null) {
                            return (T) matchedBlock;
                        }
                    }
                    break;
                // FOLLOWING
                case FOLLOWING_SIBLING:
                    nextBlock = block.getNextSibling();
                    if (nextBlock != null && this.matcher.match(nextBlock)) {
                        return (T) nextBlock;
                    }
                    break;
                case FOLLOWING:
                    for (Block nextSibling = block.getNextSibling(); nextSibling != null;
                        nextSibling = nextSibling.getNextSibling()) {
                        Block matchedBlock = getFirstBlock(nextSibling, Axes.DESCENDANT_OR_SELF);
                        if (matchedBlock != null) {
                            return (T) matchedBlock;
                        }
                    }
                    break;
                // PRECEDING
                case PRECEDING_SIBLING:
                    nextBlock = block.getPreviousSibling();
                    if (nextBlock != null && this.matcher.match(nextBlock)) {
                        return (T) nextBlock;
                    }
                    break;
                case PRECEDING:
                    for (Block previousSibling = block.getPreviousSibling(); previousSibling != null;
                        previousSibling = previousSibling.getPreviousSibling()) {
                        Block matchedBlock = getFirstBlock(previousSibling, Axes.DESCENDANT_OR_SELF);
                        if (matchedBlock != null) {
                            return (T) matchedBlock;
                        }
                    }
                    break;
                default:
                    break;
            }

            block = nextBlock;
        }

        return (T) block;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy