/* * Copyright 2000-2009 JetBrains s.r.o. * * 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.community.intellij.plugins.communitycase.commands; import com.intellij.execution.process.ProcessOutputTypes; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Key; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.EventDispatcher; import org.jetbrains.annotations.NotNull; import java.io.File; import java.util.ArrayList; import java.util.Iterator; import java.util.List; /** * The handler that is based on per-line processing of the text. */ public class LineHandler extends TextHandler { /** * the partial line from stdout stream */ private final StringBuilder myStdoutLine = new StringBuilder(); /** * the partial line from stderr stream */ private final StringBuilder myStderrLine = new StringBuilder(); /** * Line listeners */ private final EventDispatcher<LineHandlerListener> myLineListeners = EventDispatcher.create(LineHandlerListener.class); /** * A constructor * * @param project a project * @param directory a process directory * @param command a command to execute */ @SuppressWarnings({"WeakerAccess"}) public LineHandler(@NotNull Project project, @NotNull File directory, @NotNull Command command) { super(project, directory, command); } /** * A constructor * * @param project a project * @param vcsRoot a process directory * @param command a command to execute */ public LineHandler(@NotNull final Project project, @NotNull final VirtualFile vcsRoot, @NotNull final Command command) { super(project, vcsRoot, command); } /** * {@inheritDoc} */ protected void processTerminated(final int exitCode) { // force newline if (myStdoutLine.length() != 0) { onTextAvailable("\n\r", ProcessOutputTypes.STDOUT); } else if (!isStderrSuppressed() && myStderrLine.length() != 0) { onTextAvailable("\n\r", ProcessOutputTypes.STDERR); } } /** * Add listener * * @param listener a listener to add */ public void addLineListener(LineHandlerListener listener) { super.addListener(listener); myLineListeners.addListener(listener); } /** * {@inheritDoc} */ protected void onTextAvailable(final String text, final Key outputType) { Iterator<String> lines = splitText(text).iterator(); if (ProcessOutputTypes.STDOUT == outputType) { notifyLines(outputType, lines, myStdoutLine); } else if (ProcessOutputTypes.STDERR == outputType) { notifyLines(outputType, lines, myStderrLine); } } /** * Notify listeners for each complete line. Note that in the case of stderr, the last line is saved. * * @param outputType output type * @param lines line iterator * @param lineBuilder a line builder */ private void notifyLines(final Key outputType, final Iterator<String> lines, final StringBuilder lineBuilder) { /* while(lines.hasNext()) { String line = lines.next(); //lineBuilder.append(line); notifyLine(line, outputType); } */ if (!lines.hasNext()) return; if (lineBuilder.length() > 0) { lineBuilder.append(lines.next()); if (lines.hasNext()) { // line is complete final String line = lineBuilder.toString(); notifyLine(line, outputType); lineBuilder.setLength(0); } } while (true) { String line = lines.next(); if (lines.hasNext()) { notifyLine(line, outputType); } else { if (line.length() > 0) { lineBuilder.append(line); } break; } } } /** * Notify single line * * @param line a line to notify * @param outputType output type */ private void notifyLine(final String line, final Key outputType) { String trimmed = trimLineSeparator(line); // if line ends with return, then it is a progress line, ignore it if (myVcs != null && !"\r".equals(line.substring(trimmed.length()))) { if (outputType == ProcessOutputTypes.STDOUT && !isStdoutSuppressed()) { myVcs.showMessages(trimmed); } else if (outputType == ProcessOutputTypes.STDERR && !isStderrSuppressed()) { myVcs.showErrorMessages(trimmed); } } myLineListeners.getMulticaster().onLineAvailable(trimmed, outputType); } /** * Trim line separator from new line if it presents * * @param line a line to process * @return a trimmed line */ private static String trimLineSeparator(String line) { int n = line.length(); if (n == 0) { return line; } char ch = line.charAt(n - 1); if (ch == '\n' || ch == '\r') { n--; } else { return line; } if (n > 0) { char ch2 = line.charAt(n - 1); if ((ch2 == '\n' || ch2 == '\r') && ch2 != ch) { n--; } } return line.substring(0, n); } /** * Split text into lines. New line characters are treated as separators. So if the text starts * with newline, empty string will be the first element, if the text ends with new line, the * empty string will be the last element. The returned lines will be substrings of * the text argument. The new line characters are included into the line text. * * @param text a text to split * @return a list of elements (note that there are always at least one element) */ private static List<String> splitText(String text) { int startLine = 0; int i = 0; int n = text.length(); ArrayList<String> rc = new ArrayList<String>(); while (i < n) { switch (text.charAt(i)) { case '\n': i++; if (i < n && text.charAt(i) == '\r') { i++; } rc.add(text.substring(startLine, i)); startLine = i; break; case '\r': i++; if (i < n && text.charAt(i) == '\n') { i++; } rc.add(text.substring(startLine, i)); startLine = i; break; default: i++; } } if (startLine == text.length()) { // still add empty line or previous line wouldn't be treated as completed rc.add(""); } else { rc.add(text.substring(startLine, i)); } return rc; } }