Package org.gradle.logging.internal

Source Code of org.gradle.logging.internal.AnsiConsole$Widget

/*
* 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;
import java.util.Iterator;

public class AnsiConsole implements Console {
    private final static String EOL = System.getProperty("line.separator");
    private final Appendable target;
    private final Flushable flushable;
    private LabelImpl statusBar;
    private final TextAreaImpl textArea;
    private final Screen container;
    private final ColorMap colorMap;

    public AnsiConsole(Appendable target, Flushable flushable, ColorMap colorMap) {
        this.target = target;
        this.flushable = flushable;
        this.colorMap = colorMap;
        container = new Screen();
        textArea = new TextAreaImpl(container);
    }

    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() {
        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 AbstractStyledTextOutput 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 doAppend(final String text) {
            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);

                    Iterator<String> tokenizer = new LineSplitter(text);
                    while (tokenizer.hasNext()) {
                        String token = tokenizer.next();
                        if (token.equals(EOL)) {
                            width = 0;
                            extraEol = false;
                        } else {
                            width += token.length();
                        }
                        ansi.a(token);
                    }

                    color.off(ansi);
                }
            });
        }
    }

    private static class LineSplitter implements Iterator<String> {
        private final CharSequence text;
        private int start;
        private int end;

        private LineSplitter(CharSequence text) {
            this.text = text;
            findNext();
        }

        public boolean findNext() {
            if (end == text.length()) {
                start = -1;
                return false;
            }
            if (startsWithEol(text, end)) {
                start = end;
                end = start + EOL.length();
                return true;
            }
            int pos = end;
            while (pos < text.length()) {
                if (startsWithEol(text, pos)) {
                    start = end;
                    end = pos;
                    return true;
                }
                pos++;
            }
            start = end;
            end = text.length();
            return true;
        }

        private boolean startsWithEol(CharSequence text, int startAt) {
            if (startAt + EOL.length() > text.length()) {
                return false;
            }
            for (int i = 0; i < EOL.length(); i++) {
                if (EOL.charAt(i) != text.charAt(startAt + i)) {
                    return false;
                }
            }
            return true;
        }

        public boolean hasNext() {
            return start >= 0;
        }

        public String next() {
            CharSequence next = text.subSequence(start, end);
            findNext();
            return next.toString();
        }

        public void remove() {
            throw new UnsupportedOperationException();
        }
    }
}
TOP

Related Classes of org.gradle.logging.internal.AnsiConsole$Widget

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.