com.vaadin.terminal.gwt.client.VDebugConsole.java Source code

Java tutorial

Introduction

Here is the source code for com.vaadin.terminal.gwt.client.VDebugConsole.java

Source

/*
@VaadinApache2LicenseForJavaFiles@
 */

package com.vaadin.terminal.gwt.client;

import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JsArray;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.Style;
import com.google.gwt.dom.client.Style.FontWeight;
import com.google.gwt.dom.client.Style.Overflow;
import com.google.gwt.dom.client.Style.Position;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.event.shared.UmbrellaException;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.Response;
import com.google.gwt.http.client.UrlBuilder;
import com.google.gwt.user.client.Cookies;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Event.NativePreviewEvent;
import com.google.gwt.user.client.Event.NativePreviewHandler;
import com.google.gwt.user.client.EventPreview;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.Window.Location;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.terminal.gwt.client.ui.VLazyExecutor;
import com.vaadin.terminal.gwt.client.ui.VOverlay;

/**
 * A helper console for client side development. The debug console can also be
 * used to resolve layout issues, inspect the communication between browser and
 * the server, start GWT dev mode and restart application.
 * 
 * <p>
 * This implementation is used vaadin is in debug mode (see manual) and
 * developer appends "?debug" query parameter to url. Debug information can also
 * be shown on browsers internal console only, by appending "?debug=quiet" query
 * parameter.
 * <p>
 * This implementation can be overridden with GWT deferred binding.
 * 
 */
public class VDebugConsole extends VOverlay implements Console {

    private final class HighlightModeHandler implements NativePreviewHandler {
        private final Label label;

        private HighlightModeHandler(Label label) {
            this.label = label;
        }

        public void onPreviewNativeEvent(NativePreviewEvent event) {
            if (event.getTypeInt() == Event.ONKEYDOWN
                    && event.getNativeEvent().getKeyCode() == KeyCodes.KEY_ESCAPE) {
                highlightModeRegistration.removeHandler();
                VUIDLBrowser.deHiglight();
                return;
            }
            if (event.getTypeInt() == Event.ONMOUSEMOVE) {
                VUIDLBrowser.deHiglight();
                Element eventTarget = Util.getElementFromPoint(event.getNativeEvent().getClientX(),
                        event.getNativeEvent().getClientY());
                if (getElement().isOrHasChild(eventTarget)) {
                    return;
                }

                for (ApplicationConnection a : ApplicationConfiguration.getRunningApplications()) {
                    Paintable paintable = Util.getPaintableForElement(a, a.getView(), eventTarget);
                    if (paintable == null) {
                        paintable = Util.getPaintableForElement(a, RootPanel.get(), eventTarget);
                    }
                    if (paintable != null) {
                        String pid = a.getPid(paintable);
                        VUIDLBrowser.highlight(paintable);
                        label.setText("Currently focused  :" + paintable.getClass() + " ID:" + pid);
                        event.cancel();
                        event.consume();
                        event.getNativeEvent().stopPropagation();
                        return;
                    }
                }
            }
            if (event.getTypeInt() == Event.ONCLICK) {
                VUIDLBrowser.deHiglight();
                event.cancel();
                event.consume();
                event.getNativeEvent().stopPropagation();
                highlightModeRegistration.removeHandler();
                Element eventTarget = Util.getElementFromPoint(event.getNativeEvent().getClientX(),
                        event.getNativeEvent().getClientY());
                for (ApplicationConnection a : ApplicationConfiguration.getRunningApplications()) {
                    Paintable paintable = Util.getPaintableForElement(a, a.getView(), eventTarget);
                    if (paintable == null) {
                        paintable = Util.getPaintableForElement(a, RootPanel.get(), eventTarget);
                    }

                    if (paintable != null) {
                        a.highlightComponent(paintable);
                        return;
                    }
                }
            }
            event.cancel();
        }
    }

    private static final String POS_COOKIE_NAME = "VDebugConsolePos";

    private HandlerRegistration highlightModeRegistration;

    Element caption = DOM.createDiv();

    private Panel panel;

    private Button clear = new Button("C");
    private Button restart = new Button("R");
    private Button forceLayout = new Button("FL");
    private Button analyzeLayout = new Button("AL");
    private Button savePosition = new Button("S");
    private Button highlight = new Button("H");
    private CheckBox hostedMode = new CheckBox("GWT");
    private CheckBox autoScroll = new CheckBox("Autoscroll ");
    private HorizontalPanel actions;
    private boolean collapsed = false;

    private boolean resizing;
    private int startX;
    private int startY;
    private int initialW;
    private int initialH;

    private boolean moving = false;

    private int origTop;

    private int origLeft;

    private static final String help = "Drag title=move, shift-drag=resize, doubleclick title=min/max."
            + "Use debug=quiet to log only to browser console.";

    public VDebugConsole() {
        super(false, false);
        getElement().getStyle().setOverflow(Overflow.HIDDEN);
        clear.setTitle("Clear console");
        restart.setTitle("Restart app");
        forceLayout.setTitle("Force layout");
        analyzeLayout.setTitle("Analyze layouts");
        savePosition.setTitle("Save pos");
    }

    private EventPreview dragpreview = new EventPreview() {

        public boolean onEventPreview(Event event) {
            onBrowserEvent(event);
            return false;
        }
    };

    private boolean quietMode;

    @Override
    public void onBrowserEvent(Event event) {
        super.onBrowserEvent(event);
        switch (DOM.eventGetType(event)) {
        case Event.ONMOUSEDOWN:
            if (DOM.eventGetShiftKey(event)) {
                resizing = true;
                DOM.setCapture(getElement());
                startX = DOM.eventGetScreenX(event);
                startY = DOM.eventGetScreenY(event);
                initialW = VDebugConsole.this.getOffsetWidth();
                initialH = VDebugConsole.this.getOffsetHeight();
                DOM.eventCancelBubble(event, true);
                DOM.eventPreventDefault(event);
                DOM.addEventPreview(dragpreview);
            } else if (DOM.eventGetTarget(event) == caption) {
                moving = true;
                startX = DOM.eventGetScreenX(event);
                startY = DOM.eventGetScreenY(event);
                origTop = getAbsoluteTop();
                origLeft = getAbsoluteLeft();
                DOM.eventCancelBubble(event, true);
                DOM.eventPreventDefault(event);
                DOM.addEventPreview(dragpreview);
            }

            break;
        case Event.ONMOUSEMOVE:
            if (resizing) {
                int deltaX = startX - DOM.eventGetScreenX(event);
                int detalY = startY - DOM.eventGetScreenY(event);
                int w = initialW - deltaX;
                if (w < 30) {
                    w = 30;
                }
                int h = initialH - detalY;
                if (h < 40) {
                    h = 40;
                }
                VDebugConsole.this.setPixelSize(w, h);
                DOM.eventCancelBubble(event, true);
                DOM.eventPreventDefault(event);
            } else if (moving) {
                int deltaX = startX - DOM.eventGetScreenX(event);
                int detalY = startY - DOM.eventGetScreenY(event);
                int left = origLeft - deltaX;
                if (left < 0) {
                    left = 0;
                }
                int top = origTop - detalY;
                if (top < 0) {
                    top = 0;
                }
                VDebugConsole.this.setPopupPosition(left, top);
                DOM.eventCancelBubble(event, true);
                DOM.eventPreventDefault(event);
            }
            break;
        case Event.ONLOSECAPTURE:
        case Event.ONMOUSEUP:
            if (resizing) {
                DOM.releaseCapture(getElement());
                resizing = false;
            } else if (moving) {
                DOM.releaseCapture(getElement());
                moving = false;
            }
            DOM.removeEventPreview(dragpreview);
            break;
        case Event.ONDBLCLICK:
            if (DOM.eventGetTarget(event) == caption) {
                if (collapsed) {
                    panel.setVisible(true);
                    setToDefaultSizeAndPos();
                } else {
                    panel.setVisible(false);
                    setPixelSize(120, 20);
                    setPopupPosition(Window.getClientWidth() - 125, Window.getClientHeight() - 25);
                }
                collapsed = !collapsed;
            }
            break;
        default:
            break;
        }

    }

    private void setToDefaultSizeAndPos() {
        String cookie = Cookies.getCookie(POS_COOKIE_NAME);
        int width, height, top, left;
        boolean autoScrollValue = false;
        if (cookie != null) {
            String[] split = cookie.split(",");
            left = Integer.parseInt(split[0]);
            top = Integer.parseInt(split[1]);
            width = Integer.parseInt(split[2]);
            height = Integer.parseInt(split[3]);
            autoScrollValue = Boolean.valueOf(split[4]);
        } else {
            width = 400;
            height = 150;
            top = Window.getClientHeight() - 160;
            left = Window.getClientWidth() - 410;
        }
        setPixelSize(width, height);
        setPopupPosition(left, top);
        autoScroll.setValue(autoScrollValue);
    }

    @Override
    public void setPixelSize(int width, int height) {
        if (height < 20) {
            height = 20;
        }
        if (width < 2) {
            width = 2;
        }
        panel.setHeight((height - 20) + "px");
        panel.setWidth((width - 2) + "px");
        getElement().getStyle().setWidth(width, Unit.PX);
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.vaadin.terminal.gwt.client.Console#log(java.lang.String)
     */
    public void log(String msg) {
        if (msg == null) {
            msg = "null";
        }
        // remoteLog(msg);

        logToDebugWindow(msg, false);
        GWT.log(msg);
        consoleLog(msg);
    }

    private List<String> msgQueue = new LinkedList<String>();

    private ScheduledCommand doSend = new ScheduledCommand() {
        public void execute() {
            if (!msgQueue.isEmpty()) {
                RequestBuilder requestBuilder = new RequestBuilder(RequestBuilder.POST, getRemoteLogUrl());
                try {
                    String requestData = "";
                    for (String str : msgQueue) {
                        requestData += str;
                        requestData += "\n";
                    }
                    requestBuilder.sendRequest(requestData, new RequestCallback() {

                        public void onResponseReceived(Request request, Response response) {
                            // TODO Auto-generated method stub

                        }

                        public void onError(Request request, Throwable exception) {
                            // TODO Auto-generated method stub

                        }
                    });
                } catch (RequestException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                msgQueue.clear();
            }
        }

    };
    private VLazyExecutor sendToRemoteLog = new VLazyExecutor(350, doSend);

    protected String getRemoteLogUrl() {
        return "http://sun-vehje.local:8080/remotelog/";
    }

    protected void remoteLog(String msg) {
        msgQueue.add(msg);
        sendToRemoteLog.trigger();
    }

    /**
     * Logs the given message to the debug window.
     * 
     * @param msg
     *            The message to log. Must not be null.
     */
    private void logToDebugWindow(String msg, boolean error) {
        Widget row;
        if (error) {
            row = createErrorHtml(msg);
        } else {
            row = new HTML(msg);
        }
        panel.add(row);
        if (autoScroll.getValue()) {
            row.getElement().scrollIntoView();
        }
    }

    private HTML createErrorHtml(String msg) {
        HTML html = new HTML(msg);
        html.getElement().getStyle().setColor("#f00");
        html.getElement().getStyle().setFontWeight(FontWeight.BOLD);
        return html;
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.vaadin.terminal.gwt.client.Console#error(java.lang.String)
     */
    public void error(String msg) {
        if (msg == null) {
            msg = "null";
        }

        logToDebugWindow(msg, true);

        GWT.log(msg);
        consoleErr(msg);
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.vaadin.terminal.gwt.client.Console#printObject(java.lang.
     * Object)
     */
    public void printObject(Object msg) {
        String str;
        if (msg == null) {
            str = "null";
        } else {
            str = msg.toString();
        }
        panel.add((new Label(str)));
        consoleLog(str);
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.vaadin.terminal.gwt.client.Console#dirUIDL(com.vaadin
     * .terminal.gwt.client.UIDL)
     */
    public void dirUIDL(ValueMap u, ApplicationConfiguration conf) {
        if (panel.isAttached()) {
            VUIDLBrowser vuidlBrowser = new VUIDLBrowser(u, conf);
            vuidlBrowser.setText("Response:");
            panel.add(vuidlBrowser);
        }
        consoleDir(u);
        // consoleLog(u.getChildrenAsXML());
    }

    private static native void consoleDir(ValueMap u)
    /*-{
     if($wnd.console && $wnd.console.log) {
         if($wnd.console.dir) {
             $wnd.console.dir(u);
         } else {
             $wnd.console.log(u);
         }
     }
        
    }-*/;

    private static native void consoleLog(String msg)
    /*-{
     if($wnd.console && $wnd.console.log) {
         $wnd.console.log(msg);
     }
     }-*/;

    private static native void consoleErr(String msg)
    /*-{
     if($wnd.console) {
         if ($wnd.console.error)
             $wnd.console.error(msg);
         else if ($wnd.console.log)
             $wnd.console.log(msg);
     }
     }-*/;

    public void printLayoutProblems(ValueMap meta, ApplicationConnection ac, Set<Paintable> zeroHeightComponents,
            Set<Paintable> zeroWidthComponents) {
        JsArray<ValueMap> valueMapArray = meta.getJSValueMapArray("invalidLayouts");
        int size = valueMapArray.length();
        panel.add(new HTML("<div>************************</di>"
                + "<h4>Layouts analyzed on server, total top level problems: " + size + " </h4>"));
        if (size > 0) {
            SimpleTree root = new SimpleTree("Root problems");

            for (int i = 0; i < size; i++) {
                printLayoutError(valueMapArray.get(i), root, ac);
            }
            panel.add(root);

        }
        if (zeroHeightComponents.size() > 0 || zeroWidthComponents.size() > 0) {
            panel.add(new HTML(
                    "<h4> Client side notifications</h4>" + " <em>The following relative sized components were "
                            + "rendered to a zero size container on the client side."
                            + " Note that these are not necessarily invalid "
                            + "states, but reported here as they might be.</em>"));
            if (zeroHeightComponents.size() > 0) {
                panel.add(new HTML("<p><strong>Vertically zero size:</strong><p>"));
                printClientSideDetectedIssues(zeroHeightComponents, ac);
            }
            if (zeroWidthComponents.size() > 0) {
                panel.add(new HTML("<p><strong>Horizontally zero size:</strong><p>"));
                printClientSideDetectedIssues(zeroWidthComponents, ac);
            }
        }
        log("************************");
    }

    private void printClientSideDetectedIssues(Set<Paintable> zeroHeightComponents, ApplicationConnection ac) {
        for (final Paintable paintable : zeroHeightComponents) {
            final Container layout = Util.getLayout((Widget) paintable);

            VerticalPanel errorDetails = new VerticalPanel();
            errorDetails
                    .add(new Label("" + Util.getSimpleName(paintable) + " inside " + Util.getSimpleName(layout)));
            final CheckBox emphasisInUi = new CheckBox(
                    "Emphasize components parent in UI (the actual component is not visible)");
            emphasisInUi.addClickHandler(new ClickHandler() {
                public void onClick(ClickEvent event) {
                    if (paintable != null) {
                        Element element2 = ((Widget) layout).getElement();
                        Widget.setStyleName(element2, "invalidlayout", emphasisInUi.getValue());
                    }
                }
            });
            errorDetails.add(emphasisInUi);
            panel.add(errorDetails);
        }
    }

    private void printLayoutError(ValueMap valueMap, SimpleTree root, final ApplicationConnection ac) {
        final String pid = valueMap.getString("id");
        final Paintable paintable = ac.getPaintable(pid);

        SimpleTree errorNode = new SimpleTree();
        VerticalPanel errorDetails = new VerticalPanel();
        errorDetails.add(new Label(Util.getSimpleName(paintable) + " id: " + pid));
        if (valueMap.containsKey("heightMsg")) {
            errorDetails.add(new Label("Height problem: " + valueMap.getString("heightMsg")));
        }
        if (valueMap.containsKey("widthMsg")) {
            errorDetails.add(new Label("Width problem: " + valueMap.getString("widthMsg")));
        }
        final CheckBox emphasisInUi = new CheckBox("Emphasize component in UI");
        emphasisInUi.addClickHandler(new ClickHandler() {
            public void onClick(ClickEvent event) {
                if (paintable != null) {
                    Element element2 = ((Widget) paintable).getElement();
                    Widget.setStyleName(element2, "invalidlayout", emphasisInUi.getValue());
                }
            }
        });
        errorDetails.add(emphasisInUi);
        errorNode.add(errorDetails);
        if (valueMap.containsKey("subErrors")) {
            HTML l = new HTML("<em>Expand this node to show problems that may be dependent on this problem.</em>");
            errorDetails.add(l);
            JsArray<ValueMap> suberrors = valueMap.getJSValueMapArray("subErrors");
            for (int i = 0; i < suberrors.length(); i++) {
                ValueMap value = suberrors.get(i);
                printLayoutError(value, errorNode, ac);
            }

        }
        root.add(errorNode);
    }

    public void log(Throwable e) {
        if (e instanceof UmbrellaException) {
            UmbrellaException ue = (UmbrellaException) e;
            for (Throwable t : ue.getCauses()) {
                log(t);
            }
            return;
        }
        log(Util.getSimpleName(e) + ": " + e.getMessage());
        GWT.log(e.getMessage(), e);
    }

    public void error(Throwable e) {
        if (e instanceof UmbrellaException) {
            UmbrellaException ue = (UmbrellaException) e;
            for (Throwable t : ue.getCauses()) {
                error(t);
            }
            return;
        }
        error(Util.getSimpleName(e) + ": " + e.getMessage());
        GWT.log(e.getMessage(), e);
    }

    public void init() {
        panel = new FlowPanel();
        if (!quietMode) {
            DOM.appendChild(getContainerElement(), caption);
            setWidget(panel);
            caption.setClassName("v-debug-console-caption");
            setStyleName("v-debug-console");
            getElement().getStyle().setZIndex(20000);
            getElement().getStyle().setOverflow(Overflow.HIDDEN);

            sinkEvents(Event.ONDBLCLICK);

            sinkEvents(Event.MOUSEEVENTS);

            panel.setStyleName("v-debug-console-content");

            caption.setInnerHTML("Debug window");
            caption.getStyle().setHeight(25, Unit.PX);
            caption.setTitle(help);

            show();
            setToDefaultSizeAndPos();

            actions = new HorizontalPanel();
            Style style = actions.getElement().getStyle();
            style.setPosition(Position.ABSOLUTE);
            style.setBackgroundColor("#666");
            style.setLeft(135, Unit.PX);
            style.setHeight(25, Unit.PX);
            style.setTop(0, Unit.PX);

            actions.add(clear);
            actions.add(restart);
            actions.add(forceLayout);
            actions.add(analyzeLayout);
            actions.add(highlight);
            highlight.setTitle(
                    "Select a component and print details about it to the server log and client side console.");
            actions.add(savePosition);
            savePosition.setTitle("Saves the position and size of debug console to a cookie");
            actions.add(autoScroll);
            actions.add(hostedMode);
            if (Location.getParameter("gwt.codesvr") != null) {
                hostedMode.setValue(true);
            }
            hostedMode.addClickHandler(new ClickHandler() {
                public void onClick(ClickEvent event) {
                    if (hostedMode.getValue()) {
                        addHMParameter();
                    } else {
                        removeHMParameter();
                    }
                }

                private void addHMParameter() {
                    UrlBuilder createUrlBuilder = Location.createUrlBuilder();
                    createUrlBuilder.setParameter("gwt.codesvr", "localhost:9997");
                    Location.assign(createUrlBuilder.buildString());
                }

                private void removeHMParameter() {
                    UrlBuilder createUrlBuilder = Location.createUrlBuilder();
                    createUrlBuilder.removeParameter("gwt.codesvr");
                    Location.assign(createUrlBuilder.buildString());

                }
            });

            autoScroll.setTitle("Automatically scroll so that new messages are visible");

            panel.add(actions);

            panel.add(new HTML("<i>" + help + "</i>"));

            clear.addClickHandler(new ClickHandler() {
                public void onClick(ClickEvent event) {
                    int width = panel.getOffsetWidth();
                    int height = panel.getOffsetHeight();
                    panel = new FlowPanel();
                    panel.setPixelSize(width, height);
                    panel.setStyleName("v-debug-console-content");
                    panel.add(actions);
                    setWidget(panel);
                }
            });

            restart.addClickHandler(new ClickHandler() {
                public void onClick(ClickEvent event) {

                    String queryString = Window.Location.getQueryString();
                    if (queryString != null && queryString.contains("restartApplications")) {
                        Window.Location.reload();
                    } else {
                        String url = Location.getHref();
                        String separator = "?";
                        if (url.contains("?")) {
                            separator = "&";
                        }
                        if (!url.contains("restartApplication")) {
                            url += separator;
                            url += "restartApplication";
                        }
                        if (!"".equals(Location.getHash())) {
                            String hash = Location.getHash();
                            url = url.replace(hash, "") + hash;
                        }
                        Window.Location.replace(url);
                    }

                }
            });

            forceLayout.addClickHandler(new ClickHandler() {
                public void onClick(ClickEvent event) {
                    // TODO for each client in appconf force layout
                    // VDebugConsole.this.client.forceLayout();
                }
            });

            analyzeLayout.addClickHandler(new ClickHandler() {
                public void onClick(ClickEvent event) {
                    List<ApplicationConnection> runningApplications = ApplicationConfiguration
                            .getRunningApplications();
                    for (ApplicationConnection applicationConnection : runningApplications) {
                        applicationConnection.analyzeLayouts();
                    }
                }
            });
            analyzeLayout.setTitle("Analyzes currently rendered view and "
                    + "reports possible common problems in usage of relative sizes."
                    + "Will cause server visit/rendering of whole screen and loss of"
                    + " all non committed variables form client side.");

            savePosition.addClickHandler(new ClickHandler() {
                public void onClick(ClickEvent event) {
                    String pos = getAbsoluteLeft() + "," + getAbsoluteTop() + "," + getOffsetWidth() + ","
                            + getOffsetHeight() + "," + autoScroll.getValue();
                    Cookies.setCookie(POS_COOKIE_NAME, pos);
                }
            });

            highlight.addClickHandler(new ClickHandler() {

                public void onClick(ClickEvent event) {
                    final Label label = new Label("--");
                    log("<i>Use mouse to select a component or click ESC to exit highlight mode.</i>");
                    panel.add(label);
                    highlightModeRegistration = Event.addNativePreviewHandler(new HighlightModeHandler(label));

                }
            });

        }
        log("Starting Vaadin client side engine. Widgetset: " + GWT.getModuleName());

        log("Widget set is built on version: " + ApplicationConfiguration.VERSION);

        logToDebugWindow("<div class=\"v-theme-version v-theme-version-"
                + ApplicationConfiguration.VERSION.replaceAll("\\.", "_") + "\">Warning: widgetset version "
                + ApplicationConfiguration.VERSION + " does not seem to match theme version </div>", true);

    }

    public void setQuietMode(boolean quietDebugMode) {
        quietMode = quietDebugMode;
    }
}