/* * Copyright 2010 The Rabbit Eclipse Plug-in Project * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package rabbit.tracking.internal.util; import static com.google.common.base.Preconditions.checkArgument; import com.google.common.base.Objects; import java.util.Observable; import javax.annotation.Nullable; /** * A utility class for recording collapsed time. * <p> * This class extends {@link Observable}, and observers are notified when the * value of {@link #getLastRecord()} changes, the argument object passed to the * observers will be the new value of {@link #getLastRecord()}. * <p> * This class is thread safe. * * @param <T> * The user data type, the user data is an optional data object * associate with each recording. */ public final class Recorder<T> extends Observable { /** * Represents a recorded session. */ public static final class Record<T> { private final long startTimeMillis; private final long endTimeMillis; private final T userData; /** * Constructs a new record. * * @param startMillis * The start time in milliseconds. * @param endMillis * The end time in milliseconds. * @param data * The optional user data. * @throws IllegalArgumentException * If {@link #endTimeMillis} < {@link #startTimeMillis}. */ public Record(long startMillis, long endMillis, @Nullable T data) { checkArgument(endMillis >= startMillis); startTimeMillis = startMillis; endTimeMillis = endMillis; userData = data; } /** * Gets the start time of this record in milliseconds. * * @return The start time of this record in milliseconds. */ public long getStartTimeMillis() { return startTimeMillis; } /** * Gets the end time of this record in milliseconds. * * @return The end time of this record in milliseconds. */ public long getEndTimeMillis() { return endTimeMillis; } /** * Gets the associated user data. * * @return The user data, or null if none. */ public T getUserData() { return userData; } } /** * Start time of a recording session, in milliseconds. */ private long start; /** * The associated user data for the current session. */ private T data; private Record<T> record; private boolean running; /** * Constructs a new recorder. */ public Recorder() { } /** * Gets the currently associated user data. * * @return The user data, or null if none. */ public synchronized T getUserData() { return data; } /** * Starts recording on the given user object. Calling this method when the * recorder is already running has no effects. If this recorder is recording * and the given user object is different from the one that is currently * referenced, {@link #stop()} will be called then a new session will be * started. * * @param userData * The optional user object for this record session. * @see #isRecording() */ public synchronized void start(@Nullable T userData) { if (isRecording()) { if (!Objects.equal(data, userData)) { stop(); } else { return; } } start = System.currentTimeMillis(); data = userData; running = true; } /** * Starts recording, same as calling start(null). Calling this method when the * recorder is already running has no effects. * * @see #isRecording() */ public synchronized void start() { start(null); } /** * Stops recording. Calling this method when the recorder is not running has * no effects. */ public void stop() { Record<T> r = null; synchronized (this) { if (!isRecording()) { return; } record = new Record<T>(start, System.currentTimeMillis(), data); r = record; running = false; data = null; } setChanged(); notifyObservers(r); } /** * Indicates whether this recorder is currently recording. * * @return True if this recorder is currently recording a session, false * otherwise. */ public synchronized boolean isRecording() { return running; } /** * Gets the last recorded session. Observers of this class will be notified * when the return value of this method changes. * * @return The last recorded session. */ public synchronized Record<T> getLastRecord() { return record; } }