/*
* $Id$
* This file is a part of the Arakhne Foundation Classes, http://www.arakhne.org/afc
*
* Copyright (c) 2000-2012 Stephane GALLAND.
* Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
* Universite de Technologie de Belfort-Montbeliard.
* Copyright (c) 2013-2016 The original authors, and other authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.arakhne.afc.progress;
import org.arakhne.afc.util.ListenerCollection;
/**
* An object that permits to indicates the progression of
* a task. The progression of the value is always ascendent.
*
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
public class DefaultProgression implements Progression {
/** Collection of listeners.
*/
protected final ListenerCollection<ProgressionListener> listeners = new ListenerCollection<>();
private int min;
private int max;
/** Use floating point value to allow finest subtask's progression.
*/
private double current;
private boolean isIndeterminate;
private boolean isAdjusting;
private String comment;
private SubProgressionModel child;
/** Create a progress model with the specified <i>determinate</i> state.
*
* @param value is the initial value of this model (between {@code min} and {@code max}).
* @param min is the minimal value
* @param max is the maximal value
* @param adjusting indicates if this model is under adjustement or not.
*/
public DefaultProgression(int value, int min, int max, boolean adjusting) {
if (min > max) {
this.min = max;
this.max = min;
} else {
this.min = min;
this.max = max;
}
this.current = (value < this.min) ? this.min : (value > this.max) ? this.max : value;
this.isIndeterminate = false;
this.isAdjusting = adjusting;
}
/** Create a progress model with the specified <i>determinate</i> state.
* The model is not adjusting.
*
* @param value is the initial value of this model (between {@code min} and {@code max}).
* @param min is the minimal value
* @param max is the maximal value
*/
public DefaultProgression(int value, int min, int max) {
this(value, min, max, false);
}
/** Create a progress model with the specified <i>indeterminate</i> state.
* The model is not adjusting.
*
* @param min is the minimal value
* @param max is the maximal value
*/
public DefaultProgression(int min, int max) {
if (min > max) {
this.min = max;
this.max = min;
} else {
this.min = min;
this.max = max;
}
this.current = this.min;
this.isIndeterminate = true;
this.isAdjusting = false;
}
/** Create a progress model in a <i>indeterminate</i> state.
* The model is not adjusting.
*/
public DefaultProgression() {
this.min = 0;
this.max = 0;
this.current = this.min;
this.isIndeterminate = true;
this.isAdjusting = false;
}
@Override
public void end() {
final int v = getValue();
final int m = getMaximum();
final String comment = getComment();
if (v < m || comment != null) {
setValue(m, null);
}
fireStateChange();
}
@Override
public void addProgressionListener(ProgressionListener listener) {
this.listeners.add(ProgressionListener.class, listener);
}
@Override
public void removeProgressionListener(ProgressionListener listener) {
this.listeners.remove(ProgressionListener.class, listener);
}
@Override
public boolean isRootModel() {
return true;
}
@Override
public Progression getSuperTask() {
return null;
}
@Override
public int getTaskDepth() {
return 0;
}
@Override
public final Progression getSubTask() {
return this.child;
}
@Override
public final void ensureNoSubTask() {
if (this.child != null) {
disconnectSubTask(
this.child,
this.child.getMaxInParent(),
false);
}
}
/** Notify listeners about state change.
*/
protected void fireStateChange() {
if (this.listeners != null) {
final ProgressionEvent event = new ProgressionEvent(this, isRootModel());
for (final ProgressionListener listener : this.listeners.getListeners(ProgressionListener.class)) {
listener.onProgressionStateChanged(event);
}
}
}
/** Notify listeners about value change.
*/
protected void fireValueChange() {
if (this.listeners != null) {
final ProgressionEvent event = new ProgressionEvent(this, isRootModel());
for (final ProgressionListener listener : this.listeners.getListeners(ProgressionListener.class)) {
listener.onProgressionValueChanged(event);
}
}
}
@Override
public int getMaximum() {
return this.max;
}
@Override
public int getMinimum() {
return this.min;
}
@Override
@SuppressWarnings("checkstyle:magicnumber")
public double getPercent() {
final int extent = this.max - this.min;
if (extent == 0.) {
return 0.;
}
return (this.current - this.min) * 100. / extent;
}
@Override
public double getProgressionFactor() {
final int extent = this.max - this.min;
if (extent == 0.) {
return 0.;
}
return (this.current - this.min) / extent;
}
@Override
public int getValue() {
return (int) this.current;
}
/** Replies the floating-point precision value.
*
* @return the floating-point precision value.
*/
protected double getFloatValue() {
return this.current;
}
@Override
public boolean isAdjusting() {
return this.isAdjusting;
}
@Override
public boolean isIndeterminate() {
return this.isIndeterminate;
}
@Override
public void setIndeterminate(boolean newValue) {
if (this.isIndeterminate != newValue) {
this.isIndeterminate = newValue;
fireStateChange();
}
}
@Override
public void setMaximum(int newMaximum) {
setProperties(this.current, this.min, newMaximum, this.isAdjusting, this.comment, false, false, true, null);
}
@Override
public void setMinimum(int newMinimum) {
setProperties(this.current, newMinimum, this.max, this.isAdjusting, this.comment, false, false, true, null);
}
@Override
public void setProperties(int value, int min, int max, boolean adjusting, String comment) {
setProperties(value, min, max, adjusting, comment, true, true, true, this.child);
}
@Override
public void setProperties(int value, int min, int max, boolean adjusting) {
setProperties(value, min, max, adjusting, this.comment, false, true, true, this.child);
}
@SuppressWarnings({"checkstyle:cyclomaticcomplexity", "checkstyle:npathcomplexity",
"checkstyle:parameternumber", "checkstyle:booleanexpressioncomplexity"})
private void setProperties(double value, int min, int max, boolean adjusting,
String comment, boolean writeLocalComment,
boolean forceDeterminate, boolean forceValue,
SubProgressionModel subTask) {
if (this.child != null && this.child != subTask) {
this.child.disconnect();
}
String uptComment = comment;
if (uptComment != null && "".equals(uptComment)) { //$NON-NLS-1$
uptComment = null;
}
int theMin = min;
int theMax = max;
if (theMin > max) {
final int tmp = theMin;
theMin = theMax;
theMax = tmp;
}
double theValue = value;
if (theValue < theMin) {
theValue = theMin;
}
if (theValue > theMax) {
theValue = theMax;
}
final boolean changed = (forceValue && (theValue != this.current))
|| ((!forceValue) && (theValue > this.current))
|| (theMin != this.min)
|| (theMax != this.max)
|| (adjusting != this.isAdjusting)
|| (uptComment != this.comment && (uptComment == null || !uptComment.equals(this.comment)));
if (changed && !adjusting) {
this.current = theValue;
this.min = theMin;
this.max = theMax;
this.isAdjusting = adjusting;
if (writeLocalComment) {
this.comment = uptComment;
}
fireValueChange();
}
if (forceDeterminate) {
setIndeterminate(false);
}
}
@Override
public void setValue(int newValue) {
setProperties(newValue, this.min, this.max, this.isAdjusting, this.comment, false, true, false, null);
}
@Override
public void setValue(int newValue, String comment) {
setProperties(newValue, this.min, this.max, this.isAdjusting, comment, true, true, false, null);
}
/** Set the value with a float-point precision.
* This method is invoked from a subtask.
*
* @param subTask the subtask.
* @param newValue the new value.
* @param comment the new comment.
*/
void setValue(SubProgressionModel subTask, double newValue, String comment) {
setProperties(newValue, this.min, this.max, this.isAdjusting,
comment == null ? this.comment : comment, true, true, false,
subTask);
}
@Override
public void setAdjusting(boolean adjusting) {
setProperties(this.current, this.min, this.max, adjusting, this.comment, false, false, false, null);
}
@Override
public final Progression subTask(int extent, int minValue, int maxValue) {
return subTask(extent, minValue, maxValue, false);
}
@Override
public Progression subTask(int extent, int minValue, int maxValue, boolean overwriteComment) {
if (this.child != null) {
this.child.disconnect();
}
this.child = new SubProgressionModel(
this,
this.current, this.current + Math.abs(extent),
minValue, maxValue,
this.isIndeterminate, this.isAdjusting,
overwriteComment);
return this.child;
}
@Override
public final Progression subTask(int extent) {
return subTask(extent, false);
}
@Override
public Progression subTask(int extent, boolean overwriteComment) {
if (this.child != null) {
this.child.disconnect();
}
this.child = new SubProgressionModel(
this,
this.current, this.current + Math.abs(extent),
this.isIndeterminate, this.isAdjusting,
overwriteComment);
return this.child;
}
/** Set the parent value and disconnect this subtask from its parent.
* This method is invoked from a subtask.
*
* @param subTask the subtask to disconnect.
* @param value the value of the task.
* @param overwriteComment indicates if the comment of this parent model may
* be overwritten by the child's comment.
*/
void disconnectSubTask(SubProgressionModel subTask, double value, boolean overwriteComment) {
if (this.child == subTask) {
if (overwriteComment) {
final String cmt = subTask.getComment();
if (cmt != null) {
this.comment = cmt;
}
}
this.child = null;
setProperties(value, this.min, this.max, this.isAdjusting, this.comment, overwriteComment, true, false, null);
}
}
@Override
public String getComment() {
if (this.child != null) {
final String childComment = this.child.getComment();
if (childComment != null) {
return childComment;
}
}
return this.comment;
}
@Override
public void setComment(String comment) {
setProperties(
this.current,
this.min, this.max,
this.isAdjusting, comment,
true, true, false, null);
}
@Override
public void increment(int amount) {
setProperties((int) this.current + amount, this.min, this.max, this.isAdjusting, this.comment, false, true, false, null);
}
@Override
public void increment(int amount, String comment) {
setProperties((int) this.current + amount, this.min, this.max, this.isAdjusting, comment, true, true, false, null);
}
@Override
public void increment() {
setProperties((int) this.current + 1, this.min, this.max, this.isAdjusting, this.comment, false, true, false, null);
}
@Override
public void increment(String comment) {
setProperties((int) this.current + 1, this.min, this.max, this.isAdjusting, comment, true, true, false, null);
}
}