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.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 }