001    // Copyright 2008 Thiago H. de Paula Figueiredo
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 br.com.arsmachina.tapestrycrud.components;
016    
017    
018    import org.apache.tapestry5.BindingConstants;
019    import org.apache.tapestry5.ComponentResources;
020    import org.apache.tapestry5.Field;
021    import org.apache.tapestry5.MarkupWriter;
022    import org.apache.tapestry5.ValidationDecorator;
023    import org.apache.tapestry5.annotations.AfterRender;
024    import org.apache.tapestry5.annotations.BeginRender;
025    import org.apache.tapestry5.annotations.Environmental;
026    import org.apache.tapestry5.annotations.IncludeStylesheet;
027    import org.apache.tapestry5.annotations.Mixin;
028    import org.apache.tapestry5.annotations.Parameter;
029    import org.apache.tapestry5.annotations.SupportsInformalParameters;
030    import org.apache.tapestry5.corelib.components.Label;
031    import org.apache.tapestry5.corelib.mixins.DiscardBody;
032    import org.apache.tapestry5.dom.Element;
033    import org.apache.tapestry5.ioc.annotations.Inject;
034    import org.apache.tapestry5.services.Heartbeat;
035    
036    import br.com.arsmachina.tapestrycrud.Constants;
037    
038    /**
039     * <p>
040     * {@link Label} subclass that always ignores its body and generates the label name from the
041     * corresponding field id.
042     * </p>
043     * <p>
044     * This class' code was initially copied from {@link Label}, as one of its methods (<code>afterRender</code>)
045     * has package visibility and thus cannot be overriden.
046     * </p>
047     * 
048     * @author Thiago H. de Paula Figueiredo
049     */
050    @SupportsInformalParameters
051    @IncludeStylesheet(Constants.TAPESTRY_CRUD_CSS_ASSET)
052    public class ImprovedLabel {
053    
054            /**
055             * Discards the body of this class.
056             */
057            @SuppressWarnings("unused")
058            @Mixin
059            private DiscardBody discardBody;
060    
061            /**
062             * The for parameter is used to identify the {@link Field} linked to this label (it is named
063             * this way because it results in the for attribute of the label element).
064             */
065            @Parameter(name = "for", required = true, defaultPrefix = BindingConstants.COMPONENT)
066            private Field field;
067    
068            @Environmental
069            private Heartbeat heartbeat;
070    
071            @Environmental
072            private ValidationDecorator decorator;
073    
074            @Inject
075            private ComponentResources resources;
076    
077            private Element labelElement;
078    
079            @BeginRender
080            void begin(MarkupWriter writer) {
081                    final Field field = this.field;
082    
083                    decorator.beforeLabel(field);
084    
085                    labelElement = writer.element("label");
086    
087                    resources.renderInformalParameters(writer);
088    
089                    // Since we don't know if the field has rendered yet, we need to defer writing the for and
090                    // id
091                    // attributes until we know the field has rendered (and set its clientId property). That's
092                    // exactly what Heartbeat is for.
093    
094                    Runnable command = new Runnable() {
095    
096                            public void run() {
097                                    String fieldId = field.getClientId();
098    
099                                    labelElement.forceAttributes("for", fieldId, "id", fieldId + ":label");
100    
101                                    decorator.insideLabel(field, labelElement);
102                            }
103                    };
104    
105                    heartbeat.defer(command);
106            }
107    
108            @AfterRender
109            void after(MarkupWriter writer) {
110                    // If the Label element has a body that renders some non-blank output, that takes
111                    // precendence
112                    // over the label string provided by the field.
113    
114                    // removed code from label. the rest was not modified.
115    
116                    // boolean bodyIsBlank = InternalUtils.isBlank(labelElement.getChildMarkup());
117                    //
118                    // if (bodyIsBlank)
119                    // writer.write(field.getLabel());
120    
121                    writer.write(field.getLabel());
122    
123                    writer.end(); // label
124    
125                    decorator.afterLabel(field);
126    
127            }
128    }