blob: bf48aaae34a19844c10229dbc37c59a920083b31 [file] [log] [blame]
/*
* 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;
}
}