1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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);
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
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 }