Java tutorial
/* * Copyright 2010 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.logging.internal; import org.apache.commons.lang.StringUtils; import org.fusesource.jansi.Ansi; import org.gradle.api.Action; import org.gradle.api.UncheckedIOException; import java.io.Flushable; import java.io.IOException; public class AnsiConsole implements Console { private final Appendable target; private final Flushable flushable; private LabelImpl statusBar; private final TextAreaImpl textArea; private final Screen container; private final ColorMap colorMap; private final boolean forceAnsi; public AnsiConsole(Appendable target, Flushable flushable, ColorMap colorMap) { this(target, flushable, colorMap, false); } public AnsiConsole(Appendable target, Flushable flushable, ColorMap colorMap, boolean forceAnsi) { this.target = target; this.flushable = flushable; this.colorMap = colorMap; container = new Screen(); textArea = new TextAreaImpl(container); this.forceAnsi = forceAnsi; } public Label getStatusBar() { if (statusBar == null) { statusBar = new LabelImpl(container); render(new Action<Ansi>() { public void execute(Ansi ansi) { textArea.onDeactivate(ansi); statusBar.onActivate(ansi); } }); } return statusBar; } public TextArea getMainArea() { return textArea; } private void render(Action<Ansi> action) { Ansi ansi = createAnsi(); action.execute(ansi); try { target.append(ansi.toString()); flushable.flush(); } catch (IOException e) { throw new UncheckedIOException(e); } } Ansi createAnsi() { if (forceAnsi) { return new Ansi(); } else { return Ansi.ansi(); } } private interface Container { void redraw(Widget widget, Action<Ansi> drawOperation); void close(Widget widget); } private interface Widget { /** * Called when this widget becomes the active widget. The active widget is the widget at the bottom of the * screen. When called, the cursor will be positioned at the left edge of bottom-most line of the screen. */ void onActivate(Ansi ansi); /** * Called when this widget is no longer the active widget. Should Remove content of this widget from the last * line of the screen and leave the cursor at left edge of bottom-most line. */ void onDeactivate(Ansi ansi); } private class Screen implements Container { public void redraw(Widget widget, final Action<Ansi> drawOperation) { if (widget == textArea) { render(new Action<Ansi>() { public void execute(Ansi ansi) { if (statusBar != null) { statusBar.onDeactivate(ansi); textArea.onActivate(ansi); } drawOperation.execute(ansi); if (statusBar != null) { textArea.onDeactivate(ansi); statusBar.onActivate(ansi); } } }); } else { assert widget == statusBar; render(new Action<Ansi>() { public void execute(Ansi ansi) { drawOperation.execute(ansi); } }); } } public void close(Widget widget) { if (widget == textArea) { throw new UnsupportedOperationException(); } if (widget == statusBar) { render(new Action<Ansi>() { public void execute(Ansi ansi) { statusBar.onDeactivate(ansi); textArea.onActivate(ansi); statusBar = null; } }); } } } private class LabelImpl implements Label, Widget { private final Container container; private String text = ""; private String displayedText = ""; public LabelImpl(Container container) { this.container = container; } public void setText(String text) { if (text.equals(this.text)) { return; } this.text = text; container.redraw(this, new Action<Ansi>() { public void execute(Ansi ansi) { draw(ansi); } }); } public void close() { container.close(this); } public void onDeactivate(Ansi ansi) { if (displayedText.length() > 0) { ansi.cursorLeft(displayedText.length()); ansi.eraseLine(Ansi.Erase.FORWARD); displayedText = ""; } } public void onActivate(Ansi ansi) { draw(ansi); } public void draw(Ansi ansi) { String prefix = StringUtils.getCommonPrefix(new String[] { text, displayedText }); if (prefix.length() < displayedText.length()) { ansi.cursorLeft(displayedText.length() - prefix.length()); } if (prefix.length() < text.length()) { ColorMap.Color color = colorMap.getStatusBarColor(); color.on(ansi); ansi.a(text.substring(prefix.length())); color.off(ansi); } if (displayedText.length() > text.length()) { ansi.eraseLine(Ansi.Erase.FORWARD); } displayedText = text; } } private class TextAreaImpl extends AbstractLineChoppingStyledTextOutput implements TextArea, Widget { private final Container container; private int width; boolean extraEol; private TextAreaImpl(Container container) { this.container = container; } public void onDeactivate(Ansi ansi) { if (width > 0) { ansi.newline(); extraEol = true; } } public void onActivate(Ansi ansi) { if (extraEol) { ansi.cursorUp(1); ansi.cursorRight(width); extraEol = false; } } @Override protected void doLineText(final CharSequence text, final boolean terminatesLine) { if (text.length() == 0) { return; } container.redraw(this, new Action<Ansi>() { public void execute(Ansi ansi) { ColorMap.Color color = colorMap.getColourFor(getStyle()); color.on(ansi); if (terminatesLine) { width = 0; extraEol = false; } else { width += text.length(); } ansi.a(text.toString()); color.off(ansi); } }); } } }