001    // Copyright 2006, 2007, 2008, 2009, 2010, 2011 The Apache Software Foundation
002    //
003    // Licensed under the Apache License, Version 2.0 (the "License");
004    // you may not use this file except in compliance with the License.
005    // You may obtain a copy of the License at
006    //
007    // http://www.apache.org/licenses/LICENSE-2.0
008    //
009    // Unless required by applicable law or agreed to in writing, software
010    // distributed under the License is distributed on an "AS IS" BASIS,
011    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012    // See the License for the specific language governing permissions and
013    // limitations under the License.
014    
015    package org.apache.tapestry5.corelib.components;
016    
017    import org.apache.tapestry5.BindingConstants;
018    import org.apache.tapestry5.ComponentResources;
019    import org.apache.tapestry5.Field;
020    import org.apache.tapestry5.MarkupWriter;
021    import org.apache.tapestry5.ValidationDecorator;
022    import org.apache.tapestry5.annotations.Environmental;
023    import org.apache.tapestry5.annotations.HeartbeatDeferred;
024    import org.apache.tapestry5.annotations.Parameter;
025    import org.apache.tapestry5.annotations.SupportsInformalParameters;
026    import org.apache.tapestry5.dom.Element;
027    import org.apache.tapestry5.ioc.annotations.Inject;
028    import org.apache.tapestry5.ioc.internal.util.InternalUtils;
029    
030    /**
031     * Generates a <label> element for a particular field.
032     * <p/>
033     * A Label will render its body, if it has one. However, in most cases it will not have a body, and will render its
034     * {@linkplain org.apache.tapestry5.Field#getLabel() field's label} as it's body. Remember, however, that it is the
035     * field label that will be used in any error messages. The Label component allows for client- and server-side
036     * validation error decorations.
037     * 
038     * @tapestrydoc
039     */
040    @SupportsInformalParameters
041    public class Label
042    {
043        /**
044         * The for parameter is used to identify the {@link Field} linked to this label (it is named this way because it
045         * results in the for attribute of the label element).
046         */
047        @Parameter(name = "for", required = true, allowNull = false, defaultPrefix = BindingConstants.COMPONENT)
048        private Field field;
049    
050        @Environmental
051        private ValidationDecorator decorator;
052    
053        @Inject
054        private ComponentResources resources;
055    
056        /**
057         * If true, then the body of the label element (in the template) is ignored. This is used when a designer places a
058         * value inside the &lt;label&gt; element for WYSIWYG purposes, but it should be replaced with a different
059         * (probably, localized) value at runtime. The default is false, so a body will be used if present and the field's
060         * label will only be used if the body is empty or blank.
061         */
062        @Parameter
063        private boolean ignoreBody;
064    
065        private Element labelElement;
066    
067        boolean beginRender(MarkupWriter writer)
068        {
069            decorator.beforeLabel(field);
070    
071            labelElement = writer.element("label");
072    
073            resources.renderInformalParameters(writer);
074    
075            // Since we don't know if the field has rendered yet, we need to defer writing the for and id
076            // attributes until we know the field has rendered (and set its clientId property). That's
077            // exactly what Heartbeat is for.
078    
079            updateAttributes();
080    
081            return !ignoreBody;
082        }
083    
084        @HeartbeatDeferred
085        private void updateAttributes()
086        {
087            String fieldId = field.getClientId();
088    
089            labelElement.forceAttributes("for", fieldId);
090    
091            decorator.insideLabel(field, labelElement);
092        }
093    
094        void afterRender(MarkupWriter writer)
095        {
096            // If the Label element has a body that renders some non-blank output, that takes precedence
097            // over the label string provided by the field.
098    
099            boolean bodyIsBlank = InternalUtils.isBlank(labelElement.getChildMarkup());
100    
101            if (bodyIsBlank)
102                writer.write(field.getLabel());
103    
104            writer.end(); // label
105    
106            decorator.afterLabel(field);
107        }
108    }