001package org.javasimon;
002
003import java.util.Iterator;
004import java.util.Map;
005
006import org.javasimon.clock.SimonClock;
007import org.javasimon.utils.SimonUtils;
008
009/**
010 * Represents single time split - one Stopwatch measurement. Object is obtained by {@link org.javasimon.Stopwatch#start()}
011 * and the measurement is ended using {@link #stop()} method on this object. Split will return 0 as the result
012 * if the related Stopwatch was disabled when the Split was obtained. The Split can be stopped in any other thread.
013 * Split measures real time (based on {@link org.javasimon.clock.SimonClock#nanoTime()}), it does not measure CPU time. Split can be garbage collected
014 * and no resource leak occurs if it is not stopped, however Stopwatch's active counter ({@link org.javasimon.Stopwatch#getActive()})
015 * will be stay incremented.
016 * <p/>
017 * Split can never be running ({@link #isRunning()}) if it is disabled. Enabled split is running until it is stopped.
018 * Stopped split (not running) will never again be running. Split never changes enabled flag after creation.
019 * <p/>
020 * Split implements {@link java.lang.AutoCloseable} hence it can be used in try-with-resource construction.
021 *
022 * @author <a href="mailto:virgo47@gmail.com">Richard "Virgo" Richter</a>
023 * @see Stopwatch
024 */
025public final class Split implements HasAttributes, AutoCloseable {
026
027        /** Disabled split (implies not running) for cases where monitoring is disabled and {@code null} value is not an option. */
028        public static final Split DISABLED = new Split();
029
030        /** Attribute name under which effectively used stopwatch is stored if the split was stopped with {@link #stop(String)}. */
031        public static final String ATTR_EFFECTIVE_STOPWATCH = "effective-stopwatch";
032
033        private volatile Stopwatch stopwatch;
034        private final boolean enabled;
035        private final SimonClock clock;
036        private volatile boolean running;
037
038        private volatile long start;
039        private volatile long total;
040
041        private AttributesSupport attributesSupport = new AttributesSupport();
042
043        private Split() {
044                enabled = false;
045                clock = null;
046        }
047
048        private Split(boolean enabled, SimonClock clock) {
049                this.enabled = enabled;
050                this.clock = clock;
051                start = clock.nanoTime();
052        }
053
054        /**
055         * Creates a new Split for an enabled Stopwatch with a specific timestamp in nanoseconds - <b>called internally only</b>.
056         *
057         * @param stopwatch owning Stopwatch (enabled)
058         * @param clock Clock for this Split
059         * @param start start timestamp in nanoseconds
060         */
061        Split(Stopwatch stopwatch, SimonClock clock, long start) {
062                assert start > 0 : "start ns value should not be 0 in this constructor!";
063
064                this.stopwatch = stopwatch;
065                this.start = start;
066                this.clock = clock;
067                enabled = true;
068                running = true;
069        }
070
071        /**
072         * Creates a new Split for a disabled Stopwatch - <b>called internally only</b>.
073         *
074         * @param stopwatch owning Stopwatch (disabled)
075         * @param clock Clock for this Split
076         */
077        Split(Stopwatch stopwatch, SimonClock clock) {
078                assert !(stopwatch.isEnabled()) : "stopwatch must be disabled in this constructor!";
079                this.enabled = false;
080                this.stopwatch = stopwatch;
081                this.clock = clock;
082        }
083
084        /**
085         * Creates a new Split for direct use without {@link Stopwatch} ("anonymous split") based on specified {@link SimonClock}.
086         * Stop will not update any Stopwatch, value can be added to any chosen Stopwatch using
087         * {@link Stopwatch#addSplit(Split)} - even in conjunction with {@link #stop()} like this:
088         * <p/>
089         * <pre>Split split = Split.start();
090         * ...
091         * stopwatch.addSplit(split.stop());</pre>
092         * <p/>
093         * If the split is not needed afterwards (subject to garbage collection) calling {@link #stop()} is not necessary:
094         * <p/>
095         * <pre>Split split = Split.start();
096         * ...
097         * stopwatch.addSplit(split);</pre>
098         * <p/>
099         * No callbacks are called for this Split.
100         *
101         * @param clock Clock for this Split
102         * @since 3.5
103         */
104        public static Split start(SimonClock clock) {
105                Split split = new Split(true, clock);
106                split.running = true;
107                return split;
108        }
109
110        /**
111         * Creates a new Split for direct use without {@link Stopwatch} ("anonymous split") based on system time.
112         * Equivalent of {@code Split.start(Clock.SYSTEM)}.
113         *
114         * @see #start(SimonClock)
115         * @since 3.4
116         */
117        public static Split start() {
118                return start(SimonClock.SYSTEM);
119        }
120
121        /**
122         * Creates simulated non-running Split that took specific time in nanos.
123         * No callbacks are called for this Split. Start of this Split is set {@code clock.nanoTime()}.
124         *
125         * @param nanos Split's total time in nanos
126         * @param clock Clock for this Split
127         * @return created Split
128         * @since 3.5
129         */
130        public static Split create(long nanos, SimonClock clock) {
131                Split split = new Split(false, clock);
132                split.total = nanos;
133                return split;
134        }
135
136        /**
137         * Creates simulated non-running Split that took specific time in nanos.
138         * No callbacks are called for this Split. Start of this Split is based on {@link SimonClock#SYSTEM}.
139         *
140         * @param nanos Split's total time in nanos
141         * @return created Split
142         * @see #create(long, SimonClock)
143         * @since 3.5
144         */
145        public static Split create(long nanos) {
146                return create(nanos, SimonClock.SYSTEM);
147        }
148
149        /**
150         * Returns the stopwatch that this split is running for. May be {@code null} for anonymous splits (directly created).
151         *
152         * @return owning stopwatch, may return {@code null}
153         */
154        public Stopwatch getStopwatch() {
155                return stopwatch;
156        }
157
158        /**
159         * Stops the split, updates the stopwatch and returns this. Subsequent calls do not change the state of the split.
160         *
161         * @return this split object
162         * @since 3.1 - previously returned split time in ns, call additional {@link #runningFor()} for the same result
163         */
164        public Split stop() {
165                return stop(null);
166        }
167
168        /**
169         * Stops the split, updates the sub-stopwatch specified by parameter and returns this. Active counter of the original stopwatch
170         * is decreased as expected. Subsequent calls do not change the state of the split.
171         * <p/>
172         * <b>Important:</b> While {@link #stop()} (or this stop with {@code null} argument)
173         * results in {@link org.javasimon.callback.Callback#onStopwatchStop(Split, StopwatchSample)} callback method being invoked,
174         * if sub-simon is affected then {@link org.javasimon.callback.Callback#onStopwatchAdd(Stopwatch, Split, StopwatchSample)}
175         * is called instead.
176         * <p/>
177         * If the split was obtained from disabled Stopwatch, this method does not update sub-simon even if it is enabled, because
178         * split itself is disabled as well. If split is enabled, but sub-simon is disabled, the latter is not updated.
179         *
180         * @param subSimon name of the sub-stopwatch (hierarchy delimiter is added automatically) - if {@code null}
181         * it behaves exactly like {@link #stop()}
182         * @return this split object
183         * @since 3.4
184         */
185        public Split stop(String subSimon) {
186                if (!running) {
187                        return this;
188                }
189                running = false;
190                long nowNanos = clock.nanoTime();
191                total = nowNanos - start; // we update total before calling the stop so that callbacks can use it
192                if (stopwatch != null) {
193                        ((StopwatchImpl) stopwatch).stop(this, start, nowNanos, subSimon);
194                }
195                return this;
196        }
197
198        /**
199         * Returns the current running nano-time from the start to the method call or the total split time
200         * if the Split has been stopped already.
201         *
202         * @return current running nano-time of the split
203         */
204        public long runningFor() {
205                if (!running) {
206                        return total;
207                }
208                if (enabled) {
209                        return clock.nanoTime() - start;
210                }
211                return 0;
212        }
213
214        /**
215         * Returns printable form of how long this split was running for.
216         *
217         * @return short information about the Split  time as a human readable string
218         * @since 2.2
219         */
220        public String presentRunningFor() {
221                return SimonUtils.presentNanoTime(runningFor());
222        }
223
224        /**
225         * Returns true if this split was created from enabled Simon or via {@link #start()}.
226         *
227         * @return true if this split was created from enabled Simon
228         */
229        public boolean isEnabled() {
230                return enabled;
231        }
232
233        /**
234         * Returns true if this split is still running ({@link #stop()} has not been called yet).
235         * Returns false for disabled Split.
236         *
237         * @return true if this split is still running
238         * @since 3.1.0
239         */
240        public boolean isRunning() {
241                return running;
242        }
243
244        /**
245         * Returns start nano timer value or 0 if the Split is not enabled (started for disabled Stopwatch).
246         *
247         * @return nano timer value when the Split was started or 0 if the Split is not enabled
248         * @since 3.1
249         */
250        public long getStart() {
251                return start;
252        }
253
254        /**
255         * Returns millis timestamp when this Split was started - or 0 if the Split is not enabled (started for disabled Stopwatch).
256         *
257         * @return nano timer value when the Split was started or 0 if the Split is not enabled
258         * @since 3.1
259         */
260        public long getStartMillis() {
261                return clock != null ? clock.millisForNano(start) : 0;
262        }
263
264        /**
265         * Stores an attribute in this Split. Attributes can be used to store any custom objects.
266         *
267         * @param name a String specifying the name of the attribute
268         * @param value the Object to be stored
269         * @since 2.3
270         */
271        @Override
272        public void setAttribute(String name, Object value) {
273                attributesSupport.setAttribute(name, value);
274        }
275
276        /**
277         * Returns the value of the named attribute as an Object, or {@code null} if no attribute of
278         * the given name exists.
279         *
280         * @param name a String specifying the name of the attribute
281         * @return an Object containing the value of the attribute, or {@code null} if the attribute does not exist
282         * @since 2.3
283         */
284        @Override
285        public Object getAttribute(String name) {
286                return attributesSupport.getAttribute(name);
287        }
288
289        /**
290         * Returns the value of the named attribute typed to the specified class, or {@code null} if no attribute of
291         * the given name exists.
292         *
293         * @param name a String specifying the name of the attribute
294         * @return the value of the attribute typed to T, or {@code null} if the attribute does not exist
295         * @since 3.4
296         */
297        @SuppressWarnings("unchecked")
298        @Override
299        public <T> T getAttribute(String name, Class<T> clazz) {
300                return attributesSupport.getAttribute(name, clazz);
301        }
302
303        /**
304         * Removes an attribute from this Split.
305         *
306         * @param name a String specifying the name of the attribute to remove
307         * @since 2.3
308         */
309        @Override
310        public void removeAttribute(String name) {
311                attributesSupport.removeAttribute(name);
312        }
313
314        /**
315         * Returns an Iterator containing the names of the attributes available to this Split.
316         * This method returns an empty Iterator if the Split has no attributes available to it.
317         *
318         * @return an Iterator of strings containing the names of the Split's attributes
319         * @since 2.3
320         */
321        @Override
322        public Iterator<String> getAttributeNames() {
323                return attributesSupport.getAttributeNames();
324        }
325
326        @Override
327        public Map<String, Object> getCopyAsSortedMap() {
328                return attributesSupport.getCopyAsSortedMap();
329        }
330
331        /**
332         * Allows to use Split as a resource in try-with-resource construct. Calls {@link #stop()} internally.
333         *
334         * @since 4.0
335         */
336        @Override
337        public void close() {
338                stop();
339        }
340
341        /**
342         * Returns information about this Split, if it's running, name of the related Stopwatch and split's time.
343         *
344         * @return information about the Split as a human readable string
345         */
346        @Override
347        public String toString() {
348                if (!enabled) {
349                        return "Split created from disabled Stopwatch";
350                }
351                if (running) {
352                        return "Running split" + (stopwatch != null ? " for Stopwatch '" + stopwatch.getName() + "': " : ": ") + SimonUtils.presentNanoTime(runningFor());
353                }
354                return "Stopped split" + (stopwatch != null ? " for Stopwatch '" + stopwatch.getName() + "': " : ": ") + SimonUtils.presentNanoTime(total);
355        }
356}