org.gradle.internal.logging.console.WorkInProgressRenderer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gradle-api Show documentation
Show all versions of gradle-api Show documentation
Gradle 6.9.1 API redistribution.
/*
* Copyright 2017 the original author or 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.gradle.internal.logging.console;
import org.gradle.internal.logging.events.BatchOutputEventListener;
import org.gradle.internal.logging.events.EndOutputEvent;
import org.gradle.internal.logging.events.MaxWorkerCountChangeEvent;
import org.gradle.internal.logging.events.OperationIdentifier;
import org.gradle.internal.logging.events.OutputEvent;
import org.gradle.internal.logging.events.OutputEventListener;
import org.gradle.internal.logging.events.ProgressCompleteEvent;
import org.gradle.internal.logging.events.ProgressEvent;
import org.gradle.internal.logging.events.ProgressStartEvent;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class WorkInProgressRenderer extends BatchOutputEventListener {
private final OutputEventListener listener;
private final ProgressOperations operations = new ProgressOperations();
private final BuildProgressArea progressArea;
private final DefaultWorkInProgressFormatter labelFormatter;
private final ConsoleLayoutCalculator consoleLayoutCalculator;
// Track all unused labels to display future progress operation
private final Deque unusedProgressLabels;
// Track currently associated label with its progress operation
private final Map operationIdToAssignedLabels = new HashMap();
// Track any progress operation that either can't be display due to label shortage or child progress operation is already been displayed
private final Deque unassignedProgressOperations = new ArrayDeque();
// Track the parent-children relation between progress operation to avoid displaying a parent when children are been displayed
private final Map> parentIdToChildrenIds = new HashMap>();
public WorkInProgressRenderer(OutputEventListener listener, BuildProgressArea progressArea, DefaultWorkInProgressFormatter labelFormatter, ConsoleLayoutCalculator consoleLayoutCalculator) {
this.listener = listener;
this.progressArea = progressArea;
this.labelFormatter = labelFormatter;
this.consoleLayoutCalculator = consoleLayoutCalculator;
this.unusedProgressLabels = new ArrayDeque(progressArea.getBuildProgressLabels());
}
@Override
public void onOutput(OutputEvent event) {
if (event instanceof ProgressStartEvent) {
progressArea.setVisible(true);
ProgressStartEvent startEvent = (ProgressStartEvent) event;
ProgressOperation op = operations.start(startEvent.getShortDescription(), startEvent.getStatus(), startEvent.getCategory(), startEvent.getOperationId(), startEvent.getParentId());
attach(op);
} else if (event instanceof ProgressCompleteEvent) {
ProgressCompleteEvent completeEvent = (ProgressCompleteEvent) event;
detach(operations.complete(completeEvent.getOperationId()));
} else if (event instanceof ProgressEvent) {
ProgressEvent progressEvent = (ProgressEvent) event;
operations.progress(progressEvent.getStatus(), progressEvent.getOperationId());
} else if (event instanceof EndOutputEvent) {
progressArea.setVisible(false);
} else if (event instanceof MaxWorkerCountChangeEvent) {
int newCount = consoleLayoutCalculator.calculateNumWorkersForConsoleDisplay(
((MaxWorkerCountChangeEvent) event).getNewMaxWorkerCount());
resizeTo(newCount);
}
listener.onOutput(event);
}
@Override
public void onOutput(Iterable events) {
super.onOutput(events);
renderNow();
}
private void resizeTo(int newBuildProgressLabelCount) {
int previousBuildProgressLabelCount = progressArea.getBuildProgressLabels().size();
if (previousBuildProgressLabelCount >= newBuildProgressLabelCount) {
// We don't support shrinking at the moment
return;
}
progressArea.resizeBuildProgressTo(newBuildProgressLabelCount);
// Add new labels to the unused queue
for (int i = newBuildProgressLabelCount - 1; i >= previousBuildProgressLabelCount; --i) {
unusedProgressLabels.push(progressArea.getBuildProgressLabels().get(i));
}
// Try to empty the unassigned progress operations
while (!unusedProgressLabels.isEmpty() && !unassignedProgressOperations.isEmpty()) {
attach(unassignedProgressOperations.pop());
}
}
private void attach(ProgressOperation operation) {
// Skip attach if a children is already present
if (isChildAssociationAlreadyExists(operation.getOperationId())) {
return;
}
AssociationLabel association = null;
// Reuse parent label if possible
if (operation.getParent() != null) {
addDirectChildOperationId(operation.getParent().getOperationId(), operation.getOperationId());
association = operationIdToAssignedLabels.remove(operation.getParent().getOperationId());
if (association != null) {
unusedProgressLabels.push(association.label);
association = null;
}
}
// No parent? Try to use a new label
if (association == null && !unusedProgressLabels.isEmpty()) {
association = new AssociationLabel(operation, unusedProgressLabels.pop());
}
if (association == null) {
unassignedProgressOperations.addLast(operation);
} else {
operationIdToAssignedLabels.put(operation.getOperationId(), association);
}
}
private void detach(ProgressOperation operation) {
if (operation.getParent() != null) {
removeDirectChildOperationId(operation.getParent().getOperationId(), operation.getOperationId());
}
AssociationLabel association = operationIdToAssignedLabels.remove(operation.getOperationId());
if (association != null) {
unusedProgressLabels.push(association.label);
if (operation.getParent() != null) {
attach(operation.getParent());
} else if (!unassignedProgressOperations.isEmpty()){
attach(unassignedProgressOperations.pop());
}
} else {
unassignedProgressOperations.remove(operation);
}
}
private void addDirectChildOperationId(OperationIdentifier parentId, OperationIdentifier childId) {
Set children = parentIdToChildrenIds.get(parentId);
if (children == null) {
children = new HashSet();
parentIdToChildrenIds.put(parentId, children);
}
children.add(childId);
}
private void removeDirectChildOperationId(OperationIdentifier parentId, OperationIdentifier childId) {
Set children = parentIdToChildrenIds.get(parentId);
if (children == null) {
throw new IllegalStateException("");
}
children.remove(childId);
if (children.isEmpty()) {
parentIdToChildrenIds.remove(parentId);
}
}
private boolean isChildAssociationAlreadyExists(OperationIdentifier parentId) {
Set children = parentIdToChildrenIds.get(parentId);
if (children != null && !children.isEmpty()) {
return true;
}
return false;
}
private void renderNow() {
for (AssociationLabel associatedLabel : operationIdToAssignedLabels.values()) {
associatedLabel.renderNow();
}
for (StyledLabel emptyLabel : unusedProgressLabels) {
emptyLabel.setText(labelFormatter.format());
}
}
private class AssociationLabel {
final ProgressOperation operation;
final StyledLabel label;
AssociationLabel(ProgressOperation operation, StyledLabel label) {
this.operation = operation;
this.label = label;
}
void renderNow() {
label.setText(labelFormatter.format(operation));
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy