org.eclipse.jgit.lib.BatchingProgressMonitor.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.jgit.lib.BatchingProgressMonitor.java

Source

/*
 * Copyright (C) 2008-2011, Google Inc.
 * and other copyright owners as documented in the project's IP log.
 *
 * This program and the accompanying materials are made available
 * under the terms of the Eclipse Distribution License v1.0 which
 * accompanies this distribution, is reproduced below, and is
 * available at http://www.eclipse.org/org/documents/edl-v10.php
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or
 * without modification, are permitted provided that the following
 * conditions are met:
 *
 * - Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above
 *   copyright notice, this list of conditions and the following
 *   disclaimer in the documentation and/or other materials provided
 *   with the distribution.
 *
 * - Neither the name of the Eclipse Foundation, Inc. nor the
 *   names of its contributors may be used to endorse or promote
 *   products derived from this software without specific prior
 *   written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package org.eclipse.jgit.lib;

import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

import org.eclipse.jgit.lib.internal.WorkQueue;

/**
 * ProgressMonitor that batches update events.
 */
public abstract class BatchingProgressMonitor implements ProgressMonitor {
    private long delayStartTime;

    private TimeUnit delayStartUnit = TimeUnit.MILLISECONDS;

    private Task task;

    /**
     * Set an optional delay before the first output.
     *
     * @param time
     *            how long to wait before output. If 0 output begins on the
     *            first {@link #update(int)} call.
     * @param unit
     *            time unit of {@code time}.
     */
    public void setDelayStart(long time, TimeUnit unit) {
        delayStartTime = time;
        delayStartUnit = unit;
    }

    /** {@inheritDoc} */
    @Override
    public void start(int totalTasks) {
        // Ignore the number of tasks.
    }

    /** {@inheritDoc} */
    @Override
    public void beginTask(String title, int work) {
        endTask();
        task = new Task(title, work);
        if (delayStartTime != 0)
            task.delay(delayStartTime, delayStartUnit);
    }

    /** {@inheritDoc} */
    @Override
    public void update(int completed) {
        if (task != null)
            task.update(this, completed);
    }

    /** {@inheritDoc} */
    @Override
    public void endTask() {
        if (task != null) {
            task.end(this);
            task = null;
        }
    }

    /** {@inheritDoc} */
    @Override
    public boolean isCancelled() {
        return false;
    }

    /**
     * Update the progress monitor if the total work isn't known,
     *
     * @param taskName
     *            name of the task.
     * @param workCurr
     *            number of units already completed.
     */
    protected abstract void onUpdate(String taskName, int workCurr);

    /**
     * Finish the progress monitor when the total wasn't known in advance.
     *
     * @param taskName
     *            name of the task.
     * @param workCurr
     *            total number of units processed.
     */
    protected abstract void onEndTask(String taskName, int workCurr);

    /**
     * Update the progress monitor when the total is known in advance.
     *
     * @param taskName
     *            name of the task.
     * @param workCurr
     *            number of units already completed.
     * @param workTotal
     *            estimated number of units to process.
     * @param percentDone
     *            {@code workCurr * 100 / workTotal}.
     */
    protected abstract void onUpdate(String taskName, int workCurr, int workTotal, int percentDone);

    /**
     * Finish the progress monitor when the total is known in advance.
     *
     * @param taskName
     *            name of the task.
     * @param workCurr
     *            total number of units processed.
     * @param workTotal
     *            estimated number of units to process.
     * @param percentDone
     *            {@code workCurr * 100 / workTotal}.
     */
    protected abstract void onEndTask(String taskName, int workCurr, int workTotal, int percentDone);

    private static class Task implements Runnable {
        /** Title of the current task. */
        private final String taskName;

        /** Number of work units, or {@link ProgressMonitor#UNKNOWN}. */
        private final int totalWork;

        /** True when timer expires and output should occur on next update. */
        private volatile boolean display;

        /** Scheduled timer, supporting cancellation if task ends early. */
        private Future<?> timerFuture;

        /** True if the task has displayed anything. */
        private boolean output;

        /** Number of work units already completed. */
        private int lastWork;

        /** Percentage of {@link #totalWork} that is done. */
        private int lastPercent;

        Task(String taskName, int totalWork) {
            this.taskName = taskName;
            this.totalWork = totalWork;
            this.display = true;
        }

        void delay(long time, TimeUnit unit) {
            display = false;
            timerFuture = WorkQueue.getExecutor().schedule(this, time, unit);
        }

        @Override
        public void run() {
            display = true;
        }

        void update(BatchingProgressMonitor pm, int completed) {
            lastWork += completed;

            if (totalWork == UNKNOWN) {
                // Only display once per second, as the alarm fires.
                if (display) {
                    pm.onUpdate(taskName, lastWork);
                    output = true;
                    restartTimer();
                }
            } else {
                // Display once per second or when 1% is done.
                int currPercent = lastWork * 100 / totalWork;
                if (display) {
                    pm.onUpdate(taskName, lastWork, totalWork, currPercent);
                    output = true;
                    restartTimer();
                    lastPercent = currPercent;
                } else if (currPercent != lastPercent) {
                    pm.onUpdate(taskName, lastWork, totalWork, currPercent);
                    output = true;
                    lastPercent = currPercent;
                }
            }
        }

        private void restartTimer() {
            display = false;
            timerFuture = WorkQueue.getExecutor().schedule(this, 1, TimeUnit.SECONDS);
        }

        void end(BatchingProgressMonitor pm) {
            if (output) {
                if (totalWork == UNKNOWN) {
                    pm.onEndTask(taskName, lastWork);
                } else {
                    int pDone = lastWork * 100 / totalWork;
                    pm.onEndTask(taskName, lastWork, totalWork, pDone);
                }
            }
            if (timerFuture != null)
                timerFuture.cancel(false /* no interrupt */);
        }
    }
}