| /*=============================================================================# |
| # Copyright (c) 2018, 2021 Stephan Wahlbrink and others. |
| # |
| # This program and the accompanying materials are made available under the |
| # terms of the Eclipse Public License 2.0 which is available at |
| # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 |
| # which is available at https://www.apache.org/licenses/LICENSE-2.0. |
| # |
| # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 |
| # |
| # Contributors: |
| # Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation |
| #=============================================================================*/ |
| |
| package org.eclipse.statet.jcommons.status; |
| |
| import java.util.Objects; |
| |
| import org.eclipse.statet.jcommons.lang.NonNull; |
| import org.eclipse.statet.jcommons.lang.NonNullByDefault; |
| import org.eclipse.statet.jcommons.lang.Nullable; |
| |
| |
| @NonNullByDefault |
| public class BasicProgressMonitor implements ProgressMonitor { |
| |
| |
| private static final int ROOT_FLAG= 1 << 31; |
| |
| private static final int INHERITED_FLAGS= (SUPPRESS_BEGINTASK_NAME | SUPPRESS_ISCANCELED); |
| |
| |
| public static class ProgressData { |
| |
| protected static final byte MAIN_TASK_NAME= 1 << 1; |
| protected static final byte SUB_TASK_NAME= 1 << 2; |
| protected static final byte PROGRESS_UNKNOWN= 1 << 3; |
| protected static final byte PROGRESS_RATE= 1 << 4; |
| protected static final byte CANCELED= 1 << 5; |
| protected static final byte BLOCKED= 1 << 6; |
| |
| |
| private String mainTaskName= ""; //$NON-NLS-1$ |
| private @Nullable String subTaskName; |
| |
| private boolean progressUnknown; |
| /** Progress: from 1 to 0 */ |
| private double progress= 1; |
| |
| private volatile boolean isCanceled; |
| |
| private @Nullable Status blocked; |
| |
| |
| public ProgressData() { |
| } |
| |
| |
| public String getMainTaskName() { |
| return this.mainTaskName; |
| } |
| |
| protected final void setMainTaskName(final String name) { |
| if (!Objects.equals(this.mainTaskName, name)) { |
| this.mainTaskName= name; |
| this.subTaskName= null; |
| |
| onDataChanged(MAIN_TASK_NAME); |
| } |
| else if (this.subTaskName != null) { |
| this.subTaskName= null; |
| |
| onDataChanged(SUB_TASK_NAME); |
| } |
| } |
| |
| public @Nullable String getSubTaskName() { |
| return this.subTaskName; |
| } |
| |
| private final void setSubTaskName(@Nullable String name) { |
| if (name != null && name.isEmpty()) { |
| name= null; |
| } |
| if (!Objects.equals(this.subTaskName, name)) { |
| this.subTaskName= name; |
| |
| onDataChanged(SUB_TASK_NAME); |
| } |
| } |
| |
| public boolean isProgressUnknown() { |
| return this.progressUnknown; |
| } |
| |
| private void setProgressUnknown(final boolean enabled) { |
| if (this.progressUnknown != enabled) { |
| this.progressUnknown= enabled; |
| |
| onDataChanged(PROGRESS_UNKNOWN); |
| } |
| } |
| |
| public double getProgress() { |
| return this.progress; |
| } |
| |
| private void setProgress(final double progress) { |
| if (progress < this.progress) { |
| this.progress= progress; |
| |
| onDataChanged(PROGRESS_RATE); |
| } |
| } |
| |
| public boolean isCanceled() { |
| return this.isCanceled; |
| } |
| |
| protected void setCanceled(final boolean state) { |
| if (this.isCanceled != state) { |
| this.isCanceled= state; |
| |
| onDataChanged(CANCELED); |
| } |
| } |
| |
| public @Nullable Status getBlocked() { |
| return this.blocked; |
| } |
| |
| protected void setBlocked(final Status reason) { |
| if (!Objects.equals(this.blocked, reason)) { |
| this.blocked= reason; |
| |
| onDataChanged(BLOCKED); |
| } |
| } |
| |
| protected void clearBlocked() { |
| if (this.blocked != null) { |
| this.blocked= null; |
| |
| onDataChanged(BLOCKED); |
| } |
| } |
| |
| |
| protected void onDataChanged(final byte data) { |
| } |
| |
| } |
| |
| |
| protected final ProgressData data; |
| |
| private final int flags; |
| |
| private String taskName; |
| |
| /** Bound (this.workRemaining == 0) for data.progress */ |
| private final double progressDataDone; |
| /** Current work remaining: from x to 0 */ |
| private int workRemaining; |
| /** Current factor: work -> progress */ |
| private double workProgressFactor; |
| |
| private @Nullable BasicProgressMonitor currentSub; |
| |
| |
| private BasicProgressMonitor(final ProgressData data, final double progressDone, |
| final int flags) { |
| this.data= data; |
| this.flags= flags; |
| this.taskName= data.mainTaskName; |
| |
| this.progressDataDone= progressDone; |
| } |
| |
| public BasicProgressMonitor(final ProgressData data, final int flags) { |
| this(data, 0, ROOT_FLAG | flags); |
| } |
| |
| public BasicProgressMonitor(final ProgressData data) { |
| this(data, 0, ROOT_FLAG); |
| } |
| |
| |
| @Override |
| public void beginTask(final String name, final int totalWork) { |
| if ((this.flags & SUPPRESS_BEGINTASK_NAME) == 0 && name != null) { |
| this.taskName= name; |
| this.data.setMainTaskName(name); |
| } |
| |
| setWorkRemaining(totalWork); |
| } |
| |
| @Override |
| public void beginSubTask(final @Nullable String name) { |
| checkProgress(); |
| |
| this.data.setSubTaskName(name); |
| } |
| |
| @Override |
| public ProgressMonitor setWorkRemaining(final int work) { |
| checkProgress(); |
| |
| if (work >= 0) { |
| if ((this.flags & ROOT_FLAG) != 0) { |
| this.data.setProgressUnknown(false); |
| } |
| final double progressRemaining= (this.data.progress - this.progressDataDone); |
| if (work == 0 || progressRemaining <= 0) { |
| this.workProgressFactor= 0; |
| |
| this.workRemaining= 0; |
| this.data.setProgress(this.progressDataDone); |
| } |
| else { |
| this.workRemaining= work; |
| this.workProgressFactor= progressRemaining / work; |
| } |
| } |
| else { |
| if (work == UNKNOWN) { |
| if ((this.flags & ROOT_FLAG) != 0) { |
| this.data.setProgressUnknown(true); |
| } |
| } |
| this.workProgressFactor= 0; |
| } |
| |
| return this; |
| } |
| |
| @Override |
| public void addWorked(final int work) { |
| checkProgress(); |
| |
| this.data.setProgress(consumeWork(work)); |
| } |
| |
| @Override |
| public @NonNull ProgressMonitor newSubMonitor(final int work, final int flags) { |
| checkProgress(); |
| |
| return this.currentSub= new BasicProgressMonitor(this.data, consumeWork(work), |
| (this.flags & INHERITED_FLAGS) | flags ); |
| } |
| |
| private double consumeWork(final int work) { |
| if (work > 0 && this.workProgressFactor != 0) { |
| this.workRemaining= Math.max(this.workRemaining - work, 0); |
| } |
| return this.progressDataDone + this.workRemaining * this.workProgressFactor; |
| } |
| |
| private void checkProgress() { |
| final BasicProgressMonitor currentSub= this.currentSub; |
| if (currentSub != null) { |
| this.currentSub= null; |
| this.data.setMainTaskName(this.taskName); |
| this.data.setProgress(currentSub.progressDataDone); |
| } |
| } |
| |
| |
| @Override |
| public boolean isCanceled() { |
| if ((this.flags & SUPPRESS_ISCANCELED) == 0) { |
| return this.data.isCanceled(); |
| } |
| return false; |
| } |
| |
| @Override |
| public void setCanceled(final boolean state) { |
| this.data.setCanceled(state); |
| } |
| |
| |
| @Override |
| public void setBlocked(final Status reason) { |
| this.data.setBlocked(reason); |
| } |
| |
| @Override |
| public void clearBlocked() { |
| this.data.clearBlocked(); |
| } |
| |
| } |