/* This file is part of mjprof. mjprof is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. mjprof 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 General Public License for more details. You should have received a copy of the GNU General Public License along with mjprof. If not, see <http://www.gnu.org/licenses/>. */ package com.performizeit.mjprof.model; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; public class SFNode { boolean color = false; int count; String sf; HashMap<String, SFNode> children = new HashMap<>(); @Override public String toString() { if (count == 0) return ""; if (count == 1) return toStringSingle(); return toStringMulti(-1, new HashSet<>(), count); } private String toStringSingle() { String indent = " "; if (sf == null && children.size() > 0) return children.values().iterator().next().toStringSingle(); if (children.size() == 0) return indent + sf; return children.values().iterator().next().toStringSingle() + "\n" + indent + sf; } private String CSI() { return (char) 0x1b + "["; } private String GREEN() { if (!color) return ""; return CSI() + "1;32m"; } private String NC() { if (!color) return ""; return CSI() + "0m"; } public boolean isColor() { return color; } public void setColor(boolean color) { this.color = color; } public String toStringMulti(int lvl, HashSet<Integer> brkPts, int countall) { String a = ""; if (sf != null) { // we do not want to print the root node for (int i = 0; i < lvl; i++) { if (brkPts.contains(i)) { a += GREEN() + "|" + NC(); } else a += " "; } String ch = "\\"; if (children.size() > 1) { ch = "X"; brkPts.add(lvl); } if (children.size() == 0) { ch = "V"; } String bb = String.format("[%d/%d]", count, countall); a = String.format("%6.2f%%%10s", 100f * (float) count / countall, bb) + a + GREEN() + ch + NC() + " " + sf; a += "\n"; } int t = 0; for (SFNode n : children.values()) { if (t == children.size() - 1) { brkPts.remove(lvl); } t++; a += n.toStringMulti(lvl + 1, brkPts, countall); } return a; } // Visit all chlidren of a node recursively with a ProfileVisitor public void visitChildren(ProfileVisitor pv, int level) { pv.visit(this, level); HashMap<String, SFNode> newChildren = new HashMap<>(); for (SFNode child : children.values()) { // visit all childred child.visitChildren(pv, level + 1); newChildren.put(child.getStackFrame(), child); } children = newChildren; } // filter out children which do not match filter public void filterChildren(ProfileNodeFilter pnf, int level, Object context) { ArrayList<String> keysToRemove = new ArrayList<String>(); for (String childKey : children.keySet()) { SFNode child = children.get(childKey); boolean acceptNode = pnf.accept(child, level, context); child.filterChildren(pnf, level + 1, context); // first filter out the children if (!acceptNode) { keysToRemove.add(childKey); } } // if we are about to remove this child then we take all its children and fuse them to this node.... for (String key : keysToRemove) { SFNode child = children.get(key); children.remove(key); for (String grandchildKey : child.children.keySet()) { if (children.get(grandchildKey) == null) {/// there is no child with grandchildKey children.put(grandchildKey, child.children.get(grandchildKey)); } else { SFNode newChild = children.get(grandchildKey); newChild.mergeToNode(child.children.get(grandchildKey)); } } } } public void filterUpChildren(ProfileNodeFilter pnf, int level, Object context) { ArrayList<String> keysToRemove = new ArrayList<>(); for (String childKey : children.keySet()) { SFNode child = children.get(childKey); boolean acceptNode = pnf.accept(child, level, context); if (acceptNode) { // child.filterUpChildren(pnf, level+1,context); // first filter out the children } else { keysToRemove.add(childKey); } child.filterUpChildren(pnf, level + 1, context); } // if we are about to remove this child then we take all its children and fuse them to this node.... for (String key : keysToRemove) { SFNode child = children.get(key); children.remove(key); for (String grandchildKey : child.children.keySet()) { if (children.get(grandchildKey) == null) {/// there is no child with grandchildKey children.put(grandchildKey, child.children.get(grandchildKey)); } else { SFNode newChild = children.get(grandchildKey); newChild.mergeToNode(child.children.get(grandchildKey)); } } } } public void filterDownChildren(ProfileNodeFilter pnf, int level, Object context) { ArrayList<String> keysToRemove = new ArrayList<String>(); for (String childKey : children.keySet()) { SFNode child = children.get(childKey); boolean acceptNode = pnf.accept(child, level, context); //if acceptNode==true we want to save it and all childern if (!acceptNode) { keysToRemove.add(childKey); child.filterDownChildren(pnf, level + 1, context); // first filter out the children } } // if we are about to remove this child then we take all its children and fuse them to this node.... for (String key : keysToRemove) { SFNode child = children.get(key); children.remove(key); for (String grandchildKey : child.children.keySet()) { if (children.get(grandchildKey) == null) {/// there is no child with grandchildKey children.put(grandchildKey, child.children.get(grandchildKey)); } else { SFNode newChild = children.get(grandchildKey); newChild.mergeToNode(child.children.get(grandchildKey)); } } } } public void mergeToNode(SFNode that) { this.count += that.count; //merge children for (String key : that.children.keySet()) { SFNode thisChild = this.children.get(key); SFNode thatChild = that.children.get(key); if (thisChild != null) { thisChild.mergeToNode(thatChild); } else { children.put(key, thatChild.deepClone()); } } } public SFNode deepClone() { SFNode clone = new SFNode(); clone.count = this.count; clone.sf = this.sf; for (String key : children.keySet()) { clone.children.put(key, children.get(key).deepClone()); } return clone; } public String getStackFrame() { return sf; } public void setStackFrame(String sf) { this.sf = sf; } public int getNumChildren() { return children.size(); } public int getCount() { return count; } public boolean isNull() { return sf == null; } public boolean contains(String exp) { if (sf.contains(exp)) return true; for (String key : children.keySet()) { SFNode thisChild = this.children.get(key); if (thisChild.sf.contains(exp)) return true; else return thisChild.contains(exp); } return false; } public int depthBelow() { int depthB = Integer.MAX_VALUE; for (SFNode child : children.values()) { int depthBnew = child.depthBelow() + 1; if (depthBnew < depthB) depthB = depthBnew; } if (depthB == Integer.MAX_VALUE) depthB = 0; return depthB; } }