| // Copyright (C) 2018 The Android Open Source Project |
| // |
| // 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 com.googlesource.gerrit.plugins.renameproject.monitor; |
| |
| import com.google.common.util.concurrent.ThreadFactoryBuilder; |
| import java.io.IOException; |
| import java.io.Writer; |
| import java.util.concurrent.ScheduledThreadPoolExecutor; |
| import java.util.concurrent.TimeUnit; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| public class CommandProgressMonitor implements ProgressMonitor, AutoCloseable { |
| private static final Logger log = LoggerFactory.getLogger(CommandProgressMonitor.class); |
| private final Writer out; |
| private boolean write = true; |
| private Task task; |
| |
| // Constant indicating the total work units cannot be predicted in advance. |
| private static final int UNKNOWN = 0; |
| |
| public CommandProgressMonitor(Writer out) { |
| this.out = out; |
| } |
| |
| @Override |
| public synchronized void beginTask(String taskName, int work) { |
| endTask(); |
| if (work == UNKNOWN) { |
| task = new UnknownTask(taskName); |
| } else { |
| task = new Task(taskName, work); |
| } |
| } |
| |
| @Override |
| public synchronized void beginTask(String taskName) { |
| beginTask(taskName, UNKNOWN); |
| } |
| |
| @Override |
| public synchronized void update(int completed) { |
| if (task != null) { |
| task.update(completed); |
| } |
| } |
| |
| private void endTask() { |
| if (task != null) { |
| task.end(); |
| task = null; |
| } |
| } |
| |
| @Override |
| public synchronized void close() { |
| endTask(); |
| } |
| |
| private class Task { |
| protected final String taskName; |
| protected final int totalWork; |
| protected int completedWork; |
| protected boolean taskDone; |
| |
| Task(String taskName, int totalWork) { |
| this.taskName = taskName; |
| this.totalWork = totalWork; |
| sendUpdate(); |
| } |
| |
| void update(int completed) { |
| completedWork += completed; |
| sendUpdate(); |
| } |
| |
| void end() { |
| taskDone = true; |
| sendUpdate(); |
| } |
| |
| protected void sendUpdate() { |
| if (!write) { |
| return; |
| } |
| StringBuilder output = new StringBuilder(); |
| writeTaskName(output); |
| writeDetails(output); |
| if (taskDone) { |
| output.append("\n"); |
| } |
| try { |
| out.write(output.toString()); |
| out.flush(); |
| } catch (IOException err) { |
| write = false; |
| log.error("Failed to send update to the progress monitor:", err); |
| } |
| } |
| |
| protected void writeTaskName(StringBuilder output) { |
| output.append("\r"); |
| output.append(taskName); |
| output.append(": "); |
| while (output.length() < 50) { |
| output.append(' '); |
| } |
| } |
| |
| protected void writeDetails(StringBuilder output) { |
| String totalStr = String.valueOf(totalWork); |
| StringBuilder completedStr = new StringBuilder(String.valueOf(completedWork)); |
| while (completedStr.length() < totalStr.length()) { |
| completedStr.insert(0, " "); |
| } |
| int percentDone = completedWork * 100 / totalWork; |
| if (percentDone < 100) { |
| output.append(' '); |
| } |
| output.append(percentDone); |
| output.append("% ("); |
| output.append(completedStr); |
| output.append("/"); |
| output.append(totalStr); |
| output.append(")"); |
| } |
| } |
| |
| private class UnknownTask extends Task { |
| private static final String ANIMATION = "|/-\\"; |
| private static final int ANIMATION_DELAY_MS = 100; |
| private final ScheduledThreadPoolExecutor executor; |
| private int animationCounter; |
| |
| UnknownTask(String taskName) { |
| super(taskName, UNKNOWN); |
| executor = |
| new ScheduledThreadPoolExecutor( |
| 1, new ThreadFactoryBuilder().setNameFormat("Rename-Monitoring-%d").build()); |
| executor.scheduleAtFixedRate( |
| () -> { |
| animationCounter++; |
| sendUpdate(); |
| }, |
| ANIMATION_DELAY_MS, |
| ANIMATION_DELAY_MS, |
| TimeUnit.MILLISECONDS); |
| } |
| |
| @Override |
| protected void writeDetails(StringBuilder output) { |
| if (taskDone) { |
| output.append("100%"); |
| return; |
| } |
| int nextAnimation = animationCounter % ANIMATION.length(); |
| output.append(String.valueOf(ANIMATION.charAt(nextAnimation))); |
| } |
| |
| @Override |
| void end() { |
| executor.shutdownNow(); |
| try { |
| executor.awaitTermination(1, TimeUnit.SECONDS); |
| } catch (InterruptedException e) { |
| log.error("Interrupted while waiting for termination of executor:", e); |
| Thread.currentThread().interrupt(); |
| } |
| super.end(); |
| } |
| } |
| } |