001/*
002 *  Copyright 2018 Anyware Services
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.ametys.cms.search.advanced.utils;
017
018import java.util.List;
019import java.util.function.Function;
020
021import org.ametys.cms.search.advanced.AbstractTreeNode;
022import org.ametys.cms.search.advanced.TreeInternalNode;
023import org.ametys.cms.search.advanced.TreeLeaf;
024
025/**
026 * Helper to {@link #print(AbstractTreeNode) print} a tree for debug purposes.
027 */
028public final class TreePrinter
029{
030    private TreePrinter()
031    {
032        // Nothing
033    }
034    
035    /**
036     * Prints the given tree
037     * <br>Use for debug purposes only.
038     * @param <T> the type of the values of the leaves of the tree.
039     * @param tree The tree to print
040     * @return The string representing the tree
041     */
042    public static <T> String print(AbstractTreeNode<T> tree)
043    {
044        return print(tree, Object::toString);
045    }
046    
047    /**
048     * Prints the given tree
049     * <br>Use for debug purposes only.
050     * @param <T> the type of the values of the leaves of the tree.
051     * @param tree The tree to print
052     * @param leafStringifier The function to transform a leaf value to a readable String
053     * @return The string representing the tree
054     */
055    public static <T> String print(AbstractTreeNode<T> tree, Function<T, String> leafStringifier)
056    {
057        StringBuilder sb = new StringBuilder();
058        _print(sb, tree, leafStringifier, new StringBuffer(), true);
059        return sb.toString();
060    }
061    
062    private static <T> void _print(StringBuilder sb, AbstractTreeNode<T> tree, Function<T, String> leafStringifier, StringBuffer prefix, boolean isTail)
063    {
064        String name;
065        if (tree instanceof TreeLeaf<?>)
066        {
067            name = leafStringifier.apply(((TreeLeaf<T>) tree).getValue());
068        }
069        else if (tree instanceof TreeInternalNode<?>)
070        {
071            name = ((TreeInternalNode<T>) tree).getLogicalOperator().toString();
072        }
073        else
074        {
075            throw new IllegalArgumentException("Wrong type of TreeNode. It must only be a TreeInternalNode or a TreeLeaf.");
076        }
077        sb.append(prefix).append(isTail ? "└── " : "├── ").append(name).append("\n");
078        
079        if (tree instanceof TreeInternalNode<?>)
080        {
081            List<AbstractTreeNode<T>> children = ((TreeInternalNode<T>) tree).getChildren();
082            StringBuffer newPrefix = new StringBuffer(prefix).append(isTail ? "    " : "│   ");
083            int i = 1;
084            for (AbstractTreeNode<T> child : children)
085            {
086                boolean childIsTail = i == children.size();
087                _print(sb, child, leafStringifier, newPrefix, childIsTail);
088                i++;
089            }
090        }
091    }
092}