org.eclipse.che.ide.console.OutputConsoleViewImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.che.ide.console.OutputConsoleViewImpl.java

Source

/*
 * Copyright (c) 2012-2017 Red Hat, Inc.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *   Red Hat, Inc. - initial API and implementation
 */
package org.eclipse.che.ide.console;

import static com.google.common.collect.Lists.newArrayList;
import static com.google.gwt.regexp.shared.RegExp.compile;
import static org.eclipse.che.ide.ui.menu.PositionController.HorizontalAlign.MIDDLE;
import static org.eclipse.che.ide.ui.menu.PositionController.VerticalAlign.BOTTOM;

import com.google.common.base.Strings;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Node;
import com.google.gwt.dom.client.NodeList;
import com.google.gwt.dom.client.PreElement;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.ScrollEvent;
import com.google.gwt.event.dom.client.ScrollHandler;
import com.google.gwt.regexp.shared.MatchResult;
import com.google.gwt.regexp.shared.RegExp;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlUtils;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.Anchor;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.DockLayoutPanel;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ScrollPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.inject.Inject;
import java.util.List;
import org.eclipse.che.ide.CoreLocalizationConstant;
import org.eclipse.che.ide.FontAwesome;
import org.eclipse.che.ide.machine.MachineResources;
import org.eclipse.che.ide.ui.Tooltip;
import org.eclipse.che.ide.util.Pair;
import org.vectomatic.dom.svg.ui.SVGImage;

/**
 * View representation of output console.
 *
 * @author Artem Zatsarynnyi
 * @author Vitaliy Guliy
 */
public class OutputConsoleViewImpl extends Composite implements OutputConsoleView, ScrollHandler {

    private final List<Pair<RegExp, String>> output2Color = newArrayList(
            new Pair<>(compile("\\[\\s*(DOCKER)\\s*\\]"), "#4EABFF"),
            new Pair<>(compile("\\[\\s*(ERROR)\\s*\\]"), "#FF2727"),
            new Pair<>(compile("\\[\\s*(WARN)\\s*\\]"), "#F5A623"),
            new Pair<>(compile("\\[\\s*(STDOUT)\\s*\\]"), "#8ED72B"),
            new Pair<>(compile("\\[\\s*(STDERR)\\s*\\]"), "#FF4343"));

    interface OutputConsoleViewUiBinder extends UiBinder<Widget, OutputConsoleViewImpl> {
    }

    private static final OutputConsoleViewUiBinder UI_BINDER = GWT.create(OutputConsoleViewUiBinder.class);

    private ActionDelegate delegate;

    @UiField
    protected DockLayoutPanel consolePanel;

    @UiField
    protected FlowPanel commandPanel;

    @UiField
    protected FlowPanel previewPanel;

    @UiField
    Label commandTitle;

    @UiField
    Label commandLabel;

    @UiField
    ScrollPanel scrollPanel;

    @UiField
    FlowPanel consoleLines;

    @UiField
    Anchor previewUrlLabel;

    @UiField
    protected FlowPanel reRunProcessButton;

    @UiField
    protected FlowPanel stopProcessButton;

    @UiField
    protected FlowPanel clearOutputsButton;

    @UiField
    protected FlowPanel downloadOutputsButton;

    @UiField
    FlowPanel wrapTextButton;

    @UiField
    FlowPanel scrollToBottomButton;

    /** If true - next printed line should replace the previous one. */
    private boolean carriageReturn;

    /** Follow the output. Scroll to the bottom automatically when <b>true</b>. */
    private boolean followOutput = true;

    /** Scroll to the bottom immediately when view become visible. */
    private boolean followScheduled = false;

    @Inject
    public OutputConsoleViewImpl(MachineResources resources, CoreLocalizationConstant localization) {
        initWidget(UI_BINDER.createAndBindUi(this));

        reRunProcessButton.add(new SVGImage(resources.reRunIcon()));
        stopProcessButton.add(new SVGImage(resources.stopIcon()));
        clearOutputsButton.add(new SVGImage(resources.clearOutputsIcon()));
        downloadOutputsButton.getElement().setInnerHTML(FontAwesome.DOWNLOAD);

        wrapTextButton.add(new SVGImage(resources.lineWrapIcon()));
        scrollToBottomButton.add(new SVGImage(resources.scrollToBottomIcon()));

        scrollPanel.addDomHandler(this, ScrollEvent.getType());

        reRunProcessButton.addDomHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                if (!reRunProcessButton.getElement().hasAttribute("disabled") && delegate != null) {
                    delegate.reRunProcessButtonClicked();
                }
            }
        }, ClickEvent.getType());

        stopProcessButton.addDomHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                if (!stopProcessButton.getElement().hasAttribute("disabled") && delegate != null) {
                    delegate.stopProcessButtonClicked();
                }
            }
        }, ClickEvent.getType());

        clearOutputsButton.addDomHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                if (!clearOutputsButton.getElement().hasAttribute("disabled") && delegate != null) {
                    delegate.clearOutputsButtonClicked();
                }
            }
        }, ClickEvent.getType());

        downloadOutputsButton.addDomHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                if (delegate != null) {
                    delegate.downloadOutputsButtonClicked();
                }
            }
        }, ClickEvent.getType());

        wrapTextButton.addDomHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent clickEvent) {
                if (!wrapTextButton.getElement().hasAttribute("disabled") && delegate != null) {
                    delegate.wrapTextButtonClicked();
                }
            }
        }, ClickEvent.getType());

        scrollToBottomButton.addDomHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                if (!scrollToBottomButton.getElement().hasAttribute("disabled") && delegate != null) {
                    delegate.scrollToBottomButtonClicked();
                }
            }
        }, ClickEvent.getType());

        Tooltip.create((elemental.dom.Element) reRunProcessButton.getElement(), BOTTOM, MIDDLE,
                localization.consolesReRunButtonTooltip());

        Tooltip.create((elemental.dom.Element) stopProcessButton.getElement(), BOTTOM, MIDDLE,
                localization.consolesStopButtonTooltip());

        Tooltip.create((elemental.dom.Element) clearOutputsButton.getElement(), BOTTOM, MIDDLE,
                localization.consolesClearOutputsButtonTooltip());

        Tooltip.create((elemental.dom.Element) wrapTextButton.getElement(), BOTTOM, MIDDLE,
                localization.consolesWrapTextButtonTooltip());

        Tooltip.create((elemental.dom.Element) scrollToBottomButton.getElement(), BOTTOM, MIDDLE,
                localization.consolesAutoScrollButtonTooltip());
    }

    @Override
    public void setDelegate(ActionDelegate delegate) {
        this.delegate = delegate;
    }

    @Override
    public void hideCommand() {
        consolePanel.setWidgetHidden(commandPanel, true);
    }

    @Override
    public void hidePreview() {
        consolePanel.setWidgetHidden(previewPanel, true);
    }

    @Override
    public void wrapText(boolean wrap) {
        if (wrap) {
            consoleLines.getElement().setAttribute("wrap", "");
        } else {
            consoleLines.getElement().removeAttribute("wrap");
        }
    }

    @Override
    public void enableAutoScroll(boolean enable) {
        followOutput = enable;
        followOutput();
    }

    @Override
    public void clearConsole() {
        consoleLines.getElement().setInnerHTML("");
    }

    @Override
    public void toggleWrapTextButton(boolean toggle) {
        if (toggle) {
            wrapTextButton.getElement().setAttribute("toggled", "");
        } else {
            wrapTextButton.getElement().removeAttribute("toggled");
        }
    }

    @Override
    public void toggleScrollToEndButton(boolean toggle) {
        if (toggle) {
            scrollToBottomButton.getElement().setAttribute("toggled", "");
        } else {
            scrollToBottomButton.getElement().removeAttribute("toggled");
        }
    }

    @Override
    public void setReRunButtonVisible(boolean visible) {
        reRunProcessButton.setVisible(visible);
    }

    @Override
    public void setStopButtonVisible(boolean visible) {
        stopProcessButton.setVisible(visible);
    }

    @Override
    public void enableStopButton(boolean enable) {
        if (enable) {
            stopProcessButton.getElement().removeAttribute("disabled");
        } else {
            stopProcessButton.getElement().setAttribute("disabled", "");
        }
    }

    @Override
    public void showCommandLine(String commandLine) {
        commandLabel.setText(commandLine);
        Tooltip.create((elemental.dom.Element) commandLabel.getElement(), BOTTOM, MIDDLE, commandLine);
    }

    @Override
    public void showPreviewUrl(String previewUrl) {
        if (Strings.isNullOrEmpty(previewUrl)) {
            hidePreview();
        } else {
            previewUrlLabel.setText(previewUrl);
            previewUrlLabel.setHref(previewUrl);
            Tooltip.create((elemental.dom.Element) previewUrlLabel.getElement(), BOTTOM, MIDDLE, previewUrl);
        }
    }

    @Override
    public void print(String text, boolean carriageReturn) {
        print(text, carriageReturn, null);
    }

    @Override
    public void print(final String text, boolean carriageReturn, String color) {
        if (this.carriageReturn) {
            Node lastChild = consoleLines.getElement().getLastChild();
            if (lastChild != null) {
                lastChild.removeFromParent();
            }
        }

        this.carriageReturn = carriageReturn;

        final SafeHtml colorOutput = new SafeHtml() {
            @Override
            public String asString() {

                if (Strings.isNullOrEmpty(text)) {
                    return " ";
                }

                String encoded = SafeHtmlUtils.htmlEscape(text);
                if (delegate != null) {
                    if (delegate.getCustomizer() != null) {
                        if (delegate.getCustomizer().canCustomize(encoded)) {
                            encoded = delegate.getCustomizer().customize(encoded);
                        }
                    }
                }

                for (final Pair<RegExp, String> pair : output2Color) {
                    final MatchResult matcher = pair.first.exec(encoded);

                    if (matcher != null) {
                        return encoded.replaceAll(matcher.getGroup(1),
                                "<span style=\"color: " + pair.second + "\">" + matcher.getGroup(1) + "</span>");
                    }
                }

                return encoded;
            }
        };

        PreElement pre = DOM.createElement("pre").cast();
        pre.setInnerSafeHtml(colorOutput);
        if (color != null) {
            pre.getStyle().setColor(color);
        }
        consoleLines.getElement().appendChild(pre);

        followOutput();
    }

    @Override
    public String getText() {
        String text = "";
        NodeList<Node> nodes = consoleLines.getElement().getChildNodes();

        for (int i = 0; i < nodes.getLength(); i++) {
            Node node = nodes.getItem(i);
            Element element = node.cast();
            text += element.getInnerText() + "\r\n";
        }

        return text;
    }

    @Override
    public void onScroll(ScrollEvent event) {
        // Do nothing if content height less scroll area height
        if (scrollPanel.getElement().getScrollHeight() < scrollPanel.getElement().getOffsetHeight()) {
            followOutput = true;
            if (delegate != null) {
                delegate.onOutputScrolled(followOutput);
            }
            return;
        }

        // Follow output if scroll area is scrolled to the end
        if (scrollPanel.getElement().getScrollTop() + scrollPanel.getElement().getOffsetHeight() >= scrollPanel
                .getElement().getScrollHeight()) {
            followOutput = true;
        } else {
            followOutput = false;
        }

        if (delegate != null) {
            delegate.onOutputScrolled(followOutput);
        }
    }

    /** Scrolls to the bottom if following the output is enabled. */
    private void followOutput() {
        if (!followOutput) {
            return;
        }

        /** Scroll bottom immediately if view is visible */
        if (scrollPanel.getElement().getOffsetParent() != null) {
            scrollPanel.scrollToBottom();
            scrollPanel.scrollToLeft();
            return;
        }

        /** Otherwise, check the visibility periodically and scroll the view when it's visible */
        if (!followScheduled) {
            followScheduled = true;

            Scheduler.get().scheduleFixedPeriod(new Scheduler.RepeatingCommand() {
                @Override
                public boolean execute() {
                    if (!followOutput) {
                        followScheduled = false;
                        return false;
                    }

                    if (scrollPanel.getElement().getOffsetParent() != null) {
                        scrollPanel.scrollToBottom();
                        scrollPanel.scrollToLeft();
                        followScheduled = false;
                        return false;
                    }

                    return true;
                }
            }, 500);
        }
    }
}