gt: 				double toreport = parentwork - m_workReportedToParent;
context: /*
   * DomUI Java User Interface - shared code
   * Copyright (c) 2010 by Frits Jalvingh, Itris B.V.
   *
   * This library is free software; you can redistribute it and/or
   * modify it under the terms of the GNU Lesser General Public
   * License as published by the Free Software Foundation; either
   * version 2.1 of the License, or (at your option) any later version.
   *
   * This library 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
   * Lesser General Public License for more details.
   *
   * You should have received a copy of the GNU Lesser General Public
   * License along with this library; if not, write to the Free Software
   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
   *
   * See the "sponsors" file for a list of supporters.
   *
   * The latest version of DomUI and related code, support and documentation
   * can be found at http://www.domui.org/
   * The contact for the project is Frits Jalvingh <jal@etc.to>.
   */
  package to.etc.util;
  
  import org.eclipse.jdt.annotation.NonNull;
  import org.eclipse.jdt.annotation.Nullable;
  
  import java.util.ArrayList;
  import java.util.List;
  import java.util.concurrent.CopyOnWriteArrayList;
  
  /**
   * A progress reporter utility. A single instance defines progress in user-specified
   * numeric terms (for instance record 12 of 1012312), and gets translated to a
   * percentage. Progress instances can obtain sub-progress parts that are Progress
   * entries all by themselves and can define their work in their terms; they get
   * mapped to the defined part of work in their parent(s).
   *
   * @author <a href="mailto:jal@etc.to">Frits Jalvingh</a>
   * Created on Oct 16, 2009
   */
  public class Progress {
  	/** The top-level progress entry, */
  	final private Progress m_root;
  
  	/** The parent of this progress entry */
  	private Progress m_parent;
  
  	/** If a subprogress is in action this contains it. */
  	final private List<Progress> m_subProgress = new ArrayList<Progress>();
  
  	/** The amount of work in the parent(!) progress' units. */
  	private double m_parentsWorkForSub;
  
  	/** The total amount of work in *this* thing. */
  	private double m_totalWork;
  
  	/** The amount of work done. */
  	private double m_currentWork;
  
  	private double m_workReportedToParent;
  
  	/** T if a cancel request is received. */
  	private boolean m_cancelled;
  
  	private boolean m_parallel;
  
  	/** If F, then this cannot be cancelled, and any attempt to do that is ignored. */
  	private boolean m_cancelable = true;
  
  	/** A short-as-possible name for the current activity */
  	@Nullable
  	private String m_name;
  
  	@Nullable
  	private String m_extra;
  
  	@NonNull
  	final private CopyOnWriteArrayList<IProgressListener> m_listeners = new CopyOnWriteArrayList<>();
  
  	static public class Info {
  		final private String m_path;
  
  		final private int m_percentage;
  
  		public Info(String path, int percentage) {
  			m_path = path;
  			m_percentage = percentage;
  		}
  
  		public String getPath() {
  			return m_path;
  		}
  
  		public int getPercentage() {
  			return m_percentage;
  		}
  	}
  
  	/**
  	 * Top-level progress indicator for a given task.
  	 */
  	public Progress(@Nullable String name) {
  		m_root = this;
  		m_name = name;
  	}
  
  	private Progress(@NonNull Progress parent, @Nullable String name) {
  		m_parent = parent;
  		m_root = parent.m_root;
  		m_name = name;
  	}
  
  	/**
  	 * Return all parallel-running progress parts, starting with the root one.
  	 */
  	@NonNull
  	public List<Info> getParallels(int level) {
  		List<Info> prl = new ArrayList<Info>();
  		synchronized(m_root) {
  			if(m_parent != null)
  				throw new IllegalStateException("Only callable on root entity");
  
  			StringBuilder sb = new StringBuilder();
  			Progress split = findSplitPoint(sb, this, level);
  
  			//-- Got 1st split point. Add this-items' progress
  			prl.add(new Info(sb.toString(), getPercentage()));
  
  			if(split != null) {
  				sb.setLength(0);
  				handleSplit(sb, prl, split);
  			}
  		}
  		return prl;
  	}
  
  	private void handleSplit(@NonNull StringBuilder sb, @NonNull List<Info> prl, @NonNull Progress split) {
  		int len = sb.length();
  
  		for(Progress sub : split.m_subProgress) {
  			sb.setLength(len);
  			Progress nsplit = findSplitPoint(sb, sub, 3);
  			if(nsplit == null) {
  				prl.add(new Info(sb.toString(), sub.getPercentage()));
  			} else {
  				handleSplit(sb, prl, nsplit);
  			}
  		}
  		sb.setLength(len);
  	}
  
  	private Progress findSplitPoint(@NonNull StringBuilder sb, @NonNull Progress progress, int level) {
  		while(progress != null) {
  			String name = progress.m_name;
  			if(name != null) {
  				if(sb.length() > 0)
  					sb.append('>');
  				sb.append(name);
  				name = progress.m_extra;
  				if(name != null)
  					sb.append(' ').append(name);
  			}
  
  			if(--level == 0)
  				return null;
  			if(m_subProgress.size() == 0) {
  				return null;
  			} else if(m_subProgress.size() == 1) {
  				progress = m_subProgress.get(0);
  			} else {
  				return progress;
  			}
  		}
  		return null;
  	}
  
  	/**
  	 * Return the root progress handler; this NEVER returns null - it returns itself if it is the root.
  	 */
  	@NonNull
  	public Progress getRoot() {
  		return m_root;
  	}
  
  	@Nullable
  	public Progress getParent() {
  		return m_parent;
  	}
  
  	public boolean isCancelable() {
  		return getRoot().m_cancelable;
  	}
  
  	@Nullable
  	public String getName() {
  		synchronized(m_root) {
  			String extra = m_extra;
  			String name = m_name;
  			if(extra == null)
  				return name;
  			if(name == null)
  				return extra;
  			return name + " " + extra;
  		}
  	}
  
  	public double getTotalWork() {
  		synchronized(m_root) {
  			return m_totalWork;
  		}
  	}
  
  	public double getCompletedWork() {
  		synchronized(m_root) {
  			return m_currentWork;
  		}
  	}
  
  	public int getPercentage() {
  		synchronized(m_root) {
  			if(m_totalWork <= 0)
  				return 0;
  			double pct = m_currentWork * 100.0d / m_totalWork;
  			return (int) Math.round(pct);
  		}
  	}
  
  	public double getFraction() {
  		synchronized(m_root) {
  			if(m_totalWork <= 0)
  				return 0.0d;
  			return m_currentWork / m_totalWork;
  		}
  	}
  
  	/**
  	 * Get the path until the first parallel subprocess or the specified level
  	 */
  	@NonNull
  	public String getActionPath(int levels) {
  		StringBuilder sb = new StringBuilder(80);
  
  		synchronized(m_root) {
  			Progress p = this;
  			while(p != null && levels > 0) {
  				String levelName = p.m_name;
  				String levelExtra = p.m_extra;
  				if(levelName != null || levelExtra != null) {
  					if(sb.length() != 0)
  						sb.append(">");
  					if(null != levelName)
  						sb.append(levelName);
  					if(levelExtra != null) {
  						if(levelName != null)
  							sb.append(' ');
  						sb.append(levelExtra);
  					}
  					levels--;
  				}
  				if(p.m_subProgress.size() != 1)
  					p = null;
  				else
  					p = p.m_subProgress.get(0);
  			}
  		}
  		return sb.toString();
  	}
  
  	/**
  	 * Request the action to be cancelled.
  	 */
  	public void cancel() {
  		synchronized(m_root) {
  			if(!m_cancelled && m_root.m_cancelable) {
  				m_cancelled = true;
  				if(m_parent != null)
  					m_parent.cancel();        // Pass upwards.
  				updateTree();
  			}
  		}
  	}
  
  	public void setCancelable(boolean yes) {
  		synchronized(m_root) {
  			m_root.m_cancelable = yes;
  		}
  	}
  
  	public boolean isCancelled() {
  		synchronized(m_root) {
  			return m_root.m_cancelled;        // Do not use getter, please 8-(
  		}
  	}
  
  	/**
  	 * Set the current amount of work.
  	 */
  	public void setTotalWork(double work) {
  		setTotalWork(work, null);
  	}
  
  	/**
  	 * Set the current amount of work.
  	 *
  	 * @param work int for convenience.
  	 */
  	public void setTotalWork(int work) {
  		setTotalWork(work, null);
  	}
  
  	/**
  	 * Set the current amount of work.
  	 */
  	public void setTotalWork(double work, @Nullable String extra) {
  		synchronized(m_root) {
  			checkCancelled();
  			if(m_totalWork != 0) {
  				m_currentWork = 0;
  			}
  			m_totalWork = work;
  			m_extra = extra;
  		}
  	}
  
  	private void checkCancelled() {
  		synchronized(m_root) {
  			if(m_root.m_cancelled && m_root.m_cancelable)
  				throw new CancelledException();
  		}
  	}
  
  	/**
  	 * Set the amount of work completed. It can only be set to a valid value, and it can
  	 * only advance, not go back.
  	 */
  	public void setCompleted(double now) {
  		checkCancelled();
  		setCompleted(now, null);
  	}
  
  	/**
  	 * Set the amount of work completed. It can only be set to a valid value, and it can
  	 * only advance, not go back.
  	 *
  	 * @param now int for convenience
  	 */
  	public void setCompleted(int now) {
  		checkCancelled();
  		setCompleted(now, null);
  	}
  
  	/**
  	 * Set the amount of work completed. It can only be set to a valid value, and it can
  	 * only advance, not go back.
  	 */
  	private void internalSetCompleted(double now) {
  		synchronized(m_root) {
  			checkCancelled();
  			if(now <= m_currentWork)
  				return;
  			if(now >= m_totalWork)
  				now = m_totalWork;
  			m_currentWork = now;
  
  			//-- If I have a parent: increment it's work done.
  			Progress dad = m_parent;
  			if(null != dad) {
  				double parentwork = getFraction() * m_parentsWorkForSub;        // Amount of work done if parent's units'
  