tikhomirov@148: /*
tikhomirov@425:  * Copyright (c) 2011-2012 TMate Software Ltd
tikhomirov@148:  *  
tikhomirov@148:  * This program is free software; you can redistribute it and/or modify
tikhomirov@148:  * it under the terms of the GNU General Public License as published by
tikhomirov@148:  * the Free Software Foundation; version 2 of the License.
tikhomirov@148:  *
tikhomirov@148:  * This program is distributed in the hope that it will be useful,
tikhomirov@148:  * but WITHOUT ANY WARRANTY; without even the implied warranty of
tikhomirov@148:  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
tikhomirov@148:  * GNU General Public License for more details.
tikhomirov@148:  *
tikhomirov@148:  * For information on how to redistribute this software under
tikhomirov@148:  * the terms of a license other than GNU General Public License
tikhomirov@148:  * contact TMate Software at support@hg4j.com
tikhomirov@148:  */
tikhomirov@148: package org.tmatesoft.hg.util;
tikhomirov@148: 
tikhomirov@148: /**
tikhomirov@148:  * Mix-in to report progress of a long-running operation
tikhomirov@148:  *  
tikhomirov@148:  * @author Artem Tikhomirov
tikhomirov@148:  * @author TMate Software Ltd.
tikhomirov@148:  */
tikhomirov@148: public interface ProgressSupport {
tikhomirov@148: 
tikhomirov@215: 	// -1 for unspecified?
tikhomirov@215: 	public void start(int totalUnits);
tikhomirov@425: 	public void worked(int units); // fraction of totalUnits from #start(int)
tikhomirov@215: 	// XXX have to specify whether PS implementors may expect #done regardless of job completion (i.e. in case of cancellation) 
tikhomirov@148: 	public void done();
tikhomirov@148: 
tikhomirov@148: 	static class Factory {
tikhomirov@148: 
tikhomirov@148: 		/**
tikhomirov@148: 		 * @param target object that might be capable to report progress. Can be null
tikhomirov@148: 		 * @return support object extracted from target or an empty, no-op implementation
tikhomirov@148: 		 */
tikhomirov@148: 		public static ProgressSupport get(Object target) {
tikhomirov@356: 			ProgressSupport ps = Adaptable.Factory.getAdapter(target, ProgressSupport.class, null);
tikhomirov@356: 			if (ps != null) {
tikhomirov@356: 				return ps;
tikhomirov@148: 			}
tikhomirov@148: 			return new ProgressSupport() {
tikhomirov@215: 				public void start(int totalUnits) {
tikhomirov@148: 				}
tikhomirov@148: 				public void worked(int units) {
tikhomirov@148: 				}
tikhomirov@148: 				public void done() {
tikhomirov@148: 				}
tikhomirov@148: 			};
tikhomirov@148: 		}
tikhomirov@148: 	}
tikhomirov@215: 	
tikhomirov@215: 	class Sub implements ProgressSupport {
tikhomirov@425: 		private int perChildWorkUnitMultiplier; // to multiply child ps units
tikhomirov@425: 		private int perChildWorkUnitDivisor; // to scale down to parent ps units
tikhomirov@425: 		private int unitsConsumed; // parent ps units consumed so far
tikhomirov@425: 		private int fraction = 0; // leftovers of previous not completely consumed work units
tikhomirov@215: 		private final ProgressSupport ps;
tikhomirov@425: 		private final int psUnits; // total parent ps units
tikhomirov@215: 
tikhomirov@215: 		public Sub(ProgressSupport parent, int parentUnits) {
tikhomirov@215: 			if (parent == null) {
tikhomirov@215: 				throw new IllegalArgumentException();
tikhomirov@215: 			}
tikhomirov@215: 			ps = parent;
tikhomirov@215: 			psUnits = parentUnits;
tikhomirov@215: 		}
tikhomirov@215: 
tikhomirov@215: 		public void start(int totalUnits) {
tikhomirov@425: //			perChildWorkUnit = (psUnits*100) / totalUnits;
tikhomirov@425: 			perChildWorkUnitDivisor = 10 * totalUnits;
tikhomirov@425: 			perChildWorkUnitMultiplier = psUnits * perChildWorkUnitDivisor / totalUnits;
tikhomirov@425: 			
tikhomirov@215: 		}
tikhomirov@215: 
tikhomirov@215: 		public void worked(int worked) {
tikhomirov@425: 			int x = fraction + worked * perChildWorkUnitMultiplier;
tikhomirov@425: 			int u = x / perChildWorkUnitDivisor;
tikhomirov@425: 			fraction = x % perChildWorkUnitDivisor;
tikhomirov@425: 			if (u > 0) {
tikhomirov@425: 				ps.worked(u);
tikhomirov@425: 				unitsConsumed += u;
tikhomirov@215: 			}
tikhomirov@215: 		}
tikhomirov@215: 
tikhomirov@215: 		public void done() {
tikhomirov@425: 			ps.worked(psUnits - unitsConsumed);
tikhomirov@215: 		}
tikhomirov@215: 	}
tikhomirov@425: 	
tikhomirov@215: 	interface Target {
tikhomirov@215: 		T set(ProgressSupport ps);
tikhomirov@215: 	}
tikhomirov@148: }