All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.vaadin.client.debug.internal.HierarchySection Maven / Gradle / Ivy

Go to download

Vaadin is a web application framework for Rich Internet Applications (RIA). Vaadin enables easy development and maintenance of fast and secure rich web applications with a stunning look and feel and a wide browser support. It features a server-side architecture with the majority of the logic running on the server. Ajax technology is used at the browser-side to ensure a rich and interactive user experience.

There is a newer version: 8.25.2
Show newest version
/*
 * Copyright 2000-2013 Vaadin Ltd.
 * 
 * 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 com.vaadin.client.debug.internal;

import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import com.google.gwt.core.client.JsArray;
import com.google.gwt.dom.client.Style.TextDecoration;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.DoubleClickEvent;
import com.google.gwt.event.dom.client.DoubleClickHandler;
import com.google.gwt.event.dom.client.HasDoubleClickHandlers;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.MouseOutEvent;
import com.google.gwt.event.dom.client.MouseOutHandler;
import com.google.gwt.event.dom.client.MouseOverEvent;
import com.google.gwt.event.dom.client.MouseOverHandler;
import com.google.gwt.event.shared.HandlerRegistration;
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.ui.Button;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.ApplicationConfiguration;
import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.ComponentConnector;
import com.vaadin.client.ComputedStyle;
import com.vaadin.client.ConnectorMap;
import com.vaadin.client.JsArrayObject;
import com.vaadin.client.ServerConnector;
import com.vaadin.client.SimpleTree;
import com.vaadin.client.Util;
import com.vaadin.client.VConsole;
import com.vaadin.client.ValueMap;
import com.vaadin.client.metadata.NoDataException;
import com.vaadin.client.metadata.Property;
import com.vaadin.client.ui.AbstractConnector;
import com.vaadin.client.ui.UnknownComponentConnector;
import com.vaadin.shared.AbstractComponentState;
import com.vaadin.shared.communication.SharedState;

/**
 * Provides functionality for examining the UI component hierarchy.
 * 
 * @since 7.1
 * @author Vaadin Ltd
 */
public class HierarchySection implements Section {
    private final DebugButton tabButton = new DebugButton(Icon.HIERARCHY,
            "Examine component hierarchy");

    private final FlowPanel content = new FlowPanel();
    private final FlowPanel controls = new FlowPanel();

    private final Button find = new DebugButton(Icon.HIGHLIGHT,
            "Select a component on the page to inspect it");
    private final Button analyze = new DebugButton(Icon.ANALYZE,
            "Check layouts for potential problems");
    private final Button generateWS = new DebugButton(Icon.OPTIMIZE,
            "Show used connectors and how to optimize widgetset");
    private final Button showHierarchy = new DebugButton(Icon.HIERARCHY,
            "Show the connector hierarchy tree");

    private HandlerRegistration highlightModeRegistration = null;

    public HierarchySection() {
        controls.add(showHierarchy);
        showHierarchy.setStylePrimaryName(VDebugWindow.STYLENAME_BUTTON);
        showHierarchy.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                showHierarchy();
            }
        });

        controls.add(find);
        find.setStylePrimaryName(VDebugWindow.STYLENAME_BUTTON);
        find.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                toggleFind();
            }
        });

        controls.add(analyze);
        analyze.setStylePrimaryName(VDebugWindow.STYLENAME_BUTTON);
        analyze.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                stopFind();
                analyzeLayouts();
            }
        });

        controls.add(generateWS);
        generateWS.setStylePrimaryName(VDebugWindow.STYLENAME_BUTTON);
        generateWS.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                generateWidgetset();
            }
        });

        content.setStylePrimaryName(VDebugWindow.STYLENAME + "-hierarchy");

        HTML info = new HTML(showHierarchy.getHTML() + " "
                + showHierarchy.getTitle() + "
" + find.getHTML() + " " + find.getTitle() + "
" + analyze.getHTML() + " " + analyze.getTitle() + "
" + generateWS.getHTML() + " " + generateWS.getTitle() + "
"); info.setStyleName(VDebugWindow.STYLENAME + "-info"); content.add(info); } private void showHierarchy() { Highlight.hideAll(); content.clear(); // TODO Clearing and rebuilding the contents is not optimal for UX as // any previous expansions are lost. SimplePanel trees = new SimplePanel(); for (ApplicationConnection application : ApplicationConfiguration .getRunningApplications()) { ServerConnector uiConnector = application.getUIConnector(); Widget connectorTree = buildConnectorTree(uiConnector); trees.add(connectorTree); } content.add(trees); } private Widget buildConnectorTree(final ServerConnector connector) { String connectorString = Util.getConnectorString(connector); List children = connector.getChildren(); Widget widget; if (children == null || children.isEmpty()) { // Leaf node, just add a label Label label = new Label(connectorString); label.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { Highlight.showOnly(connector); Highlight.showServerDebugInfo(connector); } }); widget = label; } else { SimpleTree tree = new SimpleTree(connectorString) { @Override protected void select(ClickEvent event) { super.select(event); Highlight.showOnly(connector); Highlight.showServerDebugInfo(connector); } }; for (ServerConnector child : children) { tree.add(buildConnectorTree(child)); } widget = tree; } if (widget instanceof HasDoubleClickHandlers) { HasDoubleClickHandlers has = (HasDoubleClickHandlers) widget; has.addDoubleClickHandler(new DoubleClickHandler() { @Override public void onDoubleClick(DoubleClickEvent event) { printState(connector, true); } }); } return widget; } @Override public DebugButton getTabButton() { return tabButton; } @Override public Widget getControls() { return controls; } @Override public Widget getContent() { return content; } @Override public void show() { } @Override public void hide() { stopFind(); } private void generateWidgetset() { content.clear(); HTML h = new HTML("Getting used connectors"); content.add(h); String s = ""; for (ApplicationConnection ac : ApplicationConfiguration .getRunningApplications()) { ApplicationConfiguration conf = ac.getConfiguration(); s += "

Used connectors for " + conf.getServiceUrl() + "

"; for (String connectorName : getUsedConnectorNames(conf)) { s += connectorName + "
"; } s += "

To make an optimized widgetset based on these connectors, do:

"; s += "

1. Add to your widgetset.gwt.xml file:

"; s += ""; s += "

2. Add the following java file to your project:

"; s += ""; s += "

3. Recompile widgetset

"; } h.setHTML(s); } private Set getUsedConnectorNames( ApplicationConfiguration configuration) { int tag = 0; Set usedConnectors = new HashSet(); while (true) { String serverSideClass = configuration .getServerSideClassNameForTag(tag); if (serverSideClass == null) { break; } Class connectorClass = configuration .getConnectorClassByEncodedTag(tag); if (connectorClass == null) { break; } if (connectorClass != UnknownComponentConnector.class) { usedConnectors.add(connectorClass.getName()); } tag++; if (tag > 10000) { // Sanity check VConsole.error("Search for used connector classes was forcefully terminated"); break; } } return usedConnectors; } public String generateOptimizedWidgetSet(Set usedConnectors) { String s = "import java.util.HashSet;\n"; s += "import java.util.Set;\n"; s += "import com.google.gwt.core.ext.typeinfo.JClassType;\n"; s += "import com.vaadin.client.ui.ui.UIConnector;\n"; s += "import com.vaadin.server.widgetsetutils.ConnectorBundleLoaderFactory;\n"; s += "import com.vaadin.shared.ui.Connect.LoadStyle;\n\n"; s += "public class OptimizedConnectorBundleLoaderFactory extends\n"; s += " ConnectorBundleLoaderFactory {\n"; s += " private Set eagerConnectors = new HashSet();\n"; s += " {\n"; for (String c : usedConnectors) { s += " eagerConnectors.add(" + c + ".class.getName());\n"; } s += " }\n"; s += "\n"; s += " @Override\n"; s += " protected LoadStyle getLoadStyle(JClassType connectorType) {\n"; s += " if (eagerConnectors.contains(connectorType.getQualifiedBinaryName())) {\n"; s += " return LoadStyle.EAGER;\n"; s += " } else {\n"; s += " // Loads all other connectors immediately after the initial view has\n"; s += " // been rendered\n"; s += " return LoadStyle.DEFERRED;\n"; s += " }\n"; s += " }\n"; s += "}\n"; return s; } private void analyzeLayouts() { content.clear(); content.add(new Label("Analyzing layouts...")); List runningApplications = ApplicationConfiguration .getRunningApplications(); for (ApplicationConnection applicationConnection : runningApplications) { applicationConnection.analyzeLayouts(); } } @Override public void meta(ApplicationConnection ac, ValueMap meta) { content.clear(); JsArray valueMapArray = meta .getJSValueMapArray("invalidLayouts"); int size = valueMapArray.length(); if (size > 0) { SimpleTree root = new SimpleTree("Layouts analyzed, " + size + " top level problems"); for (int i = 0; i < size; i++) { printLayoutError(ac, valueMapArray.get(i), root); } root.open(false); content.add(root); } else { content.add(new Label("Layouts analyzed, no top level problems")); } Set zeroHeightComponents = new HashSet(); Set zeroWidthComponents = new HashSet(); findZeroSizeComponents(zeroHeightComponents, zeroWidthComponents, ac.getUIConnector()); if (zeroHeightComponents.size() > 0 || zeroWidthComponents.size() > 0) { content.add(new HTML("

Client side notifications

" + " 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.")); if (zeroHeightComponents.size() > 0) { content.add(new HTML( "

Vertically zero size:

")); printClientSideDetectedIssues(zeroHeightComponents, ac); } if (zeroWidthComponents.size() > 0) { content.add(new HTML( "

Horizontally zero size:

")); printClientSideDetectedIssues(zeroWidthComponents, ac); } } } private void printClientSideDetectedIssues( Set zeroSized, ApplicationConnection ac) { // keep track of already highlighted parents HashSet parents = new HashSet(); for (final ComponentConnector connector : zeroSized) { final ServerConnector parent = connector.getParent(); final String parentId = parent.getConnectorId(); final Label errorDetails = new Label(Util.getSimpleName(connector) + "[" + connector.getConnectorId() + "]" + " inside " + Util.getSimpleName(parent)); if (parent instanceof ComponentConnector) { final ComponentConnector parentConnector = (ComponentConnector) parent; if (!parents.contains(parentId)) { parents.add(parentId); Highlight.show(parentConnector, "yellow"); } errorDetails.addMouseOverHandler(new MouseOverHandler() { @Override public void onMouseOver(MouseOverEvent event) { Highlight.hideAll(); Highlight.show(parentConnector, "yellow"); Highlight.show(connector); errorDetails.getElement().getStyle() .setTextDecoration(TextDecoration.UNDERLINE); } }); errorDetails.addMouseOutHandler(new MouseOutHandler() { @Override public void onMouseOut(MouseOutEvent event) { Highlight.hideAll(); errorDetails.getElement().getStyle() .setTextDecoration(TextDecoration.NONE); } }); errorDetails.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { printState(connector, true); } }); } Highlight.show(connector); content.add(errorDetails); } } private void printLayoutError(ApplicationConnection ac, ValueMap valueMap, SimpleTree root) { final String pid = valueMap.getString("id"); // find connector final ComponentConnector connector = (ComponentConnector) ConnectorMap .get(ac).getConnector(pid); if (connector == null) { root.add(new SimpleTree("[" + pid + "] NOT FOUND")); return; } Highlight.show(connector); final SimpleTree errorNode = new SimpleTree( Util.getSimpleName(connector) + " id: " + pid); errorNode.addDomHandler(new MouseOverHandler() { @Override public void onMouseOver(MouseOverEvent event) { Highlight.showOnly(connector); ((Widget) event.getSource()).getElement().getStyle() .setTextDecoration(TextDecoration.UNDERLINE); } }, MouseOverEvent.getType()); errorNode.addDomHandler(new MouseOutHandler() { @Override public void onMouseOut(MouseOutEvent event) { Highlight.hideAll(); ((Widget) event.getSource()).getElement().getStyle() .setTextDecoration(TextDecoration.NONE); } }, MouseOutEvent.getType()); errorNode.addDomHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { if (event.getNativeEvent().getEventTarget().cast() == errorNode .getElement().getChild(1).cast()) { printState(connector, true); } } }, ClickEvent.getType()); VerticalPanel errorDetails = new VerticalPanel(); 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"))); } if (errorDetails.getWidgetCount() > 0) { errorNode.add(errorDetails); } if (valueMap.containsKey("subErrors")) { HTML l = new HTML( "Expand this node to show problems that may be dependent on this problem."); errorDetails.add(l); JsArray suberrors = valueMap .getJSValueMapArray("subErrors"); for (int i = 0; i < suberrors.length(); i++) { ValueMap value = suberrors.get(i); printLayoutError(ac, value, errorNode); } } root.add(errorNode); } private void findZeroSizeComponents( Set zeroHeightComponents, Set zeroWidthComponents, ComponentConnector connector) { Widget widget = connector.getWidget(); ComputedStyle computedStyle = new ComputedStyle(widget.getElement()); if (computedStyle.getIntProperty("height") == 0) { zeroHeightComponents.add(connector); } if (computedStyle.getIntProperty("width") == 0) { zeroWidthComponents.add(connector); } List children = connector.getChildren(); for (ServerConnector serverConnector : children) { if (serverConnector instanceof ComponentConnector) { findZeroSizeComponents(zeroHeightComponents, zeroWidthComponents, (ComponentConnector) serverConnector); } } } @Override public void uidl(ApplicationConnection ac, ValueMap uidl) { // NOP } private boolean isFindMode() { return (highlightModeRegistration != null); } private void toggleFind() { if (isFindMode()) { stopFind(); } else { startFind(); } } private void startFind() { Highlight.hideAll(); if (!isFindMode()) { highlightModeRegistration = Event .addNativePreviewHandler(highlightModeHandler); find.addStyleDependentName(VDebugWindow.STYLENAME_ACTIVE); } } private void stopFind() { if (isFindMode()) { highlightModeRegistration.removeHandler(); highlightModeRegistration = null; find.removeStyleDependentName(VDebugWindow.STYLENAME_ACTIVE); } } private void printState(ServerConnector connector, boolean serverDebug) { Highlight.showOnly(connector); if (serverDebug) { Highlight.showServerDebugInfo(connector); } SharedState state = connector.getState(); Set ignoreProperties = new HashSet(); ignoreProperties.add("id"); String html = getRowHTML("Id", connector.getConnectorId()); html += getRowHTML("Connector", Util.getSimpleName(connector)); if (connector instanceof ComponentConnector) { ComponentConnector component = (ComponentConnector) connector; ignoreProperties.addAll(Arrays.asList("caption", "description", "width", "height")); AbstractComponentState componentState = component.getState(); html += getRowHTML("Widget", Util.getSimpleName(component.getWidget())); html += getRowHTML("Caption", componentState.caption); html += getRowHTML("Description", componentState.description); html += getRowHTML("Width", componentState.width + " (actual: " + component.getWidget().getOffsetWidth() + "px)"); html += getRowHTML("Height", componentState.height + " (actual: " + component.getWidget().getOffsetHeight() + "px)"); } try { JsArrayObject properties = AbstractConnector .getStateType(connector).getPropertiesAsArray(); for (int i = 0; i < properties.size(); i++) { Property property = properties.get(i); String name = property.getName(); if (!ignoreProperties.contains(name)) { html += getRowHTML(property.getDisplayName(), property.getValue(state)); } } } catch (NoDataException e) { html += "
Could not read state, error has been logged to the console
"; VConsole.error(e); } content.clear(); content.add(new HTML(html)); } private String getRowHTML(String caption, Object value) { return "
" + caption + "" + Util.escapeHTML(String.valueOf(value)) + "
"; } private final NativePreviewHandler highlightModeHandler = new NativePreviewHandler() { @Override public void onPreviewNativeEvent(NativePreviewEvent event) { if (event.getTypeInt() == Event.ONKEYDOWN && event.getNativeEvent().getKeyCode() == KeyCodes.KEY_ESCAPE) { stopFind(); Highlight.hideAll(); return; } if (event.getTypeInt() == Event.ONMOUSEMOVE) { Highlight.hideAll(); Element eventTarget = Util.getElementFromPoint(event .getNativeEvent().getClientX(), event.getNativeEvent() .getClientY()); if (VDebugWindow.get().getElement().isOrHasChild(eventTarget)) { content.clear(); return; } for (ApplicationConnection a : ApplicationConfiguration .getRunningApplications()) { ComponentConnector connector = Util.getConnectorForElement( a, a.getUIConnector().getWidget(), eventTarget); if (connector == null) { connector = Util.getConnectorForElement(a, RootPanel.get(), eventTarget); } if (connector != null) { printState(connector, false); event.cancel(); event.consume(); event.getNativeEvent().stopPropagation(); return; } } content.clear(); } if (event.getTypeInt() == Event.ONCLICK) { Highlight.hideAll(); event.cancel(); event.consume(); event.getNativeEvent().stopPropagation(); stopFind(); Element eventTarget = Util.getElementFromPoint(event .getNativeEvent().getClientX(), event.getNativeEvent() .getClientY()); for (ApplicationConnection a : ApplicationConfiguration .getRunningApplications()) { ComponentConnector connector = Util.getConnectorForElement( a, a.getUIConnector().getWidget(), eventTarget); if (connector == null) { connector = Util.getConnectorForElement(a, RootPanel.get(), eventTarget); } if (connector != null) { printState(connector, true); return; } } } event.cancel(); } }; }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy