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