View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.myfaces.tobago.internal.renderkit.renderer;
21  
22  import org.apache.myfaces.tobago.component.Attributes;
23  import org.apache.myfaces.tobago.component.LabelLayout;
24  import org.apache.myfaces.tobago.component.SupportsAccessKey;
25  import org.apache.myfaces.tobago.component.SupportsLabelLayout;
26  import org.apache.myfaces.tobago.context.Markup;
27  import org.apache.myfaces.tobago.internal.component.AbstractUIStyle;
28  import org.apache.myfaces.tobago.internal.util.HtmlRendererUtils;
29  import org.apache.myfaces.tobago.internal.util.StringUtils;
30  import org.apache.myfaces.tobago.renderkit.LabelWithAccessKey;
31  import org.apache.myfaces.tobago.renderkit.css.BootstrapClass;
32  import org.apache.myfaces.tobago.renderkit.css.TobagoClass;
33  import org.apache.myfaces.tobago.renderkit.html.HtmlAttributes;
34  import org.apache.myfaces.tobago.renderkit.html.HtmlElements;
35  import org.apache.myfaces.tobago.util.ComponentUtils;
36  import org.apache.myfaces.tobago.webapp.TobagoResponseWriter;
37  
38  import javax.faces.component.UIComponent;
39  import javax.faces.context.FacesContext;
40  import java.io.IOException;
41  import java.util.List;
42  
43  /**
44   * Manages the rendering of the <b>label</b> and the <b>field</b> together with different possibilities for the position
45   * of the label (defined by {@link org.apache.myfaces.tobago.component.Attributes#labelLayout}
46   */
47  public abstract class LabelLayoutRendererBase<T extends UIComponent & SupportsLabelLayout>
48      extends DecodingInputRendererBase<T> {
49  
50    public abstract HtmlElements getComponentTag();
51  
52    @Override
53    public void encodeBeginInternal(final FacesContext facesContext, final T component) throws IOException {
54  
55      encodeBeginSurroundingLabel(facesContext, component);
56  
57      if (component.isNextToRenderIsLabel()) {
58        // skip, because its only the lable to render
59      } else {
60        encodeBeginMessageField(facesContext, component);
61      }
62    }
63  
64    @Override
65    public void encodeEndInternal(final FacesContext facesContext, final T component) throws IOException {
66  
67      if (component.isNextToRenderIsLabel()) {
68        // skip, because its only the lable to render
69      } else {
70        encodeEndMessageField(facesContext, component);
71      }
72  
73      // render the styles here, because inside of <select> its not possible.
74      if (component.getRendersChildren()) {
75        final List<AbstractUIStyle> children = ComponentUtils.findDescendantList(component, AbstractUIStyle.class);
76        for (final AbstractUIStyle child : children) {
77          child.encodeAll(facesContext);
78        }
79      }
80  
81      encodeEndSurroundingLabel(facesContext, component);
82    }
83  
84    protected void encodeAttributes(final FacesContext facesContext, final T component) throws IOException {
85    }
86  
87    @Override
88    public void encodeChildrenInternal(final FacesContext context, final T component) throws IOException {
89      if (component.getChildCount() > 0) {
90        for (int i = 0, childCount = component.getChildCount(); i < childCount; i++) {
91          final UIComponent child = component.getChildren().get(i);
92          if (!child.isRendered()) {
93            continue;
94          }
95          if (child instanceof AbstractUIStyle) {
96            // will be rendered in {@link #encodeEnd}
97            continue;
98          }
99  
100         child.encodeAll(context);
101       }
102     }
103   }
104 
105   protected abstract void encodeBeginMessageField(FacesContext facesContext, T component) throws IOException;
106 
107   protected abstract void encodeEndMessageField(FacesContext facesContext, T component) throws IOException;
108 
109   protected void encodeBeginSurroundingLabel(final FacesContext facesContext, final T component)
110       throws IOException {
111 
112     final TobagoResponseWriter writer = getResponseWriter(facesContext);
113     String clientId = component.getClientId(facesContext);
114     final Markup markup = (Markup) ComponentUtils.getAttribute(component, Attributes.markup);
115 
116     final LabelLayout labelLayout = component.getLabelLayout();
117     final boolean nextToRenderIsLabel = component.isNextToRenderIsLabel();
118     final boolean flex;
119 
120     switch (labelLayout) {
121       case flexLeft:
122       case flexRight:
123         flex = true;
124         break;
125       case segmentLeft:
126       case segmentRight:
127         if (nextToRenderIsLabel) {
128           clientId += ComponentUtils.SUB_SEPARATOR + "label";
129         }
130         flex = false;
131         break;
132       case flowLeft:
133       case gridLeft:
134       case gridRight:
135       case gridTop:
136       case gridBottom:
137         encodeLabel(facesContext, component, writer, labelLayout);
138         flex = false;
139         break;
140       case none:
141       case top:
142       case flowRight:
143       default:
144         flex = false;
145     }
146 
147 //    if (labelLayout == LabelLayout.gridLeft || labelLayout == LabelLayout.gridRight
148 //        || labelLayout == LabelLayout.gridTop || labelLayout == LabelLayout.gridBottom) {
149 //      writer.startElement(HtmlElements.LABEL);
150 //      writer.writeIdAttribute(clientId + ComponentUtils.SUB_SEPARATOR + "label");
151 //    } else {
152     if (nextToRenderIsLabel) {
153       // skip, because its only the lable to render
154     } else {
155       writer.startElement(getComponentTag());
156       writer.writeIdAttribute(clientId);
157       encodeAttributes(facesContext, component);
158       writer.writeClassAttribute(
159           flex ? TobagoClass.FLEX_LAYOUT : null,
160           flex ? TobagoClass.LABEL__CONTAINER :  null,
161           BootstrapClass.MB_3,
162           ComponentUtils.getBooleanAttribute(component, Attributes.required) ? TobagoClass.REQUIRED : null,
163           markup != null && markup.contains(Markup.SPREAD) ? TobagoClass.SPREAD : null);
164     }
165 
166     switch (labelLayout) {
167       case none:
168       case flexRight:
169       case flowRight:
170       case flowLeft:
171       case gridLeft:
172       case gridRight:
173       case gridTop:
174       case gridBottom:
175         break;
176       case segmentLeft:
177       case segmentRight:
178         if (nextToRenderIsLabel) {
179           encodeLabel(facesContext, component, writer, labelLayout);
180         }
181         break;
182       default:
183         encodeLabel(facesContext, component, writer, labelLayout);
184     }
185 
186 //    switch (labelLayout) {
187 //      case gridLeft:
188 //      case gridRight:
189 //      case gridTop:
190 //      case gridBottom:
191 //        writer.endElement(HtmlElements.LABEL);
192 
193 //        writer.startElement(getComponentTag());
194 //        writer.writeIdAttribute(clientId);
195 //        encodeAttributes(facesContext, component);
196 //        writer.writeClassAttribute(
197 //            BootstrapClass.MB_3,
198 //            ComponentUtils.getBooleanAttribute(component, Attributes.required) ? TobagoClass.REQUIRED : null,
199 //            markup != null && markup.contains(Markup.SPREAD) ? TobagoClass.SPREAD : null);
200 //        break;
201 //      default:
202 //    }
203   }
204 
205   protected void encodeEndSurroundingLabel(final FacesContext facesContext, final T component)
206       throws IOException {
207 
208     final TobagoResponseWriter writer = getResponseWriter(facesContext);
209     final LabelLayout labelLayout = component.getLabelLayout();
210 
211     if (labelLayout == LabelLayout.flexRight) {
212       encodeLabel(facesContext, component, writer, labelLayout);
213     }
214 
215     final boolean nextToRenderIsLabel = component.isNextToRenderIsLabel();
216     if (!nextToRenderIsLabel) {
217       writer.endElement(getComponentTag());
218     }
219 
220     if (labelLayout == LabelLayout.flowRight) {
221       encodeLabel(facesContext, component, writer, labelLayout);
222     }
223   }
224 
225   protected void encodeLabel(final FacesContext facesContext, final T component,
226                              final TobagoResponseWriter writer, final LabelLayout labelLayout)
227       throws IOException {
228     // TBD: maybe use an interface for getLabel()
229     final String label = ComponentUtils.getStringAttribute(component, Attributes.label);
230     if (StringUtils.isNotBlank(label)) {
231       writer.startElement(HtmlElements.LABEL);
232       writer.writeAttribute(HtmlAttributes.FOR, getFieldId(facesContext, component), false);
233       writer.writeClassAttribute(BootstrapClass.COL_FORM_LABEL);
234       if (component instanceof SupportsAccessKey) {
235         final LabelWithAccessKey labelWithAccessKey = new LabelWithAccessKey((SupportsAccessKey) component);
236         HtmlRendererUtils.writeLabelWithAccessKey(writer, labelWithAccessKey);
237       } else {
238         writer.writeText(label);
239       }
240       writer.endElement(HtmlElements.LABEL);
241     }
242   }
243 
244   protected abstract String getFieldId(final FacesContext facesContext, final T component);
245 }