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}