001package net.sf.jkniv.experimental;
002
003import java.text.DecimalFormat;
004import java.util.concurrent.ConcurrentHashMap;
005import java.util.concurrent.atomic.AtomicLong;
006
007public class Chrono implements Comparable<Chrono>
008{
009    private static final DecimalFormat             df = new DecimalFormat("0.00");
010    private String                                 name;
011    private String                                 status;
012    private AtomicLong                             milliseconds;
013    //private AtomicLong   startTime;
014    private long                                   times;
015    private long                                   min;
016    private long                                   max;
017    private ConcurrentHashMap<String, ChronoSlice> chronoSlices;
018    
019    Chrono(String name)
020    {
021        this.name = name;
022        this.chronoSlices = new ConcurrentHashMap<String, ChronoSlice>();
023        reset();
024    }
025    
026    private void reset()
027    {
028        this.times = 0L;
029        this.milliseconds = new AtomicLong();
030        this.min = Long.MAX_VALUE;
031        this.max = 0L;
032        this.status = "pause";
033        chronoSlices.clear();
034    }
035    
036    protected void clear()
037    {
038        TimerKeeper.clear();
039        reset();
040    }
041    
042    long changeTimer(boolean pause)
043    {
044        if (pause)
045        {
046            //System.out.println("pause ["+name+"] thread: " + Thread.currentThread().getName());
047            ChronoSlice chronoSlice = chronoSlices.remove(Thread.currentThread().getName());
048            if (chronoSlice != null)
049            {
050                chronoSlice.changeTimer(pause);
051                if (chronoSlice.milliseconds > 0)
052                {
053                    long sliceTime = chronoSlice.milliseconds;
054                    this.milliseconds.addAndGet(sliceTime);
055                    if (this.min > sliceTime)
056                        this.min = sliceTime;
057                    if (this.max < sliceTime)
058                        this.max = sliceTime;
059                }
060                if (chronoSlices.size() == 0)
061                    this.status = "paused";
062            }
063        }
064        else
065        {
066            ChronoSlice chronoSlice = chronoSlices.get(Thread.currentThread().getName());
067            if (chronoSlice == null)
068            {
069                chronoSlice = new ChronoSlice();
070                chronoSlices.put(Thread.currentThread().getName(), chronoSlice);
071            }
072            this.times++;
073            this.status = "running";
074            long sliceTime = chronoSlice.changeTimer(pause);
075            
076            if (sliceTime > 0L)
077            {
078                this.milliseconds.addAndGet(sliceTime);
079            }
080        }
081        return this.milliseconds.get();
082    }
083    
084    public double avg()
085    {
086        return times > 0 ? (double) (this.milliseconds.get() / times) : 0D;
087    }
088    
089    public long times()
090    {
091        return this.times;
092    }
093    
094    public long time()
095    {
096        return this.milliseconds.get();
097    }
098    
099    public long max()
100    {
101        return this.max;
102    }
103    
104    public long min()
105    {
106        return this.min;
107    }
108    
109    public String getTime()
110    {
111        String value = milliseconds + " ms";
112        double seconds = milliseconds.get() / 1000L;
113        if (seconds > 1)
114            value = df.format(seconds) + " seg";
115        double minutes = seconds / 60L;
116        if (minutes > 1)
117            value = df.format(minutes) + " min";
118        double hours = minutes / 60L;
119        if (hours > 1)
120            value = df.format(hours) + " h";
121        return value;
122    }
123    
124    @Override
125    public String toString()
126    {
127        return "Chrono [name=" + name + ", milliseconds=" + milliseconds.get() + ", times=" + times + ", status="
128                + status + ", min=" + min + ", max=" + max + ", avg=" + avg() + "]";
129    }
130    
131    @Override
132    public int compareTo(Chrono o)
133    {
134        if (o == null)
135            return -1;
136        else if (milliseconds.get() < o.milliseconds.get())
137            return 1;
138        else if (milliseconds.get() > o.milliseconds.get())
139            return -1;
140        else
141            return 0;
142    }
143    
144    /**
145     * slice time for threads for same tag name
146     */
147    class ChronoSlice
148    {
149        private long milliseconds;
150        private long startTime;
151        
152        ChronoSlice()
153        {
154            this.milliseconds = 0L;
155            this.startTime = 0L;
156        }
157        
158        long changeTimer(boolean pause)
159        {
160            if (pause)
161            {
162                long sliceTime = 0;
163                if (startTime > 0)
164                {
165                    sliceTime = System.currentTimeMillis() - startTime;
166                    this.milliseconds += sliceTime;
167                }
168                startTime = 0;
169            }
170            else
171            {
172                if (startTime > 0L)
173                {
174                    long sliceTime = System.currentTimeMillis() - startTime;
175                    this.milliseconds += sliceTime;
176                }
177                this.startTime = System.currentTimeMillis();
178            }
179            return this.milliseconds;
180        }
181        
182    }
183    
184}