001    /*
002     * Copyright 2011 The Kuali Foundation.
003     * 
004     * Licensed under the Educational Community License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     * 
008     * http://www.opensource.org/licenses/ecl2.php
009     * 
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.kuali.kfs.sys.document.web.renderers;
017    
018    import java.io.IOException;
019    import java.util.ArrayList;
020    import java.util.List;
021    
022    import javax.servlet.jsp.JspException;
023    import javax.servlet.jsp.JspWriter;
024    import javax.servlet.jsp.PageContext;
025    import javax.servlet.jsp.tagext.Tag;
026    
027    import org.apache.struts.taglib.html.ErrorsTag;
028    import org.kuali.kfs.sys.KFSKeyConstants;
029    import org.kuali.kfs.sys.context.SpringContext;
030    import org.kuali.rice.kns.service.KualiConfigurationService;
031    import org.kuali.rice.kns.util.KNSConstants;
032    
033    /**
034     * Renders any errors associated with an accounting line group
035     */
036    public class GroupErrorsRenderer implements Renderer {
037        private List<String> errorsRendered;
038        private List<String> warningsRendered;
039        private List<String> infoRendered;
040        private String errorKeyMatch;
041        private int colSpan = -1;
042        private ErrorsTag errorTag = new ErrorsTag();
043    
044        /**
045         * Cleans up the errorPropertyList, the sectionTitle, the errorsRendered (so you'd better read that first),
046         * and the ErrorTag used to display the errors
047         * @see org.kuali.kfs.sys.document.web.renderers.Renderer#clear()
048         */
049        public void clear() {
050            errorsRendered = null;
051            warningsRendered = null;
052            infoRendered = null;
053            errorKeyMatch = null;
054            colSpan = -1;
055            
056            cleanUpErrorTag();
057        }
058        
059        /**
060         * Cleans up the ErrorTag
061         */
062        protected void cleanUpErrorTag() {
063            errorTag.setPageContext(null);
064            errorTag.setParent(null);
065            errorTag.setProperty(null);
066        }
067    
068        /**
069         * Renders the errors, warnings, and messages for this page
070         * @see org.kuali.kfs.sys.document.web.renderers.Renderer#render(javax.servlet.jsp.PageContext, javax.servlet.jsp.tagext.Tag)
071         */
072        public void render(PageContext pageContext, Tag parentTag) throws JspException {
073            renderMessages(pageContext, parentTag, KFSKeyConstants.MESSAGE_ACCOUNTING_LINES_ERROR_SECTION_TITLE, getErrorPropertyList(pageContext), "errormark.gif", "error", getErrorsRendered());
074            renderMessages(pageContext, parentTag, KFSKeyConstants.MESSAGE_ACCOUNTING_LINES_WARNING_SECTION_TITLE, getWarningPropertyList(pageContext), "warning.png", "warning", getWarningsRendered());
075            renderMessages(pageContext, parentTag, KFSKeyConstants.MESSAGE_ACCOUNTING_LINES_INFORMATION_SECTION_TITLE, getInfoPropertyList(pageContext), "info.png", "info", getInfoRendered());
076        }
077        
078        /**
079         * Renders a group of messages
080         * @param pageContext the page context to render to
081         * @param parentTag the name of the parent tag requesting this rendering
082         * @param titleConstant the Key Constant to text for the title
083         * @param propertyList the list of properties to display
084         * @param sectionMarkGraphicName the file name of the mark graphic to display
085         * @param sectionGraphicAlt the wording to be used in the "alt" section of the mark graphic
086         * @throws JspException thrown if rendering cannot be successfully completed
087         */
088        protected void renderMessages(PageContext pageContext, Tag parentTag, String titleConstant, List propertyList, String sectionMarkGraphicName, String sectionGraphicAlt, List<String> keysRendered) throws JspException {
089            JspWriter out = pageContext.getOut();
090            
091            try {
092                final List<String> matchingKeys = getMatchingKeys(propertyList, getKeysToMatch());
093                if (matchingKeys.size() > 0) {
094                    out.write(buildTableRowAndCellOpening());
095                    out.write(buildSectionTitle(titleConstant, sectionMarkGraphicName, sectionGraphicAlt));
096                }
097                
098                for (String matchingKey : matchingKeys) {
099                    out.write(buildKeyComment(matchingKey, sectionGraphicAlt));
100                    if (!keysRendered.contains(matchingKey)) {
101                        errorTag.setPageContext(pageContext);
102                        errorTag.setParent(parentTag);
103                        errorTag.setProperty(matchingKey);
104                       
105                        errorTag.doStartTag();
106                        errorTag.doEndTag();
107                        
108                        keysRendered.add(matchingKey);
109                    }
110                }
111                
112                if (matchingKeys.size() > 0) {
113                    out.write(buildTableRowAndCellClosing());
114                }
115            }
116            catch (IOException ioe) {
117                throw new JspException("Difficulty while rendering errors for group", ioe);
118            }
119        }
120        
121        /**
122         * Builds the HTML String for a section title
123         * @param titleConstant the Key Constant to find the text for the title
124         * @param sectionMarkGraphicName the name of the graphic file to use
125         * @param sectionGraphicAlt the alt for the graphic
126         * @return the String to output as HTML for the section title
127         */
128        protected String buildSectionTitle(String titleConstant, String sectionMarkGraphicName, String sectionGraphicAlt) {
129            final KualiConfigurationService configurationService = SpringContext.getBean(KualiConfigurationService.class);
130            final String titleMessage = configurationService.getPropertyString(titleConstant);
131            final String riceImageUrl = configurationService.getPropertyString(KNSConstants.EXTERNALIZABLE_IMAGES_URL_KEY);
132            
133            StringBuilder sectionTitle = new StringBuilder();
134            
135            sectionTitle.append("<img src=\"")
136                        .append(riceImageUrl)
137                        .append(sectionMarkGraphicName)
138                        .append("\" alt=\"")
139                        .append(sectionGraphicAlt)
140                        .append("\" /><strong>")
141                        .append(titleMessage)
142                        .append("</strong>");
143            
144            return sectionTitle.toString();
145        }
146        
147        /**
148         * Builds an HTML comment, useful for debugging, which dumps out the message key being displayed
149         * @param matchingKey the key to display
150         * @param sectionGraphicAlt the alt for this section, we'll reuse it for the comments
151         * @return the String to output for the key comment
152         */
153        protected String buildKeyComment(String matchingKey, String sectionGraphicAlt) {
154            StringBuilder keyComment = new StringBuilder();
155            
156            keyComment.append("\n<!-- ")
157                      .append(sectionGraphicAlt)
158                      .append(" key = '")
159                      .append(matchingKey)
160                      .append("' -->\n");
161            
162            return keyComment.toString();
163        }
164        
165        /**
166         * @return the HTML for the table row and cell and div to open the error display
167         */
168        protected String buildTableRowAndCellOpening() {
169            StringBuilder html = new StringBuilder();
170            html.append("<tr>");
171            html.append("<td colspan=\"");
172            html.append(colSpan);
173            html.append("\">");
174            html.append("<div class=\"left-errmsg-tab\">");
175            return html.toString();
176        }
177        
178        /**
179         * @return the HTML for the table row and cell and div which closes the error display 
180         */
181        protected String buildTableRowAndCellClosing() {
182            StringBuilder html = new StringBuilder();
183            html.append("</div>");
184            html.append("</td>");
185            html.append("</tr>");
186            return html.toString();
187        }
188        
189        /**
190         * Returns a list of all error keys that should be rendered
191         * @param keysToMatch the keys that this group will match
192         * @return a List of all error keys this group will match
193         */
194        protected List<String> getMatchingKeys(List messagePropertyList, String[] keysToMatch) {
195            List<String> matchingKeys = new ArrayList<String>();
196            
197            if (messagePropertyList != null && messagePropertyList.size() > 0) {
198                for (Object keyAsObject : messagePropertyList) {
199                    String key = (String)keyAsObject;
200                    if (matchesGroup(key, keysToMatch)) {
201                        matchingKeys.add(key);
202                    }
203                }
204            }
205            
206            return matchingKeys;
207        }
208        
209        /**
210         * @return the list of individual keys or wildcard keys that this group will match 
211         */
212        protected String[] getKeysToMatch() {
213            return errorKeyMatch.split(",");
214        }
215        
216        /**
217         * Determines if the given error key matches the keyToMatch - either because the two keys are
218         * equal, or if the keyToMatch is a wildcard key and would wildcard match the key
219         * @param key the error key to match
220         * @param keyToMatch one of the error keys this group will display
221         * @return true if the keys match, false if not
222         */
223        protected boolean foundKeyMatch(String key, String keyToMatch) {
224            return key.equals(keyToMatch) || (keyToMatch.endsWith("*") && key.startsWith(keyToMatch.replaceAll("\\*", "")));
225        }
226        
227        /**
228         * Determines if the given key matches any error key associated with this group
229         * @param key the error key that may or may not be displayed here
230         * @param keysToMatch the keys that this group will match against
231         * @return true if this group can display the given key, false otherwise
232         */
233        protected boolean matchesGroup(String key, String[] keysToMatch) {
234            for (String keyToMatch : keysToMatch) {
235                if (foundKeyMatch(key, keyToMatch)) return true;
236            }
237            return false;
238        }
239    
240        /**
241         * Looks up the InfoPropertyList from the generating request
242         * @param pageContext the pageContext which this tag is rendering to
243         * @return the ErrorPropertyList from the request
244         */
245        public List getErrorPropertyList(PageContext pageContext) {
246            return (List)pageContext.getRequest().getAttribute("ErrorPropertyList");
247        }
248        
249        /**
250         * Looks up the InfoPropertyList from the generating request
251         * @param pageContext the pageContext which this tag is rendering to
252         * @return the WarningPropertyList from the request
253         */
254        protected List getWarningPropertyList(PageContext pageContext) {
255            return (List)pageContext.getRequest().getAttribute("WarningPropertyList");
256        }
257        
258        /**
259         * Looks up the InfoPropertyList from the generating request
260         * @param pageContext the pageContext which this tag is rendering to
261         * @return the InfoPropertyList from the request
262         */
263        protected List getInfoPropertyList(PageContext pageContext) {
264            return (List)pageContext.getRequest().getAttribute("InfoPropertyList");
265        }
266    
267        /**
268         * Gets the errorsRendered attribute. 
269         * @return Returns the errorsRendered.
270         */
271        public List<String> getErrorsRendered() {
272            if (errorsRendered == null) {
273                errorsRendered = new ArrayList<String>();
274            }
275            return errorsRendered;
276        }
277        
278        /**
279         * Gets the warningsRendered attribute. 
280         * @return Returns the warningsRendered.
281         */
282        public List<String> getWarningsRendered() {
283            if (warningsRendered == null) {
284                warningsRendered = new ArrayList<String>();
285            }
286            return warningsRendered;
287        }
288        
289        /**
290         * Gets the infoRendered attribute. 
291         * @return Returns the infoRendered.
292         */
293        public List<String> getInfoRendered() {
294            if (infoRendered == null) {
295                infoRendered = new ArrayList<String>();
296            }
297            return infoRendered;
298        }
299    
300        /**
301         * Gets the errorKeyMatch attribute. 
302         * @return Returns the errorKeyMatch.
303         */
304        public String getErrorKeyMatch() {
305            return errorKeyMatch;
306        }
307    
308        /**
309         * Sets the errorKeyMatch attribute value.
310         * @param errorKeyMatch The errorKeyMatch to set.
311         */
312        public void setErrorKeyMatch(String errorKeyMatch) {
313            this.errorKeyMatch = errorKeyMatch;
314        }
315    
316        /**
317         * Gets the colSpan attribute. 
318         * @return Returns the colSpan.
319         */
320        public int getColSpan() {
321            return colSpan;
322        }
323    
324        /**
325         * Sets the colSpan attribute value.
326         * @param colSpan The colSpan to set.
327         */
328        public void setColSpan(int colSpan) {
329            this.colSpan = colSpan;
330        }
331    
332    }