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.SupportsHelp;
23 import org.apache.myfaces.tobago.component.SupportsLabelLayout;
24 import org.apache.myfaces.tobago.internal.util.StringUtils;
25 import org.apache.myfaces.tobago.renderkit.css.BootstrapClass;
26 import org.apache.myfaces.tobago.renderkit.css.CssItem;
27 import org.apache.myfaces.tobago.renderkit.css.Icons;
28 import org.apache.myfaces.tobago.renderkit.css.TobagoClass;
29 import org.apache.myfaces.tobago.renderkit.html.HtmlAttributes;
30 import org.apache.myfaces.tobago.renderkit.html.HtmlButtonTypes;
31 import org.apache.myfaces.tobago.renderkit.html.HtmlElements;
32 import org.apache.myfaces.tobago.util.ComponentUtils;
33 import org.apache.myfaces.tobago.util.ResourceUtils;
34 import org.apache.myfaces.tobago.webapp.TobagoResponseWriter;
35
36 import javax.faces.application.FacesMessage;
37 import javax.faces.component.UIComponent;
38 import javax.faces.context.FacesContext;
39 import java.io.IOException;
40 import java.util.List;
41
42 public abstract class MessageLayoutRendererBase<T extends UIComponent & SupportsLabelLayout>
43 extends LabelLayoutRendererBase<T> {
44
45 @Override
46 public void encodeBeginInternal(FacesContext facesContext, T component) throws IOException {
47 if (isInside(facesContext, HtmlElements.COMMAND)) {
48 encodeBeginField(facesContext, component);
49 } else {
50 super.encodeBeginInternal(facesContext, component);
51 }
52 }
53
54 @Override
55 public void encodeEndInternal(FacesContext facesContext, T component) throws IOException {
56 if (isInside(facesContext, HtmlElements.COMMAND)) {
57 encodeEndField(facesContext, component);
58 } else {
59 super.encodeEndInternal(facesContext, component);
60 }
61 }
62
63 @Override
64 public void encodeBeginMessageField(final FacesContext facesContext, final T component) throws IOException {
65 encodeBeginMessagesContainer(facesContext, component);
66 encodeBeginField(facesContext, component);
67 }
68
69 @Override
70 public void encodeEndMessageField(final FacesContext facesContext, final T component) throws IOException {
71 encodeEndField(facesContext, component);
72 encodeEndMessagesContainer(facesContext, component);
73 }
74
75 private void encodeBeginMessagesContainer(final FacesContext facesContext, final T component)
76 throws IOException {
77 final TobagoResponseWriter writer = getResponseWriter(facesContext);
78
79 final String clientId = component.getClientId();
80 final List<FacesMessage> messages = facesContext.getMessageList(clientId);
81 final String help = component instanceof SupportsHelp ? ((SupportsHelp) component).getHelp() : null;
82 final boolean hasMessage = !messages.isEmpty();
83 final boolean hasHelp = !StringUtils.isEmpty(help);
84
85 if (hasMessage || hasHelp) {
86 writer.startElement(HtmlElements.DIV);
87 writer.writeClassAttribute(TobagoClass.MESSAGES__CONTAINER, TobagoClass.FLEX_LAYOUT);
88 }
89 }
90
91 private void encodeEndMessagesContainer(final FacesContext facesContext, final T component)
92 throws IOException {
93 final TobagoResponseWriter writer = getResponseWriter(facesContext);
94
95 final String clientId = component.getClientId();
96 final List<FacesMessage> messages = facesContext.getMessageList(clientId);
97 final String help = component instanceof SupportsHelp ? ((SupportsHelp) component).getHelp() : null;
98 final boolean hasMessage = !messages.isEmpty();
99 final boolean hasHelp = !StringUtils.isEmpty(help);
100
101 if (hasMessage || hasHelp) {
102 if (hasMessage) {
103 encodeFacesMessagesButton(facesContext, component, writer, messages);
104 }
105 if (hasHelp) {
106 encodePopover(writer, component.getClientId(facesContext) + ComponentUtils.SUB_SEPARATOR + "help",
107 BootstrapClass.BTN_OUTLINE_INFO, Icons.QUESTION,
108 ResourceUtils.getString(facesContext, "help.title"), help);
109 }
110 writer.endElement(HtmlElements.DIV);
111 }
112 }
113
114 protected abstract void encodeBeginField(FacesContext facesContext, T component) throws IOException;
115
116 protected abstract void encodeEndField(FacesContext facesContext, T component) throws IOException;
117
118 private void encodeFacesMessagesButton(FacesContext facesContext, final T component,
119 final TobagoResponseWriter writer, final List<FacesMessage> messages) throws IOException {
120
121 encodePopover(writer, component.getClientId(facesContext) + ComponentUtils.SUB_SEPARATOR + "messages",
122 BootstrapClass.buttonColor(ComponentUtils.getMaximumSeverity(messages)),
123 Icons.EXCLAMATION, getTitle(messages), getMessage(messages));
124 }
125
126 private String getTitle(final List<FacesMessage> messages) {
127 int fatalCount = 0;
128 int errorCount = 0;
129 int warningCount = 0;
130 int informationCount = 0;
131
132 for (final FacesMessage message : messages) {
133 if (FacesMessage.SEVERITY_FATAL.equals(message.getSeverity())) {
134 fatalCount++;
135 } else if (FacesMessage.SEVERITY_ERROR.equals(message.getSeverity())) {
136 errorCount++;
137 } else if (FacesMessage.SEVERITY_WARN.equals(message.getSeverity())) {
138 warningCount++;
139 } else if (FacesMessage.SEVERITY_INFO.equals(message.getSeverity())) {
140 informationCount++;
141 }
142 }
143
144 final StringBuilder stringBuilder = new StringBuilder();
145
146 if (messages.size() > 1) {
147 if (fatalCount > 0) {
148 stringBuilder.append(fatalCount);
149 stringBuilder.append(" Fatal");
150
151 if (errorCount + warningCount + informationCount > 0) {
152 stringBuilder.append(", ");
153 }
154 }
155
156 if (errorCount > 0) {
157 stringBuilder.append(errorCount);
158 stringBuilder.append(" Error");
159 if (errorCount > 1) {
160 stringBuilder.append("s");
161 }
162
163 if (warningCount + informationCount > 0) {
164 stringBuilder.append(", ");
165 }
166 }
167
168 if (warningCount > 0) {
169 stringBuilder.append(warningCount);
170 stringBuilder.append(" Warning");
171 if (warningCount > 1) {
172 stringBuilder.append("s");
173 }
174
175 if (informationCount > 0) {
176 stringBuilder.append(", ");
177 }
178 }
179
180 if (informationCount > 0) {
181 stringBuilder.append(informationCount);
182 stringBuilder.append(" Information");
183 }
184 } else {
185 if (fatalCount == 1) {
186 stringBuilder.append("Fatal");
187 } else if (errorCount == 1) {
188 stringBuilder.append("Error");
189 } else if (warningCount == 1) {
190 stringBuilder.append("Warning");
191 } else if (informationCount == 1) {
192 stringBuilder.append("Information");
193 }
194 }
195 return stringBuilder.toString();
196 }
197
198 private String getMessage(final List<FacesMessage> messages) {
199 final StringBuilder stringBuilder = new StringBuilder();
200 boolean firstMessage = true;
201 for (final FacesMessage message : messages) {
202 if (firstMessage) {
203 firstMessage = false;
204 } else {
205 stringBuilder.append("\n\n");
206 }
207 stringBuilder.append(message.getDetail());
208 }
209 return stringBuilder.toString();
210 }
211
212 private void encodePopover(final TobagoResponseWriter writer, final String popoverId,
213 final CssItem buttonColor, final Icons buttonIcon, final String title, final String content)
214 throws IOException {
215 writer.startElement(HtmlElements.TOBAGO_POPOVER);
216 writer.writeIdAttribute(popoverId);
217
218 writer.startElement(HtmlElements.A);
219 writer.writeAttribute(HtmlAttributes.TABINDEX, "0", false);
220 writer.writeAttribute(HtmlAttributes.ROLE, HtmlButtonTypes.BUTTON);
221 writer.writeClassAttribute(TobagoClass.POPOVER__BUTTON, BootstrapClass.BTN, buttonColor);
222 writer.startElement(HtmlElements.I);
223 writer.writeClassAttribute(Icons.FA, buttonIcon);
224 writer.endElement(HtmlElements.I);
225 writer.endElement(HtmlElements.A);
226
227 writer.startElement(HtmlElements.DIV);
228 writer.writeClassAttribute(TobagoClass.POPOVER__BOX, BootstrapClass.POPOVER);
229 writer.writeNameAttribute(popoverId);
230 writer.startElement(HtmlElements.DIV);
231 writer.writeClassAttribute(BootstrapClass.POPOVER_ARROW);
232 writer.endElement(HtmlElements.DIV);
233 writer.startElement(HtmlElements.H3);
234 writer.writeClassAttribute(BootstrapClass.POPOVER_HEADER);
235 writer.writeText(title);
236 writer.endElement(HtmlElements.H3);
237 writer.startElement(HtmlElements.DIV);
238 writer.writeClassAttribute(BootstrapClass.POPOVER_BODY);
239 writer.writeText(content);
240 writer.endElement(HtmlElements.DIV);
241 writer.endElement(HtmlElements.DIV);
242
243 writer.endElement(HtmlElements.TOBAGO_POPOVER);
244 }
245 }