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.Facets;
24  import org.apache.myfaces.tobago.internal.component.AbstractUIButton;
25  import org.apache.myfaces.tobago.internal.component.AbstractUIIn;
26  import org.apache.myfaces.tobago.internal.component.AbstractUIInput;
27  import org.apache.myfaces.tobago.internal.component.AbstractUIOut;
28  import org.apache.myfaces.tobago.internal.component.AbstractUISelectOneChoice;
29  import org.apache.myfaces.tobago.internal.util.AccessKeyLogger;
30  import org.apache.myfaces.tobago.internal.util.HtmlRendererUtils;
31  import org.apache.myfaces.tobago.internal.util.RenderUtils;
32  import org.apache.myfaces.tobago.internal.util.StringUtils;
33  import org.apache.myfaces.tobago.renderkit.css.BootstrapClass;
34  import org.apache.myfaces.tobago.renderkit.css.CssItem;
35  import org.apache.myfaces.tobago.renderkit.css.TobagoClass;
36  import org.apache.myfaces.tobago.renderkit.html.HtmlAttributes;
37  import org.apache.myfaces.tobago.renderkit.html.HtmlElements;
38  import org.apache.myfaces.tobago.renderkit.html.HtmlInputTypes;
39  import org.apache.myfaces.tobago.util.ComponentUtils;
40  import org.apache.myfaces.tobago.webapp.TobagoResponseWriter;
41  import org.slf4j.Logger;
42  import org.slf4j.LoggerFactory;
43  
44  import javax.faces.component.UIComponent;
45  import javax.faces.context.FacesContext;
46  import javax.faces.validator.LengthValidator;
47  import javax.faces.validator.RegexValidator;
48  import javax.faces.validator.Validator;
49  import java.io.IOException;
50  import java.lang.invoke.MethodHandles;
51  
52  public class InRenderer<T extends AbstractUIIn> extends MessageLayoutRendererBase<T> {
53  
54    private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
55  
56    @Override
57    protected boolean isOutputOnly(T component) {
58      return component.isDisabled() || component.isReadonly();
59    }
60  
61    @Override
62    public HtmlElements getComponentTag() {
63      return HtmlElements.TOBAGO_IN;
64    }
65  
66    @Override
67    public void encodeBeginInternal(FacesContext facesContext, T component) throws IOException {
68      insideBegin(facesContext, HtmlElements.TOBAGO_IN);
69      super.encodeBeginInternal(facesContext, component);
70    }
71  
72    @Override
73    public void encodeEndInternal(FacesContext facesContext, T component) throws IOException {
74      super.encodeEndInternal(facesContext, component);
75      insideEnd(facesContext, HtmlElements.TOBAGO_IN);
76    }
77  
78    @Override
79    protected void encodeBeginField(final FacesContext facesContext, final T component)
80        throws IOException {
81      final String title = HtmlRendererUtils.getTitleFromTipAndMessages(facesContext, component);
82      final String currentValue = getCurrentValue(facesContext, component);
83      final boolean password = ComponentUtils.getBooleanAttribute(component, Attributes.password);
84      if (LOG.isDebugEnabled()) {
85        LOG.debug("currentValue = '{}'", StringUtils.toConfidentialString(currentValue, password));
86      }
87      final HtmlInputTypes type = password ? HtmlInputTypes.PASSWORD : HtmlInputTypes.TEXT;
88      final String clientId = component.getClientId(facesContext);
89      final String fieldId = component.getFieldId(facesContext);
90      final boolean readonly = component.isReadonly();
91      final boolean disabled = component.isDisabled();
92      final boolean required = ComponentUtils.getBooleanAttribute(component, Attributes.required);
93  
94      final TobagoResponseWriter writer = getResponseWriter(facesContext);
95  
96      final UIComponent after = ComponentUtils.getFacet(component, Facets.after);
97      final UIComponent before = ComponentUtils.getFacet(component, Facets.before);
98  
99      if (after != null || before != null) {
100       writer.startElement(HtmlElements.DIV); // Wrapping the field to fix input groups with flexLeft/flexRight
101       writer.writeClassAttribute(TobagoClass.INPUT__GROUP__OUTER);
102       writer.startElement(HtmlElements.DIV);
103       writer.writeClassAttribute(BootstrapClass.INPUT_GROUP);
104     }
105     encodeGroupAddon(facesContext, writer, before);
106 
107     writer.startElement(HtmlElements.INPUT);
108 
109     if (component.getAccessKey() != null) {
110       writer.writeAttribute(HtmlAttributes.ACCESSKEY, Character.toString(component.getAccessKey()), false);
111       AccessKeyLogger.addAccessKey(facesContext, component.getAccessKey(), clientId);
112     }
113 
114     writer.writeAttribute(HtmlAttributes.TYPE, type);
115     writer.writeNameAttribute(clientId);
116     writer.writeIdAttribute(fieldId);
117     HtmlRendererUtils.writeDataAttributes(facesContext, writer, component);
118     if (currentValue != null && !password) {
119       writer.writeAttribute(HtmlAttributes.VALUE, currentValue, true);
120     }
121     if (title != null) {
122       writer.writeAttribute(HtmlAttributes.TITLE, title, true);
123     }
124     int maxLength = 0;
125     int minLength = 0;
126     String pattern = null;
127     for (final Validator validator : component.getValidators()) {
128       if (validator instanceof LengthValidator) {
129         final LengthValidator lengthValidator = (LengthValidator) validator;
130         maxLength = lengthValidator.getMaximum();
131         minLength = lengthValidator.getMinimum();
132       } else if (validator instanceof RegexValidator) {
133         final RegexValidator regexValidator = (RegexValidator) validator;
134         pattern = regexValidator.getPattern();
135       }
136     }
137     if (maxLength > 0) {
138       writer.writeAttribute(HtmlAttributes.MAXLENGTH, maxLength);
139     }
140     if (minLength > 0) {
141       writer.writeAttribute(HtmlAttributes.MINLENGTH, minLength);
142     }
143     if (pattern != null) {
144       writer.writeAttribute(HtmlAttributes.PATTERN, pattern, true);
145     }
146     writer.writeAttribute(HtmlAttributes.READONLY, readonly);
147     writer.writeAttribute(HtmlAttributes.DISABLED, disabled);
148     writer.writeAttribute(HtmlAttributes.TABINDEX, component.getTabIndex());
149     if (!disabled && !readonly) {
150       writer.writeAttribute(HtmlAttributes.PLACEHOLDER, component.getPlaceholder(), true);
151     }
152 
153     final CssItem rendererCssClass = getRendererCssClass();
154     writer.writeClassAttribute(
155         rendererCssClass,
156 //        rendererCssClass != null ? rendererCssClass.createMarkup(markup) : null,
157         BootstrapClass.borderColor(ComponentUtils.getMaximumSeverity(component)),
158         BootstrapClass.FORM_CONTROL,
159         component.getCustomClass());
160 
161     writer.writeAttribute(HtmlAttributes.REQUIRED, required);
162     renderFocus(clientId, component.isFocus(), component.isError(), facesContext, writer);
163     writeAdditionalAttributes(facesContext, writer, component);
164 
165     writer.endElement(HtmlElements.INPUT);
166 
167     encodeBehavior(writer, facesContext, component);
168 
169     encodeGroupAddon(facesContext, writer, after);
170 
171     if (after != null || before != null) {
172       writer.endElement(HtmlElements.DIV);
173       writer.endElement(HtmlElements.DIV);
174     }
175   }
176 
177   private void encodeGroupAddon(
178       final FacesContext facesContext, final TobagoResponseWriter writer, final UIComponent addon) throws IOException {
179     if (addon != null) {
180       for (final UIComponent child : RenderUtils.getFacetChildren(addon)) {
181         if (child instanceof AbstractUIButton) {
182           child.encodeAll(facesContext);
183         } else if (child instanceof AbstractUIOut) {
184           child.encodeAll(facesContext);
185         } else if (child instanceof AbstractUISelectOneChoice) {
186           child.encodeAll(facesContext);
187         } else {
188           writer.startElement(HtmlElements.SPAN);
189           writer.writeClassAttribute(BootstrapClass.INPUT_GROUP_TEXT);
190           child.encodeAll(facesContext);
191           writer.endElement(HtmlElements.SPAN);
192         }
193       }
194     }
195   }
196 
197   @Override
198   protected void encodeEndField(final FacesContext facesContext, final T component) throws IOException {
199   }
200 
201   protected CssItem getRendererCssClass() {
202     return TobagoClass.IN;
203   }
204 
205   protected void writeAdditionalAttributes(
206       final FacesContext facesContext, final TobagoResponseWriter writer, final AbstractUIInput input)
207       throws IOException {
208   }
209 
210   @Override
211   protected String getFieldId(final FacesContext facesContext, final T component) {
212     return component.getFieldId(facesContext);
213   }
214 }