/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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.apache.wicket.markup.html.debug; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import org.apache.wicket.Component; import org.apache.wicket.util.io.IClusterable; import org.apache.wicket.MetaDataKey; import org.apache.wicket.Page; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.list.ListItem; import org.apache.wicket.markup.html.list.ListView; import org.apache.wicket.markup.html.panel.Panel; import org.apache.wicket.util.lang.Bytes; import org.apache.wicket.util.string.Strings; import org.apache.wicket.util.visit.IVisit; import org.apache.wicket.util.visit.IVisitor; /** * This is a simple Wicket component that displays all components of a Page in a table * representation. Useful for debugging. * <p> * Simply add this code to your page's constructor: * * <pre> * add(new PageView("componentTree", this)); * </pre> * * And this to your markup: * * <pre> * <span wicket:id="componentTree"/> * </pre> * * @author Juergen Donnerstag */ public final class PageView extends Panel { /** * A meta data key used by RenderPerformaceListener in wicket-devutils to collect the time * needed by a component to render itself */ public static final MetaDataKey<Long> RENDER_KEY = new MetaDataKey<Long>() { }; /** * El cheapo data holder. * * @author Juergen Donnerstag */ private static class ComponentData implements IClusterable { private static final long serialVersionUID = 1L; /** Component path. */ public final String path; /** Component type. */ public final String type; /** Component value. */ public String value; /** Size of component in bytes */ public final long size; /** the time it took to rended the component */ private Long renderDuration; ComponentData(String path, String type, long size) { this.path = path; this.type = type; this.size = size; } } private static final long serialVersionUID = 1L; /** * Constructor. * * @param id * See Component * @param page * The page to be analyzed * @see Component#Component(String) */ public PageView(final String id, final Page page) { super(id); // Name of page add(new Label("info", page == null ? "[Stateless Page]" : page.toString())); // Create an empty list. It'll be filled later List<ComponentData> data = null; String pageRenderDuration = "n/a"; if (page != null) { Long renderTime = page.getMetaData(RENDER_KEY); if (renderTime != null) { pageRenderDuration = renderTime.toString(); } // Get the components data and fill and sort the list data = new ArrayList<ComponentData>(getComponentData(page)); Collections.sort(data, new Comparator<ComponentData>() { @Override public int compare(ComponentData o1, ComponentData o2) { return (o1).path.compareTo((o2).path); } }); } else { data = Collections.emptyList(); } add(new Label("pageRenderDuration", pageRenderDuration)); // Create the table containing the list the components add(new ListView<ComponentData>("components", data) { private static final long serialVersionUID = 1L; /** * Populate the table with Wicket elements */ @Override protected void populateItem(final ListItem<ComponentData> listItem) { final ComponentData componentData = listItem.getModelObject(); listItem.add(new Label("row", Long.toString(listItem.getIndex() + 1))); listItem.add(new Label("path", componentData.path)); listItem.add(new Label("size", Bytes.bytes(componentData.size).toString())); listItem.add(new Label("type", componentData.type)); listItem.add(new Label("model", componentData.value)); listItem.add(new Label("renderDuration", componentData.renderDuration != null ? componentData.renderDuration.toString() : "n/a")); } }); } /** * Get recursively all components of the page, extract the information relevant for us and add * them to a list. * * @param page * @return List of component data objects */ private List<ComponentData> getComponentData(final Page page) { final List<ComponentData> data = new ArrayList<ComponentData>(); page.visitChildren(new IVisitor<Component, Void>() { @Override public void component(final Component component, final IVisit<Void> visit) { if (!component.getPath().startsWith(PageView.this.getPath())) { final ComponentData componentData; // anonymous class? Get the parent's class name String name = component.getClass().getName(); if (name.indexOf("$") > 0) { name = component.getClass().getSuperclass().getName(); } // remove the path component name = Strings.lastPathComponent(name, Component.PATH_SEPARATOR); componentData = new ComponentData(component.getPageRelativePath(), name, component.getSizeInBytes()); Long renderDuration = component.getMetaData(RENDER_KEY); if (renderDuration != null) { componentData.renderDuration = renderDuration; } try { componentData.value = component.getDefaultModelObjectAsString(); } catch (Exception e) { componentData.value = e.getMessage(); } data.add(componentData); } } }); return data; } }