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