001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.wicket.devutils.debugbar; 018 019import java.util.ArrayList; 020import java.util.List; 021 022import org.apache.wicket.Application; 023import org.apache.wicket.AttributeModifier; 024import org.apache.wicket.Component; 025import org.apache.wicket.MetaDataKey; 026import org.apache.wicket.devutils.DevUtilsPanel; 027import org.apache.wicket.markup.head.CssHeaderItem; 028import org.apache.wicket.markup.head.IHeaderResponse; 029import org.apache.wicket.markup.head.JavaScriptHeaderItem; 030import org.apache.wicket.markup.head.OnDomReadyHeaderItem; 031import org.apache.wicket.markup.html.WebMarkupContainer; 032import org.apache.wicket.markup.html.image.Image; 033import org.apache.wicket.markup.html.list.ListItem; 034import org.apache.wicket.markup.html.list.ListView; 035import org.apache.wicket.model.IModel; 036import org.apache.wicket.request.resource.CssResourceReference; 037import org.apache.wicket.request.resource.JavaScriptResourceReference; 038import org.apache.wicket.request.resource.PackageResourceReference; 039import org.apache.wicket.resource.CoreLibrariesContributor; 040import org.apache.wicket.util.lang.Args; 041 042/** 043 * The debug bar is for use during development. It allows contributors to add useful functions or 044 * data, making them readily accessible to the developer.<br /> 045 * <br /> 046 * To use it, simply add it to your base page so that all of your pages automatically have it.<br /> 047 * 048 * <br /> 049 * <code> 050 * Java: 051 * add(new DebugBar("debug")); 052 * 053 * HTML: 054 * <div wicket:id="debug"></div> 055 * </code> 056 * 057 * <br /> 058 * You can also add your own information to the bar by creating a {@link IDebugBarContributor} and 059 * registering it with the debug bar. 060 * 061 * <p>The debug bar uses CSS absolute positioning to appear in the top-right corner of the page. 062 * <strong>Important</strong>: if there is an element with a z-index in this part of your page, the DebugBar will need a higher 063 * "z-index" style value to show up. Or you can use different position for it. See wicket-debugbar.css.</p> 064 * 065 * @author Jeremy Thomerson 066 * @see IDebugBarContributor 067 */ 068public class DebugBar extends DevUtilsPanel 069{ 070 private static final long serialVersionUID = 1L; 071 072 private static final MetaDataKey<List<IDebugBarContributor>> CONTRIBS_META_KEY = new MetaDataKey<>() 073 { 074 private static final long serialVersionUID = 1L; 075 }; 076 077 078 /** 079 * Construct. 080 * <p/> 081 * Create debug bar (initially expanded) 082 * 083 * @param id 084 * component id 085 * 086 * @see #DebugBar(String, boolean) 087 */ 088 public DebugBar(final String id) 089 { 090 this(id, true); 091 } 092 093 /** 094 * Construct. 095 * 096 * @param id 097 * component id 098 * @param initiallyExpanded 099 * {@code true} to show debug bar initially expanded 100 * 101 * @see #DebugBar(String) 102 */ 103 public DebugBar(final String id, boolean initiallyExpanded) 104 { 105 super(id); 106 setMarkupId("wicketDebugBar"); 107 setOutputMarkupId(true); 108 add(AttributeModifier.replace("class", new IModel<String>() 109 { 110 private static final long serialVersionUID = 1L; 111 112 @Override 113 public String getObject() 114 { 115 return "wicketDebugBar" + (DebugBar.this.hasErrorMessage() ? "Error" : ""); 116 } 117 })); 118 119 add(new Image("logo", new PackageResourceReference(DebugBar.class, "wicket.png"))); 120 121 add(contentSection("content", initiallyExpanded)); 122 } 123 124 125 /** 126 * Positions the DebugBar at the bottom of the page 127 * @return 128 */ 129 public DebugBar positionBottom() 130 { 131 add(AttributeModifier.append("class", "bottom")); 132 return this; 133 } 134 135 private Component contentSection(final String id, boolean initiallyExpanded) 136 { 137 WebMarkupContainer section = new WebMarkupContainer(id); 138 if (!initiallyExpanded) 139 { 140 section.add(AttributeModifier.append("style", "display:none").setSeparator(";")); 141 } 142 143 List<IDebugBarContributor> contributors = getContributors(getApplication()); 144 145 section.add(new ListView<IDebugBarContributor>("contributors", contributors) 146 { 147 private static final long serialVersionUID = 1L; 148 149 @Override 150 protected void populateItem(final ListItem<IDebugBarContributor> item) 151 { 152 IDebugBarContributor contrib = item.getModelObject(); 153 Component comp = contrib.createComponent("contrib", DebugBar.this); 154 if (comp == null) 155 { 156 // some contributors only add information to the debug bar 157 // and don't actually create a contributed component 158 item.setVisibilityAllowed(false); 159 } 160 else 161 { 162 item.add(comp); 163 } 164 } 165 }); 166 167 section.add(new Image("removeImg", new PackageResourceReference(DebugBar.class, "remove.png"))); 168 169 return section; 170 } 171 172 @Override 173 public boolean isVisible() 174 { 175 return getApplication().getDebugSettings().isDevelopmentUtilitiesEnabled(); 176 } 177 178 @Override 179 public void renderHead(final IHeaderResponse response) 180 { 181 CoreLibrariesContributor.contributeAjax(getApplication(), response); 182 183 response.render(CssHeaderItem.forReference(new CssResourceReference(DebugBar.class, 184 "wicket-debugbar.css"))); 185 response.render(JavaScriptHeaderItem.forReference(new JavaScriptResourceReference( 186 DebugBar.class, "wicket-debugbar.js"))); 187 response.render(OnDomReadyHeaderItem.forScript("Wicket.debugBar();")); 188 } 189 190 /** 191 * Register your own custom contributor that will be part of the debug bar. You must have the 192 * context of an application for this thread at the time of calling this method. 193 * 194 * @param contrib 195 * custom contributor - can not be null 196 */ 197 public static void registerContributor(final IDebugBarContributor contrib) 198 { 199 registerContributor(contrib, Application.get()); 200 } 201 202 /** 203 * Register your own custom contributor that will be part of the debug bar. You must have the 204 * context of an application for this thread at the time of calling this method. 205 * 206 * @param application 207 * @param contrib 208 * custom contributor - can not be null 209 */ 210 public static void registerContributor(final IDebugBarContributor contrib, 211 final Application application) 212 { 213 Args.notNull(contrib, "contrib"); 214 215 List<IDebugBarContributor> contributors = getContributors(application); 216 contributors.add(contrib); 217 setContributors(contributors, application); 218 } 219 220 public static List<IDebugBarContributor> getContributors(final Application application) 221 { 222 List<IDebugBarContributor> list = application.getMetaData(CONTRIBS_META_KEY); 223 return list == null ? new ArrayList<IDebugBarContributor>() : list; 224 } 225 226 public static void setContributors(List<IDebugBarContributor> contributors, Application application) 227 { 228 Args.notNull(application, "application"); 229 230 application.setMetaData(CONTRIBS_META_KEY, contributors); 231 } 232}