Coverage Report - org.apache.myfaces.renderkit.html.HtmlRenderKitImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
HtmlRenderKitImpl
0%
0/108
0%
0/82
3.667
HtmlRenderKitImpl$LazyRendererWrapper
0%
0/7
0%
0/2
3.667
HtmlRenderKitImpl$MyFacesResponseStream
0%
0/13
N/A
3.667
 
 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.renderkit.html;
 20  
 
 21  
 import java.io.IOException;
 22  
 import java.io.OutputStream;
 23  
 import java.io.Writer;
 24  
 import java.util.Collections;
 25  
 import java.util.HashMap;
 26  
 import java.util.Iterator;
 27  
 import java.util.Map;
 28  
 import java.util.concurrent.ConcurrentHashMap;
 29  
 import java.util.logging.Level;
 30  
 import java.util.logging.Logger;
 31  
 import javax.faces.application.ProjectStage;
 32  
 
 33  
 import javax.faces.context.FacesContext;
 34  
 import javax.faces.context.ResponseStream;
 35  
 import javax.faces.context.ResponseWriter;
 36  
 import javax.faces.render.ClientBehaviorRenderer;
 37  
 import javax.faces.render.RenderKit;
 38  
 import javax.faces.render.Renderer;
 39  
 import javax.faces.render.RendererWrapper;
 40  
 import javax.faces.render.ResponseStateManager;
 41  
 
 42  
 import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFRenderKit;
 43  
 import org.apache.myfaces.renderkit.LazyRenderKit;
 44  
 import org.apache.myfaces.shared.config.MyfacesConfig;
 45  
 import org.apache.myfaces.shared.renderkit.ContentTypeUtils;
 46  
 import org.apache.myfaces.shared.renderkit.html.HtmlRendererUtils;
 47  
 import org.apache.myfaces.shared.renderkit.html.HtmlResponseWriterImpl;
 48  
 import org.apache.myfaces.shared.util.ClassUtils;
 49  
 
 50  
 /**
 51  
  * @author Manfred Geiler (latest modification by $Author$)
 52  
  * @version $Revision$ $Date$
 53  
  */
 54  
 @JSFRenderKit(renderKitId = "HTML_BASIC")
 55  
 public class HtmlRenderKitImpl extends RenderKit implements LazyRenderKit
 56  
 {
 57  
     //private static final Log log = LogFactory.getLog(HtmlRenderKitImpl.class);
 58  0
     private static final Logger log = Logger.getLogger(HtmlRenderKitImpl.class.getName());
 59  
 
 60  
     // ~ Instance fields ----------------------------------------------------------------------------
 61  
 
 62  
     private Map<String, Map<String, Renderer>> _renderers;
 63  
     private ResponseStateManager _responseStateManager;
 64  
     //private Map<String,Set<String>> _families;
 65  
     private Map<String, ClientBehaviorRenderer> _clientBehaviorRenderers;
 66  
     
 67  
     // ~ Constructors -------------------------------------------------------------------------------
 68  
 
 69  
     public HtmlRenderKitImpl()
 70  0
     {
 71  0
         _renderers = new ConcurrentHashMap<String, Map<String, Renderer>>(64, 0.75f, 1);
 72  0
         _responseStateManager = new HtmlResponseStateManager();
 73  
         //_families = new HashMap<String, Set<String> >();
 74  0
         _clientBehaviorRenderers = new HashMap<String, ClientBehaviorRenderer>();
 75  0
     }
 76  
 
 77  
     // ~ Methods ------------------------------------------------------------------------------------
 78  
 
 79  
     @Override
 80  
     public void addClientBehaviorRenderer(String type, ClientBehaviorRenderer renderer)
 81  
     {
 82  0
         if (type == null)
 83  
         {
 84  0
             throw new NullPointerException("client behavior renderer type must not be null");
 85  
         }
 86  0
         if ( renderer == null)
 87  
         {
 88  0
             throw new NullPointerException("client behavior renderer must not be null");
 89  
         }
 90  
         
 91  0
         _clientBehaviorRenderers.put(type, renderer);
 92  0
     }
 93  
     
 94  
     @Override
 95  
     public ClientBehaviorRenderer getClientBehaviorRenderer(String type)
 96  
     {
 97  0
         if (type == null)
 98  
         {
 99  0
             throw new NullPointerException("client behavior renderer type must not be null");
 100  
         }
 101  
         
 102  0
         return _clientBehaviorRenderers.get(type);
 103  
     }
 104  
     
 105  
     @Override
 106  
     public Iterator<String> getClientBehaviorRendererTypes()
 107  
     {
 108  0
         return _clientBehaviorRenderers.keySet().iterator();
 109  
     }
 110  
     
 111  
     @Override
 112  
     public Renderer getRenderer(String componentFamily, String rendererType)
 113  
     {
 114  0
         if (componentFamily == null)
 115  
         {
 116  0
             throw new NullPointerException("component family must not be null.");
 117  
         }
 118  0
         if (rendererType == null)
 119  
         {
 120  0
             throw new NullPointerException("renderer type must not be null.");
 121  
         }
 122  0
         Map <String,Renderer> familyRendererMap = _renderers.get(componentFamily); 
 123  0
         Renderer renderer = null;
 124  0
         if (familyRendererMap != null)
 125  
         {
 126  0
             renderer = familyRendererMap.get(rendererType);
 127  
         }
 128  0
         if (renderer == null)
 129  
         {
 130  0
             log.warning("Unsupported component-family/renderer-type: " + componentFamily + "/" + rendererType);
 131  
         }
 132  0
         if (renderer instanceof LazyRendererWrapper)
 133  
         {
 134  0
             renderer = ((LazyRendererWrapper)renderer).getWrapped();
 135  0
             familyRendererMap.put(rendererType, renderer);
 136  
         }
 137  0
         return renderer;
 138  
     }
 139  
 
 140  
     @Override
 141  
     public void addRenderer(String componentFamily, String rendererType, Renderer renderer)
 142  
     {
 143  0
         if (componentFamily == null)
 144  
         {
 145  0
             log.severe("addRenderer: componentFamily = null is not allowed");
 146  0
             throw new NullPointerException("component family must not be null.");
 147  
         }
 148  0
         if (rendererType == null)
 149  
         {
 150  0
             log.severe("addRenderer: rendererType = null is not allowed");
 151  0
             throw new NullPointerException("renderer type must not be null.");
 152  
         }
 153  0
         if (renderer == null)
 154  
         {
 155  0
             log.severe("addRenderer: renderer = null is not allowed");
 156  0
             throw new NullPointerException("renderer must not be null.");
 157  
         }
 158  
         
 159  0
         _put(componentFamily, rendererType, renderer);
 160  
 
 161  0
         if (log.isLoggable(Level.FINEST))
 162  
         {
 163  0
             log.finest("add Renderer family = " + componentFamily + " rendererType = " + rendererType
 164  
                     + " renderer class = " + renderer.getClass().getName());
 165  
         }
 166  0
     }
 167  
     
 168  
     public void addRenderer(String componentFamily, String rendererType, String rendererClass)
 169  
     {
 170  0
         if (componentFamily == null)
 171  
         {
 172  0
             log.severe("addRenderer: componentFamily = null is not allowed");
 173  0
             throw new NullPointerException("component family must not be null.");
 174  
         }
 175  0
         if (rendererType == null)
 176  
         {
 177  0
             log.severe("addRenderer: rendererType = null is not allowed");
 178  0
             throw new NullPointerException("renderer type must not be null.");
 179  
         }
 180  0
         if (rendererClass == null)
 181  
         {
 182  0
             log.severe("addRenderer: renderer = null is not allowed");
 183  0
             throw new NullPointerException("renderer must not be null.");
 184  
         }
 185  
 
 186  0
         _put(componentFamily, rendererType, new LazyRendererWrapper(rendererClass));
 187  
 
 188  0
         if (log.isLoggable(Level.FINEST))
 189  
         {
 190  0
             log.finest("add Renderer family = " + componentFamily + " rendererType = " + rendererType
 191  
                     + " renderer class = " + rendererClass);
 192  
         }
 193  0
     }
 194  
 
 195  
     /**
 196  
      * Put the renderer on the double map
 197  
      * 
 198  
      * @param componentFamily
 199  
      * @param rendererType
 200  
      * @param renderer
 201  
      */
 202  
     synchronized private void _put(String componentFamily, String rendererType, Renderer renderer)
 203  
     {
 204  0
         Map <String,Renderer> familyRendererMap = _renderers.get(componentFamily);
 205  0
         if (familyRendererMap == null)
 206  
         {
 207  0
             familyRendererMap = new ConcurrentHashMap<String, Renderer>(8, 0.75f, 1);
 208  0
             _renderers.put(componentFamily, familyRendererMap);
 209  
         }
 210  
         else
 211  
         {
 212  0
             if (familyRendererMap.get(rendererType) != null)
 213  
             {
 214  
                 // this is not necessarily an error, but users do need to be
 215  
                 // very careful about jar processing order when overriding
 216  
                 // some component's renderer with an alternate renderer.
 217  0
                 log.fine("Overwriting renderer with family = " + componentFamily +
 218  
                    " rendererType = " + rendererType +
 219  
                    " renderer class = " + renderer.getClass().getName());
 220  
             }
 221  
         }
 222  0
         familyRendererMap.put(rendererType, renderer);
 223  0
     }
 224  
 
 225  
     @Override
 226  
     public ResponseStateManager getResponseStateManager()
 227  
     {
 228  0
         return _responseStateManager;
 229  
     }
 230  
     
 231  
     /**
 232  
      * @since JSF 2.0
 233  
      */
 234  
     @Override
 235  
     public Iterator<String> getComponentFamilies()
 236  
     {
 237  
         //return _families.keySet().iterator();
 238  0
         return _renderers.keySet().iterator();
 239  
     }
 240  
     
 241  
     /**
 242  
      * @since JSF 2.0
 243  
      */
 244  
     @Override
 245  
     public Iterator<String> getRendererTypes(String componentFamily)
 246  
     {
 247  
         //Return an Iterator over the renderer-type entries for the given component-family.
 248  0
         Map<String, Renderer> map = _renderers.get(componentFamily);
 249  0
         if (map != null)
 250  
         {
 251  0
             return map.keySet().iterator();
 252  
         }
 253  
         /*
 254  
         Set<String> rendererTypes = _families.get(componentFamily);
 255  
         if(rendererTypes != null)
 256  
         {
 257  
             return rendererTypes.iterator();
 258  
         }*/
 259  
         //If the specified componentFamily is not known to this RenderKit implementation, return an empty Iterator
 260  0
         return Collections.<String>emptySet().iterator();
 261  
         
 262  
 
 263  
 
 264  
     }
 265  
 
 266  
     @Override
 267  
     public ResponseWriter createResponseWriter(Writer writer, String contentTypeListString, String characterEncoding)
 268  
     {
 269  0
         FacesContext facesContext = FacesContext.getCurrentInstance();
 270  0
         MyfacesConfig myfacesConfig = MyfacesConfig.getCurrentInstance(
 271  
                 facesContext.getExternalContext());
 272  0
         String selectedContentType = null;
 273  0
         String writerContentType = null;
 274  0
         boolean isAjaxRequest = facesContext.getPartialViewContext().isAjaxRequest();
 275  0
         String contentTypeListStringFromAccept = null;
 276  
 
 277  
         // To detect the right contentType, we need to check if the request is an ajax request or not.
 278  
         // If it is an ajax request, HTTP Accept header content type will be set for the ajax itself, which
 279  
         // is application/xml or text/xml. In that case, there are two response writers
 280  
         // (PartialResponseWriterImpl and HtmlResponseWriterImpl),
 281  
         
 282  
         //1. if there is a passed contentTypeListString, it takes precedence over accept header
 283  0
         if (contentTypeListString != null)
 284  
         {
 285  0
             selectedContentType = ContentTypeUtils.chooseWriterContentType(contentTypeListString, 
 286  
                     ContentTypeUtils.HTML_ALLOWED_CONTENT_TYPES, 
 287  
                     isAjaxRequest ? ContentTypeUtils.AJAX_XHTML_ALLOWED_CONTENT_TYPES :
 288  
                                     ContentTypeUtils.XHTML_ALLOWED_CONTENT_TYPES);
 289  
         }
 290  
 
 291  
         //2. If no selectedContentType
 292  
         //   try to derive it from accept header
 293  0
         if (selectedContentType == null)
 294  
         {
 295  0
             contentTypeListStringFromAccept = 
 296  
                 ContentTypeUtils.getContentTypeFromAcceptHeader(facesContext);
 297  
             
 298  0
             if (contentTypeListStringFromAccept != null)
 299  
             {
 300  0
                 selectedContentType = ContentTypeUtils.chooseWriterContentType(contentTypeListStringFromAccept,
 301  
                         ContentTypeUtils.HTML_ALLOWED_CONTENT_TYPES, 
 302  
                         isAjaxRequest ? ContentTypeUtils.AJAX_XHTML_ALLOWED_CONTENT_TYPES :
 303  
                                         ContentTypeUtils.XHTML_ALLOWED_CONTENT_TYPES);
 304  
             }
 305  
         }
 306  
 
 307  
         //3. if no selectedContentType was derived, set default from the param 
 308  0
         if (selectedContentType == null)
 309  
         {
 310  0
             if (contentTypeListString == null && contentTypeListStringFromAccept == null)
 311  
             {
 312  
                 //If no contentTypeList, return the default
 313  0
                 selectedContentType = myfacesConfig.getDefaultResponseWriterContentTypeMode();
 314  
             }
 315  
             else
 316  
             {
 317  
                 // If a contentTypeList was passed and we don't have direct matches, we still need
 318  
                 // to check if */* is found and if that so return the default, otherwise throw
 319  
                 // exception.
 320  0
                 if (contentTypeListString != null)
 321  
                 {
 322  0
                     String[] contentTypes = ContentTypeUtils.splitContentTypeListString(contentTypeListString);
 323  0
                     if (ContentTypeUtils.containsContentType(ContentTypeUtils.ANY_CONTENT_TYPE, contentTypes))
 324  
                     {
 325  0
                         selectedContentType = myfacesConfig.getDefaultResponseWriterContentTypeMode();
 326  
                     }
 327  
                 }
 328  
                 
 329  0
                 if (selectedContentType == null)
 330  
                 {
 331  0
                     if (contentTypeListStringFromAccept != null)
 332  
                     {
 333  0
                         String[] contentTypes = ContentTypeUtils.splitContentTypeListString(
 334  
                                 contentTypeListStringFromAccept);
 335  0
                         if (ContentTypeUtils.containsContentType(ContentTypeUtils.ANY_CONTENT_TYPE, contentTypes))
 336  
                         {
 337  0
                             selectedContentType = myfacesConfig.getDefaultResponseWriterContentTypeMode();
 338  
                         }
 339  0
                     }
 340  0
                     else if (isAjaxRequest)
 341  
                     {
 342  
                         // If is an ajax request, contentTypeListStringFromAccept == null and 
 343  
                         // contentTypeListString != null, contentTypeListString should not be taken 
 344  
                         // into account, because the final content type in this case is for PartialResponseWriter 
 345  
                         // implementation. In this case rfc2616-sec14 takes precedence:
 346  
                         // 
 347  
                         // http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
 348  
                         // 14.1 Accept 
 349  
                         // If no Accept header field is present, then it is assumed that the client 
 350  
                         // accepts all media types.
 351  0
                         selectedContentType = myfacesConfig.getDefaultResponseWriterContentTypeMode();
 352  
                     }
 353  0
                     if (selectedContentType == null)
 354  
                     {
 355  
                         // Note this case falls when contentTypeListStringFromAccept == null and 
 356  
                         // contentTypeListString != null, but since this not an ajax request, 
 357  
                         // contentTypeListString should be taken strictly and throw IllegalArgumentException
 358  0
                         throw new IllegalArgumentException(
 359  
                                 "ContentTypeList does not contain a supported content type: "
 360  
                                         + ((contentTypeListString != null) ? 
 361  
                                                 contentTypeListString : contentTypeListStringFromAccept) );
 362  
                     }
 363  
                 }
 364  
             }
 365  
         }
 366  0
         if (isAjaxRequest)
 367  
         {
 368  
             // If HTTP Accept header has application/xml or text/xml, that does not means the writer
 369  
             // content type mode should be set to application/xhtml+xml.
 370  0
             writerContentType = selectedContentType.indexOf(ContentTypeUtils.XHTML_CONTENT_TYPE) != -1 ?
 371  
                     ContentTypeUtils.XHTML_CONTENT_TYPE : ContentTypeUtils.HTML_CONTENT_TYPE;
 372  
         }
 373  
         else
 374  
         {
 375  0
             writerContentType = HtmlRendererUtils.isXHTMLContentType(selectedContentType) ? 
 376  
                     ContentTypeUtils.XHTML_CONTENT_TYPE : ContentTypeUtils.HTML_CONTENT_TYPE;
 377  
         }
 378  
         
 379  0
         if (characterEncoding == null)
 380  
         {
 381  0
             characterEncoding = HtmlRendererUtils.DEFAULT_CHAR_ENCODING;
 382  
         }
 383  
 
 384  0
         if (myfacesConfig.isEarlyFlushEnabled() &&
 385  
                 facesContext.isProjectStage(ProjectStage.Production))
 386  
         {
 387  0
             return new EarlyFlushHtmlResponseWriterImpl(writer, selectedContentType, characterEncoding, 
 388  
                 myfacesConfig.isWrapScriptContentWithXmlCommentTag(),
 389  
                         writerContentType);
 390  
         }
 391  
         else
 392  
         {
 393  0
             return new HtmlResponseWriterImpl(writer, selectedContentType, characterEncoding, 
 394  
                 myfacesConfig.isWrapScriptContentWithXmlCommentTag(),
 395  
                         writerContentType);
 396  
         }
 397  
     }
 398  
 
 399  
     @Override
 400  
     public ResponseStream createResponseStream(OutputStream outputStream)
 401  
     {
 402  0
         return new MyFacesResponseStream(outputStream);
 403  
     }
 404  
 
 405  
     private static class MyFacesResponseStream extends ResponseStream
 406  
     {
 407  
         private OutputStream output;
 408  
 
 409  
         public MyFacesResponseStream(OutputStream output)
 410  0
         {
 411  0
             this.output = output;
 412  0
         }
 413  
 
 414  
         @Override
 415  
         public void write(int b) throws IOException
 416  
         {
 417  0
             output.write(b);
 418  0
         }
 419  
 
 420  
         @Override
 421  
         public void write(byte b[]) throws IOException
 422  
         {
 423  0
             output.write(b);
 424  0
         }
 425  
 
 426  
         @Override
 427  
         public void write(byte b[], int off, int len) throws IOException
 428  
         {
 429  0
             output.write(b, off, len);
 430  0
         }
 431  
 
 432  
         @Override
 433  
         public void flush() throws IOException
 434  
         {
 435  0
             output.flush();
 436  0
         }
 437  
 
 438  
         @Override
 439  
         public void close() throws IOException
 440  
         {
 441  0
             output.close();
 442  0
         }
 443  
     }
 444  
     
 445  0
     private static class LazyRendererWrapper extends RendererWrapper
 446  
     {
 447  
         private String rendererClass;
 448  
         private Renderer delegate;
 449  
         
 450  
         public LazyRendererWrapper(String rendererClass)
 451  0
         {
 452  0
             this.rendererClass = rendererClass;
 453  0
         }
 454  
 
 455  
         @Override
 456  
         public Renderer getWrapped()
 457  
         {
 458  0
             if (delegate == null)
 459  
             {
 460  0
                 delegate = (Renderer) ClassUtils.newInstance(
 461  
                     ClassUtils.simpleClassForName(rendererClass));
 462  
             }
 463  0
             return delegate;
 464  
         }
 465  
     }
 466  
 }