/* | |
* 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.form; | |
import org.apache.wicket.Component; | |
import org.apache.wicket.MarkupContainer; | |
import org.apache.wicket.core.request.handler.ComponentNotFoundException; | |
import org.apache.wicket.markup.ComponentTag; | |
import org.apache.wicket.markup.MarkupStream; | |
import org.apache.wicket.markup.WicketTag; | |
import org.apache.wicket.markup.html.WebMarkupContainer; | |
import org.apache.wicket.markup.html.basic.Label; | |
import org.apache.wicket.markup.html.form.AutoLabelResolver.AutoLabel; | |
import org.apache.wicket.markup.html.internal.ResponseBufferZone; | |
import org.apache.wicket.markup.parser.XmlTag; | |
import org.apache.wicket.markup.resolver.IComponentResolver; | |
import org.apache.wicket.model.IModel; | |
import org.apache.wicket.model.LoadableDetachableModel; | |
import org.apache.wicket.model.Model; | |
import org.apache.wicket.model.StringResourceModel; | |
import org.apache.wicket.request.cycle.RequestCycle; | |
import org.apache.wicket.util.string.Strings; | |
/** | |
* Resolver that provides the <code>{@literal <wicket:label>}</code> tag, which will output a | |
* FormComponent's {@link FormComponent#getLabel() label} without requiring a manual extra component | |
* such as {@link Label} or {@link FormComponentLabel}. | |
* | |
* <code>{@literal <wicket:label>}</code> can be used | |
* <ul> | |
* <li>together with <code>{@literal <label wicket:for="...">}</code>: | |
* | |
* <pre> | |
* {@literal | |
* <label wicket:for="myFormComponent">some other markup, optionally<wicket:label/></label> | |
* } | |
* </pre> | |
* | |
* </li> | |
* <li> | |
* standalone, with a <code>for</code> attribute: | |
* | |
* <pre> | |
* {@literal | |
* <wicket:label for="myFormComponent"/> | |
* } | |
* </pre> | |
* | |
* </li> | |
* </ul> | |
* <p> | |
* It also supports both input and output: | |
* <ul> | |
* <li>If the FormComponent has a label model, the <code>{@literal <wicket:label>}</code> tag will | |
* be replaced by the contents of that label.</li> | |
* <li>If the FormComponent's label model is null, it can be picked up from | |
* <code>{@literal <wicket:label>}</code>: | |
* <ul> | |
* <li><code>{@literal <wicket:label>}</code> can contain some raw markup, like this: | |
* | |
* <pre> | |
* {@literal | |
* <wicket:label>I will become the component's label!</wicket:label> | |
* } | |
* </pre> | |
* | |
* </li> | |
* <li>Or it can be a message pulled from resources, similar to | |
* <code>{@literal <wicket:message/>}</code>: | |
* | |
* <pre> | |
* {@literal | |
* <wicket:label key="messagekey"/> | |
* } | |
* </pre> | |
* | |
* </li> | |
* </ul> | |
* </li> | |
* </ul> | |
* | |
* | |
* @author Carl-Eric Menzel <cmenzel@wicketbuch.de> | |
* @author igor | |
*/ | |
public class AutoLabelTextResolver implements IComponentResolver | |
{ | |
public static final String LABEL = "label"; | |
/** | |
* This is inserted by the resolver to render the label. | |
*/ | |
private static class TextLabel extends WebMarkupContainer | |
{ | |
private final Component labeled; | |
public TextLabel(String id, Component labeled) | |
{ | |
super(id); | |
this.labeled = labeled; | |
setRenderBodyOnly(true); | |
} | |
@Override | |
protected void onComponentTag(final ComponentTag tag) | |
{ | |
if (tag.isOpenClose()) | |
{ | |
tag.setType(XmlTag.TagType.OPEN); | |
} | |
super.onComponentTag(tag); | |
} | |
@Override | |
public void onComponentTagBody(final MarkupStream markupStream, final ComponentTag openTag) | |
{ | |
// try and find some form of label content... | |
IModel<String> labelModel = findLabelContent(markupStream, openTag); | |
// print the label text | |
replaceComponentTagBody(markupStream, openTag, | |
labelModel != null ? labelModel.getObject() : ""); | |
// store the label text in FormComponent's label model so its available to errors | |
if (labelModel != null) | |
{ | |
if (labeled instanceof FormComponent) | |
{ | |
FormComponent<?> fc = (FormComponent<?>)labeled; | |
fc.setLabel(labelModel); | |
} | |
else | |
{ | |
// if we can't hand off the labelmodel to a component, we have to detach it | |
labelModel.detach(); | |
} | |
} | |
} | |
private IModel<String> findLabelContent(final MarkupStream markupStream, | |
final ComponentTag tag) | |
{ | |
if (labeled instanceof ILabelProvider) | |
{ | |
// check if the labeled component is a label provider | |
ILabelProvider<String> provider = (ILabelProvider<String>)labeled; | |
if (provider.getLabel() != null) | |
{ | |
if (!Strings.isEmpty(provider.getLabel().getObject())) | |
{ | |
return provider.getLabel(); | |
} | |
} | |
} | |
// check if the labeled component is a form component | |
if (labeled instanceof FormComponent) | |
{ | |
final FormComponent<?> formComponent = (FormComponent<?>)labeled; | |
String text = formComponent.getDefaultLabel("wicket:unknown"); | |
if (!"wicket:unknown".equals(text) && !Strings.isEmpty(text)) | |
{ | |
return new LoadableDetachableModel<String>() | |
{ | |
@Override | |
protected String load() | |
{ | |
return formComponent.getDefaultLabel("wicket:unknown"); | |
} | |
}; | |
} | |
} | |
// check if wicket:label tag has a message key | |
{ | |
String resourceKey = tag.getAttribute("key"); | |
if (resourceKey != null) | |
{ | |
String text = labeled.getString(resourceKey); | |
if (!Strings.isEmpty(text)) | |
{ | |
return new StringResourceModel(resourceKey, labeled); | |
} | |
} | |
} | |
// as last resort use the tag body | |
{ | |
String text = new ResponseBufferZone(RequestCycle.get(), markupStream) | |
{ | |
@Override | |
protected void executeInsideBufferedZone() | |
{ | |
TextLabel.super.onComponentTagBody(markupStream, tag); | |
} | |
}.execute().toString(); | |
if (!Strings.isEmpty(text)) | |
{ | |
return Model.of(text); | |
} | |
} | |
return null; | |
} | |
} | |
@Override | |
public Component resolve(MarkupContainer container, MarkupStream markupStream, ComponentTag tag) | |
{ | |
if (tag instanceof WicketTag && "label".equals(tag.getName())) | |
{ | |
// We need to find a FormComponent... | |
Component related = null; | |
// ...which could be explicitly specified... | |
String forAttributeValue = tag.getAttribute("for"); | |
if (forAttributeValue != null) | |
{ | |
Component component = AutoLabelResolver.findRelatedComponent(container, forAttributeValue); | |
related = component; | |
} | |
if (related == null) | |
{ | |
// ...or available through an AutoLabel, either directly above us... | |
if (container instanceof AutoLabel) | |
{ | |
related = ((AutoLabel)container).getRelatedComponent(); | |
} | |
if (related == null) | |
{ | |
// ...or perhaps further up... | |
AutoLabel autoLabel = container.findParent(AutoLabel.class); | |
if (autoLabel != null) | |
{ | |
related = autoLabel.getRelatedComponent(); | |
} | |
} | |
} | |
if (related == null) | |
{ | |
// ...or it might just not be available. | |
String forAttr = forAttributeValue != null ? " for=\"" + forAttributeValue + "\"" : ""; | |
throw new ComponentNotFoundException("no related component found for <wicket:label"+forAttr+">"); | |
} | |
else | |
{ | |
// ...found the form component, so we can return our label. | |
return new TextLabel(tag.getId(), related); | |
} | |
} | |
return null; | |
} | |
} |