• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  *******************************************************************************
3  * Copyright (C) 2012 International Business Machines Corporation and          *
4  * others. All Rights Reserved.                                                *
5  *******************************************************************************
6  */
7 
8 package org.unicode.cldr.util;
9 
10 import java.util.Hashtable;
11 import java.util.Iterator;
12 import java.util.Map;
13 
14 /**
15  * Debugging utility.
16  *
17  * A StackTracker tracks which stack frame various objects were created from.
18  * For example, call add() and remove() alongside some cache, and then StackTracker's toString() will
19  * print out the stack frame of all adds() not balanced by remove().
20  *
21  * Objects must be Comparable.
22  *
23  * Example use is in the main() at the bottom. Outputs:
24  *
25  * "{StackTracker:
26  * Held Obj #1/2: the
27  * org.unicode.cldr.util.StackTracker.currentStack(StackTracker.java:92)
28  * org.unicode.cldr.util.StackTracker.add(StackTracker.java:34)
29  * org.unicode.cldr.util.StackTracker.main(StackTracker.java:118)
30  * ...}"
31  *
32  * @author srl
33  */
34 @CLDRTool(alias = "test.stacktracker", description = "Test for StackTracker", hidden = "test")
35 public class StackTracker implements Iterable<Object> {
36     private Hashtable<Object, String> stacks = new Hashtable<>();
37 
38     /**
39      * Add object (i.e. added to cache)
40      *
41      * @param o
42      */
add(Object o)43     public void add(Object o) {
44         String stack = currentStack();
45         stacks.put(o, stack);
46     }
47 
48     /**
49      * remove obj (i.e. removed from cache)
50      *
51      * @param o
52      */
remove(Object o)53     public void remove(Object o) {
54         stacks.remove(o);
55     }
56 
57     /**
58      * internal - convert a stack to string
59      *
60      * @param stackTrace
61      * @param skip
62      *            start at this index (skip the top stuff)
63      * @return
64      */
stackToString(StackTraceElement[] stackTrace, int skip)65     public static String stackToString(StackTraceElement[] stackTrace, int skip) {
66         StringBuffer sb = new StringBuffer();
67         for (int i = skip; i < stackTrace.length; i++) {
68             sb.append(stackTrace[i].toString() + "\n");
69         }
70         return sb.toString();
71     }
72 
73     /**
74      * Get this tracker as a string. Prints any leaked objects, and the stack frame of where they were constructed.
75      */
76     @Override
toString()77     public String toString() {
78         if (stacks.isEmpty()) {
79             return "{StackTracker: empty}";
80         }
81         StringBuffer sb = new StringBuffer();
82 
83         sb.append("{StackTracker:\n");
84         int n = 0;
85         for (Map.Entry<Object, String> e : stacks.entrySet()) {
86             sb.append("Held Obj #" + (++n) + "/" + stacks.size() + ": " + e.getKey() + "\n");
87             sb.append(e.getValue() + "\n");
88         }
89         sb.append("}");
90         return sb.toString();
91     }
92 
93     /**
94      * Purges all held objects.
95      */
clear()96     public void clear() {
97         stacks.clear();
98     }
99 
100     /**
101      * Convenience function, gets the current stack trace.
102      *
103      * @return current stack trace
104      */
currentStack()105     public static String currentStack() {
106         return stackToString(Thread.currentThread().getStackTrace(), 2);
107     }
108 
109     /**
110      * Convenience function, gets the current element
111      *
112      * @param stacks
113      *            to skip - 0 for immediate caller, 1, etc
114      */
currentElement(int skip)115     public static StackTraceElement currentElement(int skip) {
116         return Thread.currentThread().getStackTrace()[3 + skip];
117     }
118 
119     /**
120      *
121      * @return true if there are no held objects
122      */
isEmpty()123     public boolean isEmpty() {
124         return stacks.isEmpty();
125     }
126 
127     /**
128      * Iterate over held objects.
129      */
130     @Override
iterator()131     public Iterator<Object> iterator() {
132         return stacks.keySet().iterator();
133     }
134 
135     /**
136      * Example use.
137      *
138      * @param args
139      *            ignored
140      */
main(String args[])141     public static void main(String args[]) {
142         StackTracker tracker = new StackTracker();
143         System.out.println("At first: " + tracker);
144 
145         tracker.add("Now");
146         tracker.add("is");
147         tracker.add("the");
148         tracker.add("time");
149         tracker.add("for");
150         tracker.add("time");
151         tracker.remove("Now");
152         tracker.remove("for");
153         tracker.remove("time");
154 
155         // any leaks?
156         System.out.println("At end: " + tracker);
157     }
158 }
159