package com.yoursway.progress.core.internal; import static com.yoursway.progress.core.Naming.AS_CHILDREN; import static com.yoursway.progress.core.Naming.AS_SIBLINGS; import static com.yoursway.progress.core.Naming.OFF; import static com.yoursway.utils.YsMath.eq; import static com.yoursway.utils.YsMath.gt; import static com.yoursway.utils.YsMath.lt; import com.yoursway.pingback.core.Pingback; import com.yoursway.progress.core.Cancellation; import com.yoursway.progress.core.ItemizedProgress; import com.yoursway.progress.core.Naming; import com.yoursway.progress.core.Progress; public class ChildProgressTracker implements Progress, Tracker { private static final double LOCAL_EPS = 1e-6; private final Tracker parent; private double allocatedWeight = 0; private double usedWeight = 0; private final double parentWeightAllocated; private double parentWeightUsed = 0; private final Naming parentNaming; private double parentEps; private boolean taskNameSet = false; public ChildProgressTracker(Tracker parent, double parentWeightAllocated, Naming parentNaming) { if (parent == null) throw new NullPointerException("parent == null"); this.parent = parent; this.parentWeightAllocated = parentWeightAllocated; this.parentNaming = parentNaming; this.parentEps = parentWeightAllocated / 1000; } public void allocate(double totalWeigth) { checkCancellation(); allocatedWeight += totalWeigth; } public void checkCancellation() throws Cancellation { parent.checkCancellation(); } public void childStarting(int index) { } public void done() { internalDone(); } private void internalDone() { if (usedWeight < allocatedWeight) internalWorked(allocatedWeight - usedWeight); if (parentWeightUsed < parentWeightAllocated) parent.incrementWorkBecauseOfChild(parentWeightAllocated - parentWeightUsed); } public void incrementWorkBecauseOfChild(double delta) { internalWorked(delta); } public ItemizedProgress items(int items) { return items(items, items); } public ItemizedProgress items(int items, double totalWeigth) { checkCancellation(); allocatedWeight += totalWeigth; return new ChildItemizedProgressTracker(this, items, totalWeigth, AS_CHILDREN); } public void setTaskName(String name) { checkCancellation(); if (parentNaming == OFF) return; taskNameSet = true; parent.setTaskNameFromChild(name, parentNaming); } public void skip(double weight) { checkCancellation(); internalSkip(weight); } public void skipWorkBecauseOfChild(double delta) { internalSkip(delta); } public Progress subtask(double weight, Naming naming) { checkCancellation(); allocatedWeight += weight; return new ChildProgressTracker(this, weight, naming); } public void willNotRun() { checkCancellation(); parent.skipWorkBecauseOfChild(parentWeightAllocated); } public void worked(double weight) { checkCancellation(); internalWorked(weight); } private void internalSkip(double weight) { if (allocatedWeight - usedWeight < weight) { Pingback.ignorableErrorCondition("Progress.skip(" + weight + ") requests to skip more than available (" + (allocatedWeight - usedWeight) + " = " + allocatedWeight + "-" + usedWeight + ")"); weight = allocatedWeight - usedWeight; } allocatedWeight -= weight; if (eq(allocatedWeight, 0, parentEps)) internalDone(); } private void internalWorked(double weight) { if (lt(usedWeight + weight, allocatedWeight, LOCAL_EPS)) usedWeight += weight; else usedWeight = allocatedWeight; updateParentProgress(); } private void updateParentProgress() { double ratio = usedWeight / allocatedWeight; double parentUnused = parentWeightAllocated - parentWeightUsed; double parentDelta = parentUnused * ratio; if (gt(parentDelta, 0, parentEps)) { parent.incrementWorkBecauseOfChild(parentDelta); allocatedWeight -= usedWeight; usedWeight = 0; parentWeightUsed += parentDelta; } } public void setTaskNameFromChild(String name, Naming naming) { if (parentNaming == OFF) return; if (naming == AS_SIBLINGS) parent.setTaskNameFromChild(name, parentNaming); else setTaskNameFromChild(name, 0); } public void setTaskNameFromChild(String name, int level) { if (parentNaming == OFF) return; if (taskNameSet) level += 1; parent.setTaskNameFromChild(name, level); } }