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.markup.html.form; 018 019import org.apache.wicket.Component; 020import org.apache.wicket.MarkupContainer; 021import org.apache.wicket.core.request.handler.ComponentNotFoundException; 022import org.apache.wicket.markup.ComponentTag; 023import org.apache.wicket.markup.MarkupStream; 024import org.apache.wicket.markup.WicketTag; 025import org.apache.wicket.markup.html.WebMarkupContainer; 026import org.apache.wicket.markup.html.basic.Label; 027import org.apache.wicket.markup.html.form.AutoLabelResolver.AutoLabel; 028import org.apache.wicket.markup.html.internal.ResponseBufferZone; 029import org.apache.wicket.markup.parser.XmlTag; 030import org.apache.wicket.markup.resolver.IComponentResolver; 031import org.apache.wicket.model.IModel; 032import org.apache.wicket.model.LoadableDetachableModel; 033import org.apache.wicket.model.Model; 034import org.apache.wicket.model.StringResourceModel; 035import org.apache.wicket.request.cycle.RequestCycle; 036import org.apache.wicket.util.string.Strings; 037 038/** 039 * Resolver that provides the <code>{@literal <wicket:label>}</code> tag, which will output a 040 * FormComponent's {@link FormComponent#getLabel() label} without requiring a manual extra component 041 * such as {@link Label} or {@link FormComponentLabel}. 042 * 043 * <code>{@literal <wicket:label>}</code> can be used 044 * <ul> 045 * <li>together with <code>{@literal <label wicket:for="...">}</code>: 046 * 047 * <pre> 048 * {@literal 049 * <label wicket:for="myFormComponent">some other markup, optionally<wicket:label/></label> 050 * } 051 * </pre> 052 * 053 * </li> 054 * <li> 055 * standalone, with a <code>for</code> attribute: 056 * 057 * <pre> 058 * {@literal 059 * <wicket:label for="myFormComponent"/> 060 * } 061 * </pre> 062 * 063 * </li> 064 * </ul> 065 * <p> 066 * It also supports both input and output: 067 * <ul> 068 * <li>If the FormComponent has a label model, the <code>{@literal <wicket:label>}</code> tag will 069 * be replaced by the contents of that label.</li> 070 * <li>If the FormComponent's label model is null, it can be picked up from 071 * <code>{@literal <wicket:label>}</code>: 072 * <ul> 073 * <li><code>{@literal <wicket:label>}</code> can contain some raw markup, like this: 074 * 075 * <pre> 076 * {@literal 077 * <wicket:label>I will become the component's label!</wicket:label> 078 * } 079 * </pre> 080 * 081 * </li> 082 * <li>Or it can be a message pulled from resources, similar to 083 * <code>{@literal <wicket:message/>}</code>: 084 * 085 * <pre> 086 * {@literal 087 * <wicket:label key="messagekey"/> 088 * } 089 * </pre> 090 * 091 * </li> 092 * </ul> 093 * </li> 094 * </ul> 095 * 096 * 097 * @author Carl-Eric Menzel 098 * @author igor 099 */ 100public class AutoLabelTextResolver implements IComponentResolver 101{ 102 public static final String LABEL = "label"; 103 104 /** 105 * This is inserted by the resolver to render the label. 106 */ 107 private static class TextLabel extends WebMarkupContainer 108 { 109 110 private final Component labeled; 111 112 public TextLabel(String id, Component labeled) 113 114 { 115 super(id); 116 this.labeled = labeled; 117 setRenderBodyOnly(true); 118 } 119 120 @Override 121 protected void onComponentTag(final ComponentTag tag) 122 { 123 if (tag.isOpenClose()) 124 { 125 tag.setType(XmlTag.TagType.OPEN); 126 } 127 super.onComponentTag(tag); 128 } 129 130 @Override 131 public void onComponentTagBody(final MarkupStream markupStream, final ComponentTag openTag) 132 { 133 134 // try and find some form of label content... 135 IModel<String> labelModel = findLabelContent(markupStream, openTag); 136 // print the label text 137 replaceComponentTagBody(markupStream, openTag, 138 labelModel != null ? labelModel.getObject() : ""); 139 140 // store the label text in FormComponent's label model so its available to errors 141 if (labelModel != null) 142 { 143 if (labeled instanceof FormComponent) 144 { 145 FormComponent<?> fc = (FormComponent<?>)labeled; 146 fc.setLabel(labelModel); 147 } 148 else 149 { 150 // if we can't hand off the labelmodel to a component, we have to detach it 151 labelModel.detach(); 152 } 153 } 154 } 155 156 private IModel<String> findLabelContent(final MarkupStream markupStream, 157 final ComponentTag tag) 158 { 159 if (labeled instanceof ILabelProvider) 160 { 161 // check if the labeled component is a label provider 162 ILabelProvider<String> provider = (ILabelProvider<String>)labeled; 163 if (provider.getLabel() != null) 164 { 165 if (!Strings.isEmpty(provider.getLabel().getObject())) 166 167 { 168 return provider.getLabel(); 169 } 170 } 171 } 172 173 // check if the labeled component is a form component 174 if (labeled instanceof FormComponent) 175 { 176 final FormComponent<?> formComponent = (FormComponent<?>)labeled; 177 String text = formComponent.getDefaultLabel("wicket:unknown"); 178 if (!"wicket:unknown".equals(text) && !Strings.isEmpty(text)) 179 { 180 return new LoadableDetachableModel<String>() 181 { 182 @Override 183 protected String load() 184 { 185 return formComponent.getDefaultLabel("wicket:unknown"); 186 } 187 }; 188 } 189 } 190 191 // check if wicket:label tag has a message key 192 { 193 String resourceKey = tag.getAttribute("key"); 194 if (resourceKey != null) 195 { 196 String text = labeled.getString(resourceKey); 197 if (!Strings.isEmpty(text)) 198 { 199 return new StringResourceModel(resourceKey, labeled); 200 } 201 } 202 } 203 204 // as last resort use the tag body 205 { 206 String text = new ResponseBufferZone(RequestCycle.get(), markupStream) 207 { 208 @Override 209 protected void executeInsideBufferedZone() 210 { 211 TextLabel.super.onComponentTagBody(markupStream, tag); 212 } 213 }.execute().toString(); 214 215 if (!Strings.isEmpty(text)) 216 { 217 return Model.of(text); 218 } 219 } 220 221 return null; 222 } 223 } 224 225 @Override 226 public Component resolve(MarkupContainer container, MarkupStream markupStream, ComponentTag tag) 227 { 228 if (tag instanceof WicketTag && "label".equals(tag.getName())) 229 { 230 // We need to find a FormComponent... 231 Component related = null; 232 // ...which could be explicitly specified... 233 String forAttributeValue = tag.getAttribute("for"); 234 if (forAttributeValue != null) 235 { 236 Component component = AutoLabelResolver.findRelatedComponent(container, forAttributeValue); 237 related = component; 238 } 239 if (related == null) 240 { 241 // ...or available through an AutoLabel, either directly above us... 242 if (container instanceof AutoLabel) 243 { 244 related = ((AutoLabel)container).getRelatedComponent(); 245 } 246 if (related == null) 247 { 248 // ...or perhaps further up... 249 AutoLabel autoLabel = container.findParent(AutoLabel.class); 250 if (autoLabel != null) 251 { 252 related = autoLabel.getRelatedComponent(); 253 } 254 } 255 } 256 if (related == null) 257 { 258 // ...or it might just not be available. 259 String forAttr = forAttributeValue != null ? " for=\"" + forAttributeValue + "\"" : ""; 260 throw new ComponentNotFoundException("no related component found for <wicket:label"+forAttr+">"); 261 } 262 else 263 { 264 // ...found the form component, so we can return our label. 265 return new TextLabel(tag.getId(), related); 266 } 267 } 268 return null; 269 } 270 271}