/* * Copyright (C) 2014 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.android.tools.idea.gradle.invoker; import com.android.tools.idea.gradle.invoker.console.view.GradleConsoleView; import com.android.utils.SdkUtils; import com.google.common.io.Closeables; import com.intellij.execution.ui.ConsoleViewContentType; import org.gradle.tooling.BuildLauncher; import org.jetbrains.annotations.NotNull; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import static com.intellij.execution.ui.ConsoleViewContentType.ERROR_OUTPUT; import static com.intellij.execution.ui.ConsoleViewContentType.NORMAL_OUTPUT; /** * Collects and redirects the output to the "Gradle Console" view. */ class GradleOutputForwarder { private static final int SIZE = 2048; @NotNull private final ByteArrayOutputStream myStdErr; @NotNull private final ByteArrayOutputStream myOutput; @NotNull private final GradleConsoleView myConsoleView; private ConsoleViewContentType myPreviousContentType; GradleOutputForwarder(@NotNull GradleConsoleView consoleView) { myConsoleView = consoleView; myStdErr = new ByteArrayOutputStream(SIZE); myOutput = new ByteArrayOutputStream(SIZE * 2); } void attachTo(@NotNull BuildLauncher launcher) { OutputStream stdout = new ConsoleAwareOutputStream(this, NORMAL_OUTPUT); OutputStream stderr = new ConsoleAwareOutputStream(this, ERROR_OUTPUT); launcher.setStandardOutput(stdout); launcher.setStandardError(stderr); } void close() { try { Closeables.close(myOutput, true /* swallowIOException */); Closeables.close(myStdErr, true /* swallowIOException */); } catch (IOException e) { // Cannot happen } } @NotNull String getStdErr() { return myStdErr.toString(); } void write(@NotNull ConsoleViewContentType contentType, @NotNull byte[] b, int off, int len) { boolean addNewLine = false; // We are combining input from stdout and stderr and that we want to make sure that whenever the output is mixed it starts on a new // line. if (contentType != myPreviousContentType) { addNewLine = myPreviousContentType != null; myPreviousContentType = contentType; } String lineSeparator = SdkUtils.getLineSeparator(); boolean newLineAdded = false; if (addNewLine) { byte[] bytes = lineSeparator.getBytes(); myOutput.write(bytes, 0, bytes.length); myConsoleView.print(lineSeparator, contentType); newLineAdded = true; } String text = new String(b, off, len); if (lineSeparator.equals(text) && newLineAdded) { return; } myOutput.write(b, off, len); if (contentType == ERROR_OUTPUT) { myStdErr.write(b, off, len); } myConsoleView.print(text, contentType); } @Override public String toString() { return myOutput.toString(); } private static class ConsoleAwareOutputStream extends OutputStream { @NotNull private final GradleOutputForwarder myOutput; @NotNull private final ConsoleViewContentType myContentType; ConsoleAwareOutputStream(@NotNull GradleOutputForwarder output, @NotNull ConsoleViewContentType contentType) { myOutput = output; myContentType = contentType; } @Override public void write(int b) { } @Override public void write(byte[] b, int off, int len) { myOutput.write(myContentType, b, off, len); } } }