Home | History | Annotate | Download | only in util
      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<Object, String>();
     37 
     38     /**
     39      * Add object (i.e. added to cache)
     40      *
     41      * @param o
     42      */
     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      */
     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      */
     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
     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      */
     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      */
    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      */
    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      */
    123     public boolean isEmpty() {
    124         return stacks.isEmpty();
    125     }
    126 
    127     /**
    128      * Iterate over held objects.
    129      */
    130     @Override
    131     public Iterator<Object> iterator() {
    132         return stacks.keySet().iterator();
    133     }
    134 
    135     /**
    136      * Example use.
    137      *
    138      * @param args
    139      *            ignored
    140      */
    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