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.internal.component.AbstractUIBadge;
24 import org.apache.myfaces.tobago.internal.component.AbstractUICommand;
25 import org.apache.myfaces.tobago.internal.component.AbstractUIFormBase;
26 import org.apache.myfaces.tobago.internal.component.AbstractUILink;
27 import org.apache.myfaces.tobago.internal.component.AbstractUISelectBooleanCheckbox;
28 import org.apache.myfaces.tobago.internal.component.AbstractUISelectManyCheckbox;
29 import org.apache.myfaces.tobago.internal.component.AbstractUISelectOneRadio;
30 import org.apache.myfaces.tobago.internal.component.AbstractUISeparator;
31 import org.apache.myfaces.tobago.internal.component.AbstractUIStyle;
32 import org.apache.myfaces.tobago.internal.util.AccessKeyLogger;
33 import org.apache.myfaces.tobago.internal.util.HtmlRendererUtils;
34 import org.apache.myfaces.tobago.internal.util.RenderUtils;
35 import org.apache.myfaces.tobago.renderkit.LabelWithAccessKey;
36 import org.apache.myfaces.tobago.renderkit.css.BootstrapClass;
37 import org.apache.myfaces.tobago.renderkit.css.CssItem;
38 import org.apache.myfaces.tobago.renderkit.css.TobagoClass;
39 import org.apache.myfaces.tobago.renderkit.html.Arias;
40 import org.apache.myfaces.tobago.renderkit.html.DataAttributes;
41 import org.apache.myfaces.tobago.renderkit.html.HtmlAttributes;
42 import org.apache.myfaces.tobago.renderkit.html.HtmlButtonTypes;
43 import org.apache.myfaces.tobago.renderkit.html.HtmlElements;
44 import org.apache.myfaces.tobago.util.ComponentUtils;
45 import org.apache.myfaces.tobago.webapp.TobagoResponseWriter;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
48
49 import javax.faces.component.UIComponent;
50 import javax.faces.component.UIParameter;
51 import javax.faces.context.FacesContext;
52 import java.io.IOException;
53 import java.lang.invoke.MethodHandles;
54 import java.util.ArrayList;
55 import java.util.List;
56
57 public abstract class CommandRendererBase<T extends AbstractUICommand> extends DecodingCommandRendererBase<T> {
58
59 private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
60
61 @Override
62 public void encodeBeginInternal(final FacesContext facesContext, final T component) throws IOException {
63
64 final String clientId = component.getClientId(facesContext);
65 final boolean disabled = component.isDisabled();
66 final LabelWithAccessKey label = new LabelWithAccessKey(component);
67 final boolean anchor = (component.getLink() != null || component.getOutcome() != null) && !disabled;
68 final String target = component.getTarget();
69 final boolean parentOfCommands = component.isParentOfCommands();
70 final boolean dropdownSubmenu = isInside(facesContext, HtmlElements.COMMAND);
71
72 final TobagoResponseWriter writer = getResponseWriter(facesContext);
73
74 encodeBeginOuter(facesContext, component);
75
76 if (anchor) {
77 writer.startElement(HtmlElements.A);
78 } else {
79 writer.startElement(HtmlElements.BUTTON);
80 writer.writeAttribute(HtmlAttributes.TYPE, HtmlButtonTypes.BUTTON);
81 }
82 writer.writeIdAttribute(component.getFieldId(facesContext));
83 writer.writeNameAttribute(clientId);
84 writer.writeAttribute(HtmlAttributes.DISABLED, disabled);
85
86 if (!disabled) {
87 if (anchor) {
88 final String href = RenderUtils.generateUrl(facesContext, component);
89 writer.writeAttribute(HtmlAttributes.HREF, href, true);
90 writer.writeAttribute(HtmlAttributes.TARGET, target, true);
91
92 component.setOmit(true);
93 }
94
95 if (label.getAccessKey() != null) {
96 writer.writeAttribute(HtmlAttributes.ACCESSKEY, Character.toString(label.getAccessKey()), false);
97 AccessKeyLogger.addAccessKey(facesContext, label.getAccessKey(), clientId);
98 }
99
100 final int tabIndex = ComponentUtils.getIntAttribute(component, Attributes.tabIndex);
101 if (tabIndex != 0) {
102 writer.writeAttribute(HtmlAttributes.TABINDEX, tabIndex);
103 }
104 }
105
106 HtmlRendererUtils.writeDataAttributes(facesContext, writer, component);
107
108 if (parentOfCommands) {
109 writer.writeAttribute(DataAttributes.TOGGLE, "dropdown", false);
110 }
111 final String title = HtmlRendererUtils.getTitleFromTipAndMessages(facesContext, component);
112 writer.writeAttribute(HtmlAttributes.TITLE, title, true);
113
114 writer.writeClassAttribute(
115 getRendererCssClass(),
116 getRendererCssClass().createMarkup(component.getMarkup()),
117 getCssItems(facesContext, component),
118 !parentOfCommands && dropdownSubmenu ? BootstrapClass.DROPDOWN_ITEM : null,
119 parentOfCommands && !dropdownSubmenu ? BootstrapClass.DROPDOWN_TOGGLE : null,
120 component.getCustomClass(),
121 isInside(facesContext, HtmlElements.TOBAGO_LINKS) ? BootstrapClass.NAV_LINK : null);
122
123 final boolean defaultCommand = ComponentUtils.getBooleanAttribute(component, Attributes.defaultCommand);
124 if (defaultCommand) {
125 final AbstractUIFormBase form = ComponentUtils.findAncestor(component, AbstractUIFormBase.class);
126 if (form != null) {
127 writer.writeAttribute(DataAttributes.DEFAULT, form.getClientId(facesContext), false);
128 } else {
129 LOG.warn("No from found for {}", clientId);
130 }
131 }
132
133 if (!disabled) {
134 encodeBehavior(writer, facesContext, component);
135 }
136
137 final String image = ComponentUtils.getStringAttribute(component, Attributes.image);
138 HtmlRendererUtils.encodeIconOrImage(writer, image);
139
140 if (label.getLabel() != null) {
141 writer.startElement(HtmlElements.SPAN);
142 HtmlRendererUtils.writeLabelWithAccessKey(writer, label);
143 writer.endElement(HtmlElements.SPAN);
144 encodeBadge(facesContext, component);
145 }
146
147 if (anchor) {
148 writer.endElement(HtmlElements.A);
149 } else {
150 writer.endElement(HtmlElements.BUTTON);
151 }
152 }
153
154 @Override
155 public boolean getRendersChildren() {
156 return true;
157 }
158
159 @Override
160 public void encodeChildrenInternal(final FacesContext facesContext, final T component) throws IOException {
161 final boolean parentOfCommands = component.isParentOfCommands();
162
163 final TobagoResponseWriter writer = getResponseWriter(facesContext);
164
165 if (parentOfCommands) {
166 List<UIComponent> renderLater = null;
167
168 writer.startElement(HtmlElements.DIV);
169 writer.writeClassAttribute(BootstrapClass.DROPDOWN_MENU);
170
171 writer.writeAttribute(Arias.LABELLEDBY, component.getFieldId(facesContext), false);
172 writer.writeAttribute(HtmlAttributes.NAME, component.getClientId(facesContext), false);
173
174 for (final UIComponent child : component.getChildren()) {
175 if (child.isRendered()
176 && !(child instanceof UIParameter)
177 && !(child instanceof AbstractUIBadge)) {
178 if (child instanceof AbstractUIStyle) {
179 if (renderLater == null) {
180 renderLater = new ArrayList<>();
181 }
182 renderLater.add(child);
183 } else if (child instanceof AbstractUILink
184 || child instanceof AbstractUISelectBooleanCheckbox
185 || child instanceof AbstractUISelectManyCheckbox
186 || child instanceof AbstractUISelectOneRadio
187 || child instanceof AbstractUISeparator) {
188 insideBegin(facesContext, HtmlElements.COMMAND);
189 child.encodeAll(facesContext);
190 insideEnd(facesContext, HtmlElements.COMMAND);
191 } else {
192 writer.startElement(HtmlElements.DIV);
193 writer.writeClassAttribute(BootstrapClass.DROPDOWN_ITEM);
194 child.encodeAll(facesContext);
195 writer.endElement(HtmlElements.DIV);
196 }
197 }
198 }
199 writer.endElement(HtmlElements.DIV);
200
201 if (renderLater != null) {
202 for (UIComponent child : renderLater) {
203 child.encodeAll(facesContext);
204 }
205 }
206 } else {
207 for (final UIComponent child : component.getChildren()) {
208 if (!(child instanceof AbstractUIBadge)) {
209 child.encodeAll(facesContext);
210 }
211 }
212 }
213 }
214
215 @Override
216 public void encodeEndInternal(final FacesContext facesContext, final T component) throws IOException {
217 encodeEndOuter(facesContext, component);
218 }
219
220 protected void encodeBeginOuter(final FacesContext facesContext, final T command) throws IOException {
221
222 final String clientId = command.getClientId(facesContext);
223 final boolean parentOfCommands = command.isParentOfCommands();
224 final boolean dropdownSubmenu = isInside(facesContext, HtmlElements.COMMAND);
225 final TobagoResponseWriter writer = getResponseWriter(facesContext);
226
227 if (parentOfCommands) {
228 writer.startElement(HtmlElements.TOBAGO_DROPDOWN);
229 writer.writeIdAttribute(clientId);
230
231 writer.writeClassAttribute(
232 dropdownSubmenu ? TobagoClass.DROPDOWN__SUBMENU : BootstrapClass.DROPDOWN,
233 dropdownSubmenu ? BootstrapClass.DROPDOWN_ITEM : null);
234 }
235 }
236
237 protected void encodeEndOuter(final FacesContext facesContext, final T command) throws IOException {
238 final TobagoResponseWriter writer = getResponseWriter(facesContext);
239 if (command.isParentOfCommands()) {
240 writer.endElement(HtmlElements.TOBAGO_DROPDOWN);
241 }
242 }
243
244 protected CssItem[] getOuterCssItems(final FacesContext facesContext, final T command) {
245 return null;
246 }
247
248 abstract TobagoClass getRendererCssClass();
249
250 protected CssItem[] getCssItems(final FacesContext facesContext, final T command) {
251 return null;
252 }
253
254 protected void encodeBadge(final FacesContext facesContext, final T command) throws IOException {
255 }
256 }