/* * JFileSync * Copyright (C) 2002-2007, Jens Heidrich * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301, USA */ package jfs.sync; import java.util.ArrayList; import java.util.List; import jfs.JFileSync; import jfs.conf.JFSConst; import jfs.conf.JFSSettings; import jfs.conf.JFSText; /** * Visits selected steps of the file comparison and synchronization algorithm in order to compute the state of the * progression. * * @author Jens Heidrich * @version $Id: JFSProgress.java,v 1.24 2007/07/20 12:27:52 heidrich Exp $ */ public final class JFSProgress { /** * Stores the only instance of the class. * * SingletonHolder is loaded on the first execution of JFSProgress.getInstance() * or the first access to SingletonHolder.INSTANCE, not before. */ private static class SingletonHolder { public static final JFSProgress INSTANCE = new JFSProgress(); } /** * The current activity */ private ProgressActivity activity = ProgressActivity.INITIALIZATION; /** * The current state */ private ProgressState state = ProgressState.ACTIVE; /** * Duration to compute the table. */ private long duration = 0; /** * Starting time. */ private long startTime = 0; /** * Determines whether the algorithm is canceled or not. */ private boolean canceled = false; /** * Vector with all oberservers of the alogorithm's progress. */ private final List<JFSProgressObserver> observers = new ArrayList<>(); /** * The time when the observers were updated last. */ private long updateTime = 0; /** * The different activities performed by the synchronization algorithm. */ public enum ProgressActivity { INITIALIZATION("progress.init.title"), COMPARISON("progress.comparison.title"), SYNCHRONIZATION_DELETE("progress.delete.title"), SYNCHRONIZATION_COPY("progress.copy.title"); private final String name; ProgressActivity(String name) { this.name = name; } public String getName() { return name; } } /** * The states of the progress object. */ public enum ProgressState { PREPARATION, ACTIVE, DONE } /** * Creates a new progress object. */ protected JFSProgress() { // avoid instanciation from outside } /** * Returns the reference of the only instance. * * @return The only instance. */ public static JFSProgress getInstance() { return SingletonHolder.INSTANCE; } /** * Prepares starting a progress computation for a certain type of activity. * * @param activity * The type of activity to start. */ void prepare(ProgressActivity activity) { this.activity = activity; this.state = ProgressState.PREPARATION; canceled = false; updateTime = 0; duration = 0; update(); } /** * Starts a progress computation. */ void start() { state = ProgressState.ACTIVE; startTime = System.currentTimeMillis(); update(); } /** * Ends a progress computation. */ void end() { state = ProgressState.DONE; duration = System.currentTimeMillis()-startTime; update(); if (JFSSettings.getInstance().isDebug()) { System.out.println("..."+duration+"ms"); } } /** * @return Returns the current activity. */ public ProgressActivity getActivity() { return activity; } /** * @return Returns the current state of the current activity. */ public ProgressState getState() { return state; } /** * @return Returns duration for the last performed activity. */ public String getDuration() { return getTime(duration); } /** * @return Returns the predicted remaining time for completing the currently performed algorithm. */ public String getRemainingTime() { int ratio = getCompletionRatio(); if (ratio>0) { long time = (System.currentTimeMillis()-startTime)*(100-ratio)/ratio; return getTime(time); } return ""; } /** * @return Returns the completion ratio in percent between 0% and 100%. */ public int getCompletionRatio() { JFSComparisonMonitor comparison = JFSComparisonMonitor.getInstance(); JFSDeleteMonitor delete = JFSDeleteMonitor.getInstance(); JFSCopyMonitor copy = JFSCopyMonitor.getInstance(); if (activity==ProgressActivity.COMPARISON) { return comparison.getRatio(); } else if (activity==ProgressActivity.SYNCHRONIZATION_DELETE) { return delete.getRatio(); } else if (activity==ProgressActivity.SYNCHRONIZATION_COPY) { return copy.getRatio(); } else { return 0; } } /** * This method is called if an object wants to cancel the algorithm. After this request the algorithm stops as soon * as possible at predefined milestones. */ public void cancel() { canceled = true; } /** * Determines whether the algorithm is canceled or not. If so, the algorithm stops as soon as possible at the next * predefined milestone. * * @return True if and only if the algorithm was cancelled. */ public boolean isCanceled() { return canceled; } /** * Attaches an additional observer. * * @param observer * The new observer. */ public void attach(JFSProgressObserver observer) { observers.add(observer); } /** * Detaches an existing observer. * * @param observer * An old observer. */ public void detach(JFSProgressObserver observer) { observers.remove(observer); } /** * Sends a message to all existing observers that the algorithm's state was updated, if and only if a minimum time * period between two subsequent updates is gone. */ void fireUpdate() { if ((System.currentTimeMillis()-updateTime)>=JFSConst.PROGRESS_UPDATE) { updateTime = System.currentTimeMillis(); update(); } } /** * Updates the current state of the algorithm for all existing observers. */ private void update() { // Wait if debugging is enabled and output progress information: if (JFSSettings.getInstance().isDebug()) { JFileSync.busyWait(1000); System.out.println(getActivity()+", "+getState()+", "+getCompletionRatio()+"%"); } for (JFSProgressObserver po : observers) { po.update(this); } } /** * Returns the formatted time in hours, minutes, and seconds for a given time distance. * * @param time * The time in ms to format. * @return The formatted time. */ private String getTime(long time) { JFSText t = JFSText.getInstance(); String s = ""; long millis = time%1000; long seconds = (time/1000)%60; long minutes = (time/60000)%60; long hours = time/3600000; if (millis>=500) { seconds++; } if (hours>0) { s += hours+t.get("general.hours"); } if (minutes>0) { s += minutes+t.get("general.minutes"); } s += seconds+t.get("general.seconds"); return s; } }