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   * $Id: HtmlRoundedDivRenderer.java 659874 2008-05-24 20:59:15Z lu4242 $
20   */
21  package org.apache.myfaces.custom.roundeddiv;
22  
23  import java.awt.Color;
24  import java.awt.Dimension;
25  import java.awt.image.BufferedImage;
26  import java.io.ByteArrayOutputStream;
27  import java.io.IOException;
28  import java.io.OutputStream;
29  import java.util.ArrayList;
30  import java.util.Collections;
31  import java.util.HashMap;
32  import java.util.HashSet;
33  import java.util.Iterator;
34  import java.util.List;
35  import java.util.Map;
36  import java.util.Set;
37  import java.util.regex.Matcher;
38  import java.util.regex.Pattern;
39  
40  import javax.faces.component.UIComponent;
41  import javax.faces.context.FacesContext;
42  import javax.faces.context.ResponseWriter;
43  import javax.imageio.ImageIO;
44  import javax.servlet.ServletContext;
45  import javax.servlet.http.HttpServletRequest;
46  import javax.servlet.http.HttpServletResponse;
47  
48  import org.apache.commons.logging.Log;
49  import org.apache.commons.logging.LogFactory;
50  import org.apache.myfaces.component.html.util.ParameterResourceHandler;
51  import org.apache.myfaces.custom.htmlTag.HtmlTagRenderer;
52  import org.apache.myfaces.renderkit.html.util.AddResource;
53  import org.apache.myfaces.renderkit.html.util.AddResourceFactory;
54  import org.apache.myfaces.renderkit.html.util.ResourceLoader;
55  import org.apache.myfaces.shared_tomahawk.renderkit.RendererUtils;
56  import org.apache.myfaces.shared_tomahawk.renderkit.html.HTML;
57  
58  /**
59   * Renderer for the {@link HtmlRoundedDiv} component.
60   *
61   * 
62   * @JSFRenderer
63   *   renderKitId = "HTML_BASIC" 
64   *   family = "javax.faces.Output"
65   *   type = "org.apache.myfaces.HtmlRoundedDiv"
66   * 
67   * @author Andrew Robinson (latest modification by $Author: lu4242 $)
68   * @version $Revision: 659874 $ $Date: 2008-05-24 22:59:15 +0200 (Sat, 24 May 2008) $
69   */
70  public class HtmlRoundedDivRenderer extends HtmlTagRenderer implements
71          ResourceLoader
72  {
73      private final static Log log = LogFactory
74              .getLog(HtmlRoundedDivRenderer.class);
75      private static Map imageCache = Collections.synchronizedMap(new HashMap());
76      private static List cacheQueue = Collections
77              .synchronizedList(new ArrayList());
78      private static Integer cacheSize;
79  
80      private final static int DEFAULT_CACHE_SIZE = 20;
81      private final static String CACHE_SIZE_KEY = "org.apache.myfaces.ROUNDED_DIV_CACHE_SIZE";
82      public final static String RENDERER_TYPE = "org.apache.myfaces.HtmlRoundedDiv";
83  
84      /**
85       * @see org.apache.myfaces.renderkit.html.util.ResourceLoader#serveResource(
86       * javax.servlet.ServletContext, javax.servlet.http.HttpServletRequest, 
87       * javax.servlet.http.HttpServletResponse, java.lang.String)
88       */
89      public void serveResource(ServletContext context,
90              HttpServletRequest request, HttpServletResponse response,
91              String resourceUri) throws IOException
92      {
93          initCache(context);
94          boolean useCache = cacheSize.intValue() > 0;
95  
96          String cacheKey = null;
97          RoundedBorderGenerator borderGen = null;
98          if (useCache)
99          {
100             cacheKey = getCacheKey(request.getQueryString());
101             borderGen = (RoundedBorderGenerator) imageCache.get(cacheKey);
102         }
103 
104         if (borderGen == null)
105         {
106             if (useCache)
107             {
108                 borderGen = initCachedGenerator(request, cacheKey);
109             }
110             else
111             {
112                 borderGen = buildGenerator(request);
113             }
114         }
115 
116         String area = request.getParameter("a");
117         BufferedImage img;
118         if (request.getParameter("s") != null)
119         {
120             img = borderGen.createImage();
121         }
122         else
123         {
124             img = borderGen.createImageSection(getAreaAsSection(area));
125         }
126 
127         if (cacheSize.intValue() == 0)
128         {
129             borderGen.dispose();
130         }
131 
132         response.setContentType("image/png");
133 
134         ByteArrayOutputStream baos = new ByteArrayOutputStream();
135         ImageIO.write(img, "png", baos);
136         byte[] data = baos.toByteArray();
137         baos = null;
138 
139         response.setContentLength(data.length);
140         OutputStream out = response.getOutputStream();
141         try
142         {
143             out.write(data);
144         }
145         finally
146         {
147             out.close();
148         }
149     }
150 
151     /**
152      * @param request
153      * @param cacheKey
154      * @return
155      */
156     protected RoundedBorderGenerator initCachedGenerator(
157             HttpServletRequest request, String cacheKey)
158     {
159         RoundedBorderGenerator borderGen;
160 
161         synchronized (imageCache)
162         {
163             borderGen = buildGenerator(request);
164 
165             imageCache.put(cacheKey, borderGen);
166             cacheQueue.add(cacheKey);
167 
168             while (cacheQueue.size() > cacheSize.intValue())
169             {
170                 RoundedBorderGenerator gen = (RoundedBorderGenerator) imageCache
171                         .remove(cacheQueue.remove(0));
172                 gen.dispose();
173             }
174         }
175         return borderGen;
176     }
177 
178     protected RoundedBorderGenerator buildGenerator(HttpServletRequest request)
179     {
180         RoundedBorderGenerator borderGen;
181         String param;
182         Color foregroundColor = Color.decode("#" + request.getParameter("c"));
183 
184         Dimension size = null;
185         if ((param = request.getParameter("s")) != null)
186         {
187             int i = param.indexOf('x');
188             size = new Dimension(Integer.parseInt(param.substring(0, i)),
189                     Integer.parseInt(param.substring(i + 1)));
190         }
191 
192         Color backgroundColor = null, borderColor = null;
193         if ((param = request.getParameter("bgc")) != null)
194         {
195             backgroundColor = Color.decode("#" + param);
196         }
197         if ((param = request.getParameter("bc")) != null)
198         {
199             borderColor = Color.decode("#" + param);
200         }
201 
202         int borderWidth = Integer.parseInt(request.getParameter("bw"));
203         int radius = Integer.parseInt(request.getParameter("r"));
204 
205         boolean inverse = "t".equals(request.getParameter("i"));
206 
207         borderGen = new RoundedBorderGenerator(borderColor, borderWidth,
208                 radius, foregroundColor, backgroundColor, size, inverse);
209         return borderGen;
210     }
211 
212     /**
213      * @see org.apache.myfaces.custom.htmlTag.HtmlTagRenderer#encodeBegin(
214      * javax.faces.context.FacesContext, javax.faces.component.UIComponent)
215      */
216     public void encodeBegin(FacesContext context, UIComponent component)
217             throws IOException
218     {
219         super.encodeBegin(context, component);
220         if (!component.isRendered())
221             return;
222 
223         boolean ie = isIE(context);
224 
225         HtmlRoundedDiv div = (HtmlRoundedDiv) component;
226         ResponseWriter writer = context.getResponseWriter();
227         Set areas = null;
228 
229         if (div.getSize() == null)
230         {
231             areas = getAreasToRender(context, div);
232         }
233 
234         if ("table".equals(div.getValue()))
235         {
236             encodeTable(context, ie, div, writer, areas);
237         }
238         else
239         {
240             encodeDivBegin(context, ie, div, writer, areas);
241         }
242     }
243 
244     protected void encodeDivBegin(FacesContext context, boolean ie,
245             HtmlRoundedDiv div, ResponseWriter writer, Set areas)
246             throws IOException
247     {
248         if (div.getSize() != null)
249         {
250             addImage(writer, context, div, null, null, ie);
251         }
252         else
253         {
254             for (Iterator iter = areas.iterator(); iter.hasNext();)
255             {
256                 String area = (String) iter.next();
257                 addImage(writer, context, div, area, areas, ie);
258             }
259         }
260 
261         // create a DIV for the contents
262         writer.startElement(HTML.DIV_ELEM, div);
263 
264         StringBuffer style = new StringBuffer("position: relative; z-index: 1;");
265         addPadding(Math.max(div.getBorderWidth().intValue(), div.getRadius()
266                 .intValue()), style, areas);
267 
268         String val;
269         if ((val = div.getContentStyle()) != null)
270         {
271             style.append(val);
272         }
273 
274         writer.writeAttribute(HTML.STYLE_ATTR, style, null);
275         if ((val = div.getContentStyleClass()) != null)
276         {
277             writer.writeAttribute(HTML.CLASS_ATTR, val, null);
278         }
279     }
280 
281     protected void encodeTable(FacesContext context, boolean ie,
282             HtmlRoundedDiv div, ResponseWriter writer, Set areas)
283             throws IOException
284     {
285         int padding = Math.max(div.getBorderWidth().intValue(), div.getRadius()
286                 .intValue());
287 
288         writer.writeAttribute(HTML.CELLPADDING_ATTR, "0", null);
289         writer.writeAttribute(HTML.CELLSPACING_ATTR, "0", null);
290 
291         writer.startElement(HTML.TBODY_ELEM, div);
292 
293         writer.startElement(HTML.TR_ELEM, div);
294         encodeImageColumn(writer, context, div, "topleft", areas, padding, ie);
295         encodeImageColumn(writer, context, div, "top", areas, padding, ie);
296         encodeImageColumn(writer, context, div, "topright", areas, padding, ie);
297         writer.endElement(HTML.TR_ELEM);
298         writer.startElement(HTML.TR_ELEM, div);
299         encodeImageColumn(writer, context, div, "left", areas, padding, ie);
300         encodeContentColumn(writer, context, div, areas, ie);
301         encodeImageColumn(writer, context, div, "right", areas, padding, ie);
302         writer.endElement(HTML.TR_ELEM);
303         writer.startElement(HTML.TR_ELEM, div);
304         encodeImageColumn(writer, context, div, "bottomleft", areas, padding,
305                 ie);
306         encodeImageColumn(writer, context, div, "bottom", areas, padding, ie);
307         encodeImageColumn(writer, context, div, "bottomright", areas, padding,
308                 ie);
309         writer.endElement(HTML.TR_ELEM);
310 
311         writer.endElement(HTML.TBODY_ELEM);
312     }
313 
314     /**
315      * @see javax.faces.render.Renderer#encodeChildren(javax.faces.context.FacesContext, javax.faces.component.UIComponent)
316      */
317     public void encodeChildren(FacesContext context, UIComponent component)
318             throws IOException
319     {
320         if (!component.isRendered())
321         {
322             return;
323         }
324         HtmlRoundedDiv div = (HtmlRoundedDiv) component;
325         if ("div".equals(div.getValue()))
326         {
327             RendererUtils.renderChildren(context, component);
328         }
329     }
330 
331     /**
332      * @see org.apache.myfaces.custom.htmlTag.HtmlTagRenderer#encodeEnd(javax.faces.context.FacesContext, javax.faces.component.UIComponent)
333      */
334     public void encodeEnd(FacesContext context, UIComponent component)
335             throws IOException
336     {
337         super.encodeEnd(context, component);
338         if (!component.isRendered())
339         {
340             return;
341         }
342         HtmlRoundedDiv div = (HtmlRoundedDiv) component;
343         if ("div".equals(div.getValue()))
344         {
345             context.getResponseWriter().endElement(HTML.DIV_ELEM);
346         }
347     }
348 
349     /**
350      * @see javax.faces.render.Renderer#getRendersChildren()
351      */
352     public boolean getRendersChildren()
353     {
354         return true;
355     }
356 
357     /**
358      * Convert the string area to one of the integer constants from
359      * {@link RoundedBorderGenerator}
360      * 
361      * @param area the area as a string
362      * @return integer constant value
363      */
364     protected int getAreaAsSection(String area)
365     {
366         if ("top".equals(area))
367         {
368             return RoundedBorderGenerator.SECTION_TOP;
369         }
370         else if ("topright".equals(area))
371         {
372             return RoundedBorderGenerator.SECTION_TOPRIGHT;
373         }
374         else if ("right".equals(area))
375         {
376             return RoundedBorderGenerator.SECTION_RIGHT;
377         }
378         else if ("bottomright".equals(area))
379         {
380             return RoundedBorderGenerator.SECTION_BOTTOMRIGHT;
381         }
382         else if ("bottom".equals(area))
383         {
384             return RoundedBorderGenerator.SECTION_BOTTOM;
385         }
386         else if ("bottomleft".equals(area))
387         {
388             return RoundedBorderGenerator.SECTION_BOTTOMLEFT;
389         }
390         else if ("left".equals(area))
391         {
392             return RoundedBorderGenerator.SECTION_LEFT;
393         }
394         else if ("topleft".equals(area))
395         {
396             return RoundedBorderGenerator.SECTION_TOPLEFT;
397         }
398         else if ("center".equals(area))
399         {
400             return RoundedBorderGenerator.SECTION_CENTER;
401         }
402         else
403         {
404             throw new IllegalArgumentException("Invalid area: " + area);
405         }
406     }
407 
408     /**
409      * Get the areas to render. This takes into account the corners
410      * that the user has requested to be rendered.
411      * 
412      * @param context {@link FacesContext}
413      * @param component The component
414      * @return A set of strings containing the areas to render
415      */
416     protected Set getAreasToRender(FacesContext context,
417             HtmlRoundedDiv component)
418     {
419         String corners = component.getCorners();
420         Set areas = new HashSet();
421 
422         if (corners != null)
423         {
424             String[] cornersArr = corners.split("\\s*,\\s*");
425             for (int i = 0; i < cornersArr.length; i++)
426             {
427                 if ("topleft".equals(cornersArr[i]))
428                 {
429                     areas.add("top");
430                     areas.add("left");
431                     areas.add("topleft");
432                 }
433                 else if ("topright".equals(cornersArr[i]))
434                 {
435                     areas.add("top");
436                     areas.add("right");
437                     areas.add("topright");
438                 }
439                 else if ("bottomright".equals(cornersArr[i]))
440                 {
441                     areas.add("right");
442                     areas.add("bottom");
443                     areas.add("bottomright");
444                 }
445                 else if ("bottomleft".equals(cornersArr[i]))
446                 {
447                     areas.add("bottom");
448                     areas.add("bottomleft");
449                     areas.add("left");
450                 }
451                 else
452                 {
453                     throw new IllegalArgumentException("Invalid corner: "
454                             + cornersArr[i]);
455                 }
456             }
457             areas.add("center");
458         }
459         else
460         {
461             areas.add("top");
462             areas.add("topright");
463             areas.add("right");
464             areas.add("bottomright");
465             areas.add("bottom");
466             areas.add("bottomleft");
467             areas.add("left");
468             areas.add("topleft");
469             areas.add("center");
470         }
471         return areas;
472     }
473 
474     /**
475      * Adds an IMG tag to the response for an image
476      *  
477      * @param writer The response writer
478      * @param context The faces context
479      * @param component The component
480      * @param area The area of the image
481      * @param areas All the areas to be rendered
482      * @param ie If the browser is IE
483      * @throws IOException when writing to the writer
484      */
485     protected void addImage(ResponseWriter writer, FacesContext context,
486             HtmlRoundedDiv component, String area, Set areas, boolean ie)
487             throws IOException
488     {
489         StringBuffer style = new StringBuffer(
490                 "position: absolute;padding: 0px;margin: 0px;");
491         String url = getImageSource(context, component, area);
492         int padding = Math.max(component.getBorderWidth().intValue(), component
493                 .getRadius().intValue());
494 
495         if (ie)
496         {
497             // NOT CURRENTLY WORKING:
498             //            // IE transparency fix
499             //            if (component.getBackgroundColor() == null
500             //                    && ("topright".equals(area) || "topleft".equals(area)
501             //                            || "bottomright".equals(area) || "bottomleft"
502             //                            .equals(area)) && isIE6(context))
503             //            {
504             //                setIESrcForCorner(context, component, url, style, padding);
505             //                url = AddResourceFactory.getInstance(context).getResourceUri(
506             //                        context, HtmlRoundedDiv.class, "blank.gif");
507             //            }
508 
509             if (areas != null)
510             {
511                 setIECssPosition(padding, style, area, areas);
512             }
513         }
514         else if (areas != null)
515         {
516             setCssPosition(padding, style, area, areas);
517         }
518 
519         writer.startElement(HTML.IMG_ELEM, component);
520         writer.writeAttribute(HTML.SRC_ATTR, url, null);
521         writer.writeAttribute(HTML.STYLE_ATTR, style, null);
522         writer.endElement(HTML.IMG_ELEM);
523     }
524 
525     /**
526      * Set the CSS positioning attributes for IE6
527      * 
528      * @param padding The size of the image section
529      * @param style The current style
530      * @param area The area
531      * @param areas All the areas to be rendered
532      */
533     protected void setIECssPosition(int padding, StringBuffer style,
534             String area, Set areas)
535     {
536         if ("center".equals(area))
537         {
538             int p = 0;
539             if (areas.contains("left"))
540             {
541                 p += padding;
542             }
543             if (areas.contains("right"))
544             {
545                 p += padding;
546             }
547             style.append("width: expression((offsetParent.clientWidth - ")
548                     .append(p).append(") + 'px');");
549 
550             p = 0;
551             if (areas.contains("top"))
552             {
553                 p += padding;
554             }
555             if (areas.contains("bottom"))
556             {
557                 p += padding;
558             }
559             style.append("height: expression((offsetParent.clientHeight - ")
560                     .append(p).append(") + 'px');");
561 
562             style.append("top: ").append(areas.contains("top") ? padding : 0)
563                     .append("px;");
564             style.append("left: ").append(areas.contains("left") ? padding : 0)
565                     .append("px;");
566         }
567         else if ("top".equals(area))
568         {
569             style.append("top: 0px;");
570             style.append("left: ").append(areas.contains("left") ? padding : 0)
571                     .append("px;");
572             style.append("height: ").append(padding).append("px;");
573 
574             int p = 0;
575             if (areas.contains("left"))
576             {
577                 p += padding;
578             }
579             if (areas.contains("right"))
580             {
581                 p += padding;
582             }
583             style.append("width: expression((offsetParent.clientWidth - ")
584                     .append(p).append(") + 'px');");
585         }
586         else if ("topright".equals(area))
587         {
588             style.append("top: 0px;");
589             style.append("right: 0px;");
590         }
591         else if ("right".equals(area))
592         {
593             style.append("top: ").append(areas.contains("top") ? padding : 0)
594                     .append("px;");
595             style.append("right: 0px;");
596             style.append("width: ").append(padding).append("px;");
597 
598             int p = 0;
599             if (areas.contains("top"))
600             {
601                 p += padding;
602             }
603             if (areas.contains("bottom"))
604             {
605                 p += padding;
606             }
607             style.append("height: expression((offsetParent.clientHeight - ")
608                     .append(p).append(") + 'px');");
609         }
610         else if ("bottomright".equals(area))
611         {
612             style.append("right: 0px;");
613             style.append("bottom: 0px;");
614         }
615         else if ("bottom".equals(area))
616         {
617             style.append("bottom: 0px;");
618             style.append("left: ").append(areas.contains("left") ? padding : 0)
619                     .append("px;");
620             style.append("height: ").append(padding).append("px;");
621 
622             int p = 0;
623             if (areas.contains("left"))
624             {
625                 p += padding;
626             }
627             if (areas.contains("right"))
628             {
629                 p += padding;
630             }
631             style.append("width: expression((offsetParent.clientWidth - ")
632                     .append(p).append(") + 'px');");
633         }
634         else if ("bottomleft".equals(area))
635         {
636             style.append("bottom: 0px;");
637             style.append("left: 0px;");
638         }
639         else if ("left".equals(area))
640         {
641             style.append("top: ").append(areas.contains("top") ? padding : 0)
642                     .append("px;");
643             style.append("left: 0px;");
644             style.append("width: ").append(padding).append("px;");
645 
646             int p = 0;
647             if (areas.contains("top"))
648             {
649                 p += padding;
650             }
651             if (areas.contains("bottom"))
652             {
653                 p += padding;
654             }
655             style.append("height: expression((offsetParent.clientHeight - ")
656                     .append(p).append(") + 'px');");
657         }
658         else
659         //if ("topleft".equals(area))
660         {
661             style.append("top: 0px;");
662             style.append("left: 0px;");
663         }
664     }
665 
666     protected void encodeContentColumn(ResponseWriter writer,
667             FacesContext context, HtmlRoundedDiv component, Set areas,
668             boolean ie) throws IOException
669     {
670         writer.startElement(HTML.TD_ELEM, component);
671         String val;
672         StringBuffer style = new StringBuffer();
673 
674         if ((val = component.getContentStyle()) != null)
675         {
676             style.append(val).append(';');
677         }
678         style.append("background-image: url('").append(
679                 getImageSource(context, component, "center")).append("');");
680         writer.writeAttribute(HTML.STYLE_ATTR, style, null);
681 
682         if ((val = component.getContentStyleClass()) != null)
683         {
684             writer.writeAttribute(HTML.CLASS_ATTR, val, null);
685         }
686 
687         RendererUtils.renderChildren(context, component);
688 
689         writer.endElement(HTML.TD_ELEM);
690     }
691 
692     protected void encodeImageColumn(ResponseWriter writer,
693             FacesContext context, HtmlRoundedDiv component, String area,
694             Set areas, int padding, boolean ie) throws IOException
695     {
696         writer.startElement(HTML.TD_ELEM, component);
697         writer.writeAttribute(HTML.CLASS_ATTR, area, null);
698         if (areas.contains(area))
699         {
700             StringBuffer style = new StringBuffer("font-size: 1px; background-image: url('")
701                 .append(getImageSource(context,
702                     component, area)).append("');")
703                 .append("background-position: top left;");
704             
705             if ("left".equals(area) || "right".equals(area))
706             {
707                 style.append("background-repeat: repeat-y; width: ")
708                     .append(padding).append("px;");
709             }
710             else if ("top".equals(area) || "bottom".equals(area))
711             {
712                 style.append("background-repeat: repeat-x; height: ")
713                     .append(padding).append("px;");
714             }
715             
716             writer.writeAttribute(HTML.STYLE_ATTR, style, null);
717 
718             // force the browser to allocate this column in rendering
719             writer.write("&#160;");
720         }
721         writer.endElement(HTML.TD_ELEM);
722     }
723 
724     // NOT CURRENTLY WORKING:
725     //    /**
726     //     * Set the CSS style for IE6 to work-around the IE6 PNG alpha-transparency
727     //     * bug
728     //     * 
729     //     * @param context The faces context
730     //     * @param component The component
731     //     * @param url The URL of the image
732     //     * @param style The current style
733     //     */
734     //    protected void setIESrcForCorner(FacesContext context,
735     //            HtmlRoundedDiv component, String url, StringBuffer style,
736     //            int imageSize)
737     //    {
738     //        style
739     //                .append(
740     //                        " filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='")
741     //                .append(url).append("', sizingMethod='image', enabled=true);").append(
742     //                        "height: ").append(imageSize).append("px; width: ")
743     //                .append(imageSize).append("px;");
744     //    }
745 
746     /**
747      * Set the CSS position using CSS2 attributes
748      * 
749      * @param padding The size of the image section
750      * @param style The current style
751      * @param area The area
752      * @param areas All the areas to be rendered
753      */
754     protected void setCssPosition(int padding, StringBuffer style, String area,
755             Set areas)
756     {
757         if ("center".equals(area))
758         {
759             style.append("top: ").append(areas.contains("top") ? padding : 0)
760                     .append("px;");
761             style.append("right: ").append(
762                     areas.contains("right") ? padding : 0).append("px;");
763             style.append("bottom: ").append(
764                     areas.contains("bottom") ? padding : 0).append("px;");
765             style.append("left: ").append(areas.contains("left") ? padding : 0)
766                     .append("px;");
767         }
768         else if ("top".equals(area))
769         {
770             style.append("top: 0px;");
771             style.append("right: ").append(
772                     areas.contains("right") ? padding : 0).append("px;");
773             style.append("left: ").append(areas.contains("left") ? padding : 0)
774                     .append("px;");
775             style.append("height: ").append(padding).append("px;");
776         }
777         else if ("topright".equals(area))
778         {
779             style.append("top: 0px;");
780             style.append("right: 0px;");
781         }
782         else if ("right".equals(area))
783         {
784             style.append("top: ").append(areas.contains("top") ? padding : 0)
785                     .append("px;");
786             style.append("right: 0px;");
787             style.append("bottom: ").append(
788                     areas.contains("bottom") ? padding : 0).append("px;");
789             style.append("width: ").append(padding).append("px;");
790         }
791         else if ("bottomright".equals(area))
792         {
793             style.append("right: 0px;");
794             style.append("bottom: 0px;");
795         }
796         else if ("bottom".equals(area))
797         {
798             style.append("right: ").append(
799                     areas.contains("right") ? padding : 0).append("px;");
800             style.append("bottom: 0px;");
801             style.append("left: ").append(areas.contains("left") ? padding : 0)
802                     .append("px;");
803             style.append("height: ").append(padding).append("px;");
804         }
805         else if ("bottomleft".equals(area))
806         {
807             style.append("bottom: 0px;");
808             style.append("left: 0px;");
809         }
810         else if ("left".equals(area))
811         {
812             style.append("top: ").append(areas.contains("top") ? padding : 0)
813                     .append("px;");
814             style.append("bottom: ").append(
815                     areas.contains("bottom") ? padding : 0).append("px;");
816             style.append("left: 0px;");
817             style.append("width: ").append(padding).append("px;");
818         }
819         else
820         //if ("topleft".equals(area))
821         {
822             style.append("top: 0px;");
823             style.append("left: 0px;");
824         }
825     }
826 
827     /**
828      * Get the URL to use to let this class work with the MyFaces resource handling
829      * in order to generate the image
830      * 
831      * @param context The faces context
832      * @param component The component
833      * @param area The area
834      * @return The URL of the image
835      */
836     protected String getImageSource(FacesContext context,
837             HtmlRoundedDiv component, String area)
838     {
839         AddResource addResource = AddResourceFactory.getInstance(context);
840         return addResource.getResourceUri(context,
841                 new ParameterResourceHandler(this.getClass(),
842                         buildParameterMap(component, area)));
843     }
844 
845     /**
846      * Adds padding to the inner DIV so that the contents do not overlap
847      * the rounded corner
848      * 
849      * @param padding The max of the border width and radius
850      * @param style The style
851      * @param areas The areas to render
852      */
853     protected void addPadding(int padding, StringBuffer style, Set areas)
854     {
855         int top = 0, right = 0, bottom = 0, left = 0;
856         if (areas == null || areas.contains("top"))
857         {
858             top = padding;
859         }
860         if (areas == null || areas.contains("right"))
861         {
862             right = padding;
863         }
864         if (areas == null || areas.contains("bottom"))
865         {
866             bottom = padding;
867         }
868         if (areas == null || areas.contains("left"))
869         {
870             left = padding;
871         }
872 
873         style.append(" padding: ").append(top).append("px ").append(right)
874                 .append("px ").append(bottom).append("px ").append(left)
875                 .append("px;");
876     }
877 
878     protected Dimension _getSize(HtmlRoundedDiv component){
879         String size = component.getSize();
880         
881         Matcher m = Pattern.compile("(\\d+)\\D+(\\d+)").matcher(size);
882         if (!m.find())
883         {
884             throw new IllegalArgumentException("Invalid dimension");
885         }
886         return new Dimension(Integer.parseInt(m.group(1)), Integer
887                 .parseInt(m.group(2)));       
888     }
889     
890     /**
891      * Build a set of parameters as a map that are needed for the rendering
892      * 
893      * @param component The component
894      * @param area The area
895      * @return Map of parameters
896      */
897     protected Map buildParameterMap(HtmlRoundedDiv component, String area)
898     {
899         Map map = new HashMap(7);
900         if (component.getColor() != null){
901             map.put("c", colorToHtml(Color.decode(component.getColor())));
902         }
903         Color c;
904         if (component.getBackgroundColor() != null)
905         {
906             c = Color.decode(component.getBackgroundColor());
907             map.put("bgc", colorToHtml(c));
908         }
909         if ( component.getBorderColor() != null)
910         {
911             c = Color.decode(component.getBorderColor());
912             map.put("bc", colorToHtml(c));
913         }
914 
915         map.put("bw", component.getBorderWidth().toString());
916         map.put("r", component.getRadius().toString());
917         Dimension d;
918         if ( component.getSize() != null)
919         {
920             d = _getSize(component);
921             map.put("s", d.width + "x"
922                     + d.height);
923         }
924         if (area != null)
925         {
926             map.put("a", area);
927         }
928         map.put("i", component.getInverse().booleanValue() ? "t" : "f");
929         return map;
930     }
931 
932     /**
933      * Convert a color to an HTML style color (i.e. FFFFFF)
934      * 
935      * @param c The color
936      * @return Color as hexidemal representation
937      */
938     protected String colorToHtml(Color c)
939     {
940         int rbg = c.getRGB();
941         String[] strs = { Integer.toHexString((rbg >> 16) & 0xFF),
942                 Integer.toHexString((rbg >> 8) & 0xFF),
943                 Integer.toHexString(rbg & 0xFF) };
944 
945         StringBuffer sb = new StringBuffer();
946         for (int i = 0; i < strs.length; i++)
947         {
948             if (strs[i].length() == 1)
949             {
950                 sb.append('0');
951             }
952             sb.append(strs[i]);
953         }
954         return sb.toString();
955     }
956 
957     /**
958      * Check if the user is using IE
959      * 
960      * @param context Faces context
961      * @return True if IE
962      */
963     protected boolean isIE(FacesContext context)
964     {
965         try
966         {
967             String userAgent = context.getExternalContext()
968                     .getRequestHeaderMap().get("User-Agent").toString();
969 
970             return userAgent.toUpperCase().indexOf("MSIE") >= 0;
971         }
972         catch (NullPointerException ex)
973         {
974             // should never happen, but just in case
975             return false;
976         }
977     }
978 
979     private void initCache(ServletContext context)
980     {
981         if (cacheSize != null)
982         {
983             return;
984         }
985         synchronized (imageCache)
986         {
987             if (cacheSize != null)
988             {
989                 return;
990             }
991 
992             String param = context.getInitParameter(CACHE_SIZE_KEY);
993             if (cacheSize == null)
994             {
995                 cacheSize = new Integer(DEFAULT_CACHE_SIZE);
996             }
997             else
998             {
999                 try
1000                 {
1001                     cacheSize = new Integer(Integer.parseInt(param));
1002                 }
1003                 catch (NumberFormatException ex)
1004                 {
1005                     log.error("Unabled to parse cache size, using default", ex);
1006                     cacheSize = new Integer(DEFAULT_CACHE_SIZE);
1007                 }
1008             }
1009             log.debug("Using a cache of size: " + cacheSize);
1010         }
1011     }
1012 
1013     protected String getCacheKey(String queryString)
1014     {
1015         int from = queryString.indexOf("&a=") + 1;
1016         
1017         if (from == -1)
1018         {
1019             return queryString;
1020         }
1021         
1022         int to = queryString.indexOf('&', from + 1);
1023         
1024         return to == -1 ? queryString.substring(0, from) : queryString.substring(
1025                 0, from) + queryString.substring(to);
1026     }
1027 
1028     //    /**
1029     //     * Check if the user is using IE6
1030     //     * 
1031     //     * @param context Faces context
1032     //     * @return True if IE
1033     //     */
1034     //    protected boolean isIE6(FacesContext context)
1035     //    {
1036     //        try
1037     //        {
1038     //            String userAgent = context.getExternalContext()
1039     //                    .getRequestHeaderMap().get("User-Agent").toString();
1040     //
1041     //            return userAgent.toUpperCase().indexOf("MSIE 6") >= 0;
1042     //        }
1043     //        catch (NullPointerException ex)
1044     //        {
1045     //            // should never happen, but just in case
1046     //            return false;
1047     //        }
1048     //    }
1049 }