From 31052491621060db1903ca5a89f346d850b1d4de Mon Sep 17 00:00:00 2001 From: "Vladislav.Soroka" Date: Tue, 16 Apr 2019 07:39:06 +0300 Subject: [PATCH] Build tw: convert build output reader to kotlin --- .../output/BuildOutputInstantReaderImpl.java | 235 ------------------ .../output/BuildOutputInstantReaderImpl.kt | 200 +++++++++++++++ .../BuildOutputInstantReaderImplTest.kt | 2 +- 3 files changed, 201 insertions(+), 236 deletions(-) delete mode 100644 platform/lang-impl/src/com/intellij/build/output/BuildOutputInstantReaderImpl.java create mode 100644 platform/lang-impl/src/com/intellij/build/output/BuildOutputInstantReaderImpl.kt diff --git a/platform/lang-impl/src/com/intellij/build/output/BuildOutputInstantReaderImpl.java b/platform/lang-impl/src/com/intellij/build/output/BuildOutputInstantReaderImpl.java deleted file mode 100644 index fc4e6a06a954..000000000000 --- a/platform/lang-impl/src/com/intellij/build/output/BuildOutputInstantReaderImpl.java +++ /dev/null @@ -1,235 +0,0 @@ -// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. -package com.intellij.build.output; - -import com.intellij.build.BuildProgressListener; -import com.intellij.build.events.BuildEvent; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.util.Ref; -import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.annotations.TestOnly; - -import java.io.Closeable; -import java.util.LinkedList; -import java.util.List; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedTransferQueue; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.Consumer; - -/** - * @author Vladislav.Soroka - */ -public class BuildOutputInstantReaderImpl implements BuildOutputInstantReader, Closeable, Appendable { - private static final Logger LOG = Logger.getInstance("#com.intellij.build.output.BuildOutputInstantReader"); - private static final int MAX_LINES_BUFFER_SIZE = 50; - - private final Object myBuildId; - @Nullable - private StringBuilder myBuffer; - private final BlockingQueue myQueue = new LinkedTransferQueue<>(); - - private int myCurrentIndex = -1; - private final LinkedList myLinesBuffer = new LinkedList<>(); - private static final String SHUTDOWN_PILL = new String("Poison Pill Shutdown"); - - private final Thread myThread; - private final AtomicBoolean myStarted = new AtomicBoolean(); - private final AtomicBoolean myClosed = new AtomicBoolean(); - - public BuildOutputInstantReaderImpl(@NotNull Object buildId, - @NotNull BuildProgressListener buildProgressListener, - @NotNull List parsers) { - myBuildId = buildId; - myThread = new Thread(() -> { - Ref lastMessageRef = Ref.create(); - Consumer messageConsumer = event -> { - //do not add duplicates, e.g. sometimes same messages can be added both to stdout and stderr - if (!event.equals(lastMessageRef.get())) { - buildProgressListener.onEvent(event); - } - lastMessageRef.set(event); - }; - - String line; - while ((line = this.readLine()) != null) { - if (line.trim().isEmpty()) { - continue; - } - for (BuildOutputParser parser : parsers) { - if (parser.parse(line, new BuildOutputInstantReaderWrapper(this), messageConsumer)) { - break; - } - } - } - }, "Build output processor"); - } - - @Override - public Object getBuildId() { - return myBuildId; - } - - @Override - public BuildOutputInstantReaderImpl append(CharSequence csq) { - for (int i = 0; i < csq.length(); i++) { - append(csq.charAt(i)); - } - return this; - } - - @Override - public BuildOutputInstantReaderImpl append(CharSequence csq, int start, int end) { - append(csq.subSequence(start, end)); - return this; - } - - @Override - public BuildOutputInstantReaderImpl append(char c) { - if (myBuffer == null) { - myBuffer = new StringBuilder(); - } - if (c == '\n') { - doFlush(); - } - else { - myBuffer.append(c); - } - return this; - } - - @Override - public void close() { - doFlush(); - try { - myQueue.put(SHUTDOWN_PILL); - myThread.join(TimeUnit.MINUTES.toMillis(1)); - } - catch (InterruptedException ignore) { - } - finally { - myClosed.set(true); - } - } - - private void doFlush() { - if (myBuffer == null) { - return; - } - if (myClosed.get()) { - LOG.warn("Build output reader closed"); - myBuffer.setLength(0); - return; - } - - String line = myBuffer.toString(); - myBuffer.setLength(0); - try { - if (myStarted.compareAndSet(false, true)) { - myThread.start(); - } - myQueue.put(line); - } - catch (InterruptedException ignore) { - myClosed.set(true); - } - } - - @Nullable - @Override - public String readLine() { - if (myCurrentIndex < -1) { - LOG.error("Wrong buffered output lines index"); - myCurrentIndex = -1; - } - if (myClosed.get()) { - if (myCurrentIndex > 0 && myLinesBuffer.size() > myCurrentIndex) { - return myLinesBuffer.get(myCurrentIndex++); - } - return null; - } - - myCurrentIndex++; - if (myLinesBuffer.size() > myCurrentIndex) { - return myLinesBuffer.get(myCurrentIndex); - } - try { - String line = myQueue.take(); - if (line == SHUTDOWN_PILL) { - myClosed.set(true); - return null; - } - myLinesBuffer.addLast(line); - if (myLinesBuffer.size() > MAX_LINES_BUFFER_SIZE) { - myLinesBuffer.removeFirst(); - myCurrentIndex--; - } - return line; - } - catch (InterruptedException ignore) { - myClosed.set(true); - } - return null; - } - - @Override - public void pushBack() { - pushBack(1); - } - - @Override - public void pushBack(int numberOfLines) { - myCurrentIndex -= numberOfLines; - } - - @Override - public String getCurrentLine() { - return myCurrentIndex >= 0 && myLinesBuffer.size() > myCurrentIndex ? myLinesBuffer.get(myCurrentIndex) : null; - } - - @ApiStatus.Experimental - @TestOnly - static int getMaxLinesBufferSize() { - return MAX_LINES_BUFFER_SIZE; - } - - private static class BuildOutputInstantReaderWrapper implements BuildOutputInstantReader { - private final BuildOutputInstantReaderImpl myReader; - private int myLinesRead = 0; - - public BuildOutputInstantReaderWrapper(@NotNull BuildOutputInstantReaderImpl reader) {myReader = reader;} - - @Override - public Object getBuildId() { - return myReader.myBuildId; - } - - @Nullable - @Override - public String readLine() { - String line = myReader.readLine(); - if (line != null) myLinesRead++; - return line; - } - - @Override - public void pushBack() { - pushBack(1); - } - - @Override - public void pushBack(int numberOfLines) { - if (numberOfLines > myLinesRead) { - numberOfLines = myLinesRead; - } - myReader.pushBack(numberOfLines); - } - - @Override - public String getCurrentLine() { - return myReader.getCurrentLine(); - } - } -} diff --git a/platform/lang-impl/src/com/intellij/build/output/BuildOutputInstantReaderImpl.kt b/platform/lang-impl/src/com/intellij/build/output/BuildOutputInstantReaderImpl.kt new file mode 100644 index 000000000000..e4a09b530327 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/build/output/BuildOutputInstantReaderImpl.kt @@ -0,0 +1,200 @@ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.build.output + +import com.intellij.build.BuildProgressListener +import com.intellij.build.events.BuildEvent +import com.intellij.openapi.diagnostic.Logger +import com.intellij.openapi.util.Ref +import org.jetbrains.annotations.ApiStatus +import org.jetbrains.annotations.TestOnly +import java.io.Closeable +import java.util.* +import java.util.concurrent.LinkedTransferQueue +import java.util.concurrent.TimeUnit +import java.util.concurrent.atomic.AtomicBoolean + +/** + * @author Vladislav.Soroka + */ +class BuildOutputInstantReaderImpl(private val myBuildId: Any, + buildProgressListener: BuildProgressListener, + parsers: List) : BuildOutputInstantReader, Closeable, Appendable { + private var myBuffer: StringBuilder? = null + private val myQueue = LinkedTransferQueue() + + private val myLinesBuffer = LinkedList() + private var myCurrentIndex = -1 + + private val myThread: Thread + private val myStarted = AtomicBoolean() + private val myClosed = AtomicBoolean() + + init { + myThread = Thread({ + val lastMessageRef = Ref.create() + val messageConsumer = { event: BuildEvent -> + //do not add duplicates, e.g. sometimes same messages can be added both to stdout and stderr + if (event != lastMessageRef.get()) { + buildProgressListener.onEvent(event) + } + lastMessageRef.set(event) + } + + while (true) { + val line = this.readLine() ?: break + if (line.isBlank()) continue + + for (parser in parsers) { + if (parser.parse(line, BuildOutputInstantReaderWrapper(this), messageConsumer)) { + break + } + } + } + }, "Build output processor") + } + + override fun getBuildId(): Any { + return myBuildId + } + + override fun append(csq: CharSequence): BuildOutputInstantReaderImpl { + for (i in 0 until csq.length) { + append(csq[i]) + } + return this + } + + override fun append(csq: CharSequence, start: Int, end: Int): BuildOutputInstantReaderImpl { + append(csq.subSequence(start, end)) + return this + } + + override fun append(c: Char): BuildOutputInstantReaderImpl { + if (myBuffer == null) { + myBuffer = StringBuilder() + } + if (c == '\n') { + doFlush() + } + else { + myBuffer!!.append(c) + } + return this + } + + override fun close() { + doFlush() + try { + myQueue.put(SHUTDOWN_PILL) + myThread.join(TimeUnit.MINUTES.toMillis(1)) + } + catch (ignore: InterruptedException) { + } + finally { + myClosed.set(true) + } + } + + private fun doFlush() { + if (myBuffer == null) { + return + } + if (myClosed.get()) { + LOG.warn("Build output reader closed") + myBuffer!!.setLength(0) + return + } + + val line = myBuffer!!.toString() + myBuffer!!.setLength(0) + try { + if (myStarted.compareAndSet(false, true)) { + myThread.start() + } + myQueue.put(line) + } + catch (ignore: InterruptedException) { + myClosed.set(true) + } + + } + + override fun readLine(): String? { + if (myCurrentIndex < -1) { + LOG.error("Wrong buffered output lines index") + myCurrentIndex = -1 + } + if (myClosed.get()) { + return if (myCurrentIndex > 0 && myLinesBuffer.size > myCurrentIndex) { + myLinesBuffer[myCurrentIndex++] + } + else null + } + + myCurrentIndex++ + if (myLinesBuffer.size > myCurrentIndex) { + return myLinesBuffer[myCurrentIndex] + } + try { + val line = myQueue.take() + if (line === SHUTDOWN_PILL) { + myClosed.set(true) + return null + } + myLinesBuffer.addLast(line) + if (myLinesBuffer.size > getMaxLinesBufferSize()) { + myLinesBuffer.removeFirst() + myCurrentIndex-- + } + return line + } + catch (ignore: InterruptedException) { + myClosed.set(true) + } + + return null + } + + override fun pushBack() = pushBack(1) + + override fun pushBack(numberOfLines: Int) { + myCurrentIndex -= numberOfLines + } + + override fun getCurrentLine(): String? { + return if (myCurrentIndex >= 0 && myLinesBuffer.size > myCurrentIndex) myLinesBuffer[myCurrentIndex] else null + } + + private class BuildOutputInstantReaderWrapper(private val myReader: BuildOutputInstantReaderImpl) : BuildOutputInstantReader { + private var myLinesRead = 0 + + override fun getBuildId(): Any = myReader.myBuildId + + override fun readLine(): String? { + val line = myReader.readLine() + if (line != null) myLinesRead++ + return line + } + + override fun pushBack() = pushBack(1) + + override fun pushBack(numberOfLines: Int) { + if (numberOfLines > myLinesRead) { + myReader.pushBack(myLinesRead) + } + else { + myReader.pushBack(numberOfLines) + } + } + + override fun getCurrentLine(): String? = myReader.currentLine + } + + companion object { + private val LOG = Logger.getInstance("#com.intellij.build.output.BuildOutputInstantReader") + @ApiStatus.Experimental + @TestOnly + fun getMaxLinesBufferSize() = 50 + private const val SHUTDOWN_PILL = "Poison Pill Shutdown" + } +} diff --git a/platform/lang-impl/testSources/com/intellij/build/output/BuildOutputInstantReaderImplTest.kt b/platform/lang-impl/testSources/com/intellij/build/output/BuildOutputInstantReaderImplTest.kt index 8c7088cb9809..ef26d3316a67 100644 --- a/platform/lang-impl/testSources/com/intellij/build/output/BuildOutputInstantReaderImplTest.kt +++ b/platform/lang-impl/testSources/com/intellij/build/output/BuildOutputInstantReaderImplTest.kt @@ -5,7 +5,7 @@ import com.intellij.build.BuildProgressListener import com.intellij.build.events.MessageEvent import com.intellij.build.events.MessageEvent.Kind.* import com.intellij.build.events.impl.MessageEventImpl -import com.intellij.build.output.BuildOutputInstantReaderImpl.getMaxLinesBufferSize +import com.intellij.build.output.BuildOutputInstantReaderImpl.Companion.getMaxLinesBufferSize import com.intellij.openapi.util.text.StringUtil import org.junit.Assert import org.junit.Test