001package org.javasimon.callback.calltree;
002
003import java.util.LinkedList;
004
005import org.javasimon.Split;
006import org.javasimon.callback.logging.LogMessageSource;
007
008/**
009 * Call tree contains the root call tree node and the current call stack.
010 *
011 * @author gquintana
012 * @since 3.2
013 */
014public class CallTree implements LogMessageSource<Split> {
015
016        // TODO in what unit?
017        /** Log threshold. */
018        private final Long logThreshold;
019
020        /** Call stack is the path (made of tree nodes) from root tree node to the current tree node. */
021        private final LinkedList<CallTreeNode> callStack = new LinkedList<>();
022
023        /** Root call tree node. */
024        private CallTreeNode rootNode;
025
026        /**
027         * Main constructor.
028         *
029         * @param logThreshold Log threshold
030         */
031        public CallTree(Long logThreshold) {
032                this.logThreshold = logThreshold;
033        }
034
035        /**
036         * When stopwatch is started, a new tree node is added to the parent
037         * tree node and pushed on the call stack.
038         * As a result, child tree node becomes the current tree node.
039         *
040         * @return Current (child) tree node
041         */
042        public CallTreeNode onStopwatchStart(Split split) {
043                final String name = split.getStopwatch().getName();
044                CallTreeNode currentNode;
045                if (callStack.isEmpty()) {
046                        // Root tree node
047                        rootNode = new CallTreeNode(name);
048                        currentNode = rootNode;
049                        onRootStopwatchStart(currentNode, split);
050                } else {
051                        // Child node
052                        currentNode = callStack.getLast().getOrAddChild(name);
053                }
054                callStack.addLast(currentNode);
055                return currentNode;
056        }
057
058        /**
059         * When stopwatch is stopped, the the split is added to current tree node
060         * and this tree node is popped from call stack.
061         * As a result, parent tree node becomes current tree node.
062         *
063         * @return Current (child) tree node
064         */
065        public CallTreeNode onStopwatchStop(Split split) {
066                CallTreeNode currentNode = callStack.removeLast();
067                currentNode.addSplit(split);
068                if (callStack.isEmpty()) {
069                        onRootStopwatchStop(currentNode, split);
070                }
071                return currentNode;
072        }
073
074        /**
075         * When stopwatch is started, and the root tree node is pushed into
076         * the call stack, this method is called.
077         * Does nothing but can be overridden for custom needs.
078         *
079         * @param rootNode Root tree node
080         * @param split Root split
081         */
082        public void onRootStopwatchStart(CallTreeNode rootNode, Split split) {
083        }
084
085        /**
086         * When stopwatch is stopped, and root tree node is popped from
087         * call stack, this method is called.
088         * Does nothing but can be overridden for custom needs, such as logging, storing...
089         */
090        protected void onRootStopwatchStop(CallTreeNode callTreeNode, Split split) {
091        }
092
093        /**
094         * Transforms this call tree into a loggable message.
095         */
096        public String getLogMessage(Split context) {
097                context.getStopwatch().setAttribute(CallTreeCallback.ATTR_NAME_LAST, this);
098                return "Call Tree:\r\n" + rootNode.toString();
099        }
100
101        public Long getLogThreshold() {
102                return logThreshold;
103        }
104
105        public CallTreeNode getRootNode() {
106                return rootNode;
107        }
108}