001/*
002 * (C) Copyright 2017 Nuxeo (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Nuxeo - initial API and implementation
018 */
019package org.nuxeo.runtime.util;
020
021import java.text.DecimalFormat;
022import java.util.HashMap;
023import java.util.Map;
024import java.util.concurrent.TimeUnit;
025
026/**
027 * Usage:
028 *
029 * <pre>
030 * <code>
031 * Watch w = new Watch()
032 * w.start();
033 * ...
034 * w.start("interval-name")
035 * w.stop("interval-name")
036 * ..
037 * w.stop()
038 * </code>
039 * </pre>
040 *
041 * @author bogdan
042 * @since 9.2
043 */
044public class Watch {
045
046    public final TimeInterval total = new TimeInterval("total");
047
048    public final Map<String, TimeInterval> intervals = new HashMap<>();
049
050    public Watch start() {
051        // reset if needed
052        total.t0 = 0;
053        total.t1 = 0;
054        intervals.clear();
055
056        total.start();
057        return this;
058    }
059
060    public Watch stop() {
061        total.stop();
062        return this;
063    }
064
065    public Watch start(String interval) {
066        TimeInterval ti = intervals.get(interval);
067        if (ti == null) {
068            ti = new TimeInterval(interval);
069            intervals.put(interval, ti);
070        }
071        ti.start();
072        return this;
073    }
074
075    public Watch stop(String interval) {
076        TimeInterval ti = intervals.get(interval);
077        if (ti != null) {
078            ti.stop();
079        }
080        return this;
081    }
082
083    public long elapsed(TimeUnit unit) {
084        return total.elapsed(unit);
085    }
086
087    public long elapsed(String name, TimeUnit unit) {
088        TimeInterval ti = intervals.get(name);
089        if (ti != null) {
090            return total.elapsed(unit);
091        }
092        return 0;
093    }
094
095    public TimeInterval getTotal() {
096        return total;
097    }
098
099    public TimeInterval[] getIntervals() {
100        return intervals.values().toArray(new TimeInterval[intervals.size()]);
101    }
102
103    public static class TimeInterval implements Comparable<TimeInterval> {
104
105        public String name;
106
107        public long t0;
108
109        public long t1;
110
111        public TimeInterval(String name) {
112            this.name = name;
113        }
114
115        /**
116         * Elapsed time in nano seconds
117         */
118        public long elapsed() {
119            return t1 - t0;
120        }
121
122        public long elapsed(TimeUnit unit) {
123            return unit.convert(t1 - t0, TimeUnit.NANOSECONDS);
124        }
125
126        protected void start() {
127            this.t0 = System.nanoTime();
128        }
129
130        protected void stop() {
131            this.t1 = System.nanoTime();
132        }
133
134        @Override
135        public int compareTo(TimeInterval o) {
136            long dt = (t1 - t0) - (o.t1 - o.t0); // this may be out of range for an int
137            return dt < 0 ? -1 : (dt > 0 ? 1 : 0);
138        }
139
140        public String formatSeconds() {
141            return new DecimalFormat("0.000").format(((double) this.t1 - this.t0) / 1000000000);
142        }
143
144        @Override
145        public String toString() {
146            return name + ": " + this.formatSeconds() + " sec.";
147        }
148
149    }
150
151}