All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.myfaces.renderkit.html.HtmlRenderKitImpl Maven / Gradle / Ivy

Go to download

The private implementation classes of the Apache MyFaces Core JSF-2.3-next Implementation

There is a newer version: 4.1.0-RC2
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.myfaces.renderkit.html;

import java.io.IOException;
import java.io.OutputStream;
import java.io.Writer;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.faces.context.FacesContext;
import javax.faces.context.ResponseStream;
import javax.faces.context.ResponseWriter;
import javax.faces.render.ClientBehaviorRenderer;
import javax.faces.render.RenderKit;
import javax.faces.render.Renderer;
import javax.faces.render.RendererWrapper;
import javax.faces.render.ResponseStateManager;

import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFRenderKit;
import org.apache.myfaces.renderkit.LazyRenderKit;
import org.apache.myfaces.config.MyfacesConfig;
import org.apache.myfaces.renderkit.ContentTypeUtils;
import org.apache.myfaces.util.lang.Assert;
import org.apache.myfaces.util.lang.ClassUtils;

/**
 * @author Manfred Geiler (latest modification by $Author$)
 * @version $Revision$ $Date$
 */
@JSFRenderKit(renderKitId = "HTML_BASIC")
public class HtmlRenderKitImpl extends RenderKit implements LazyRenderKit
{
    private static final Logger log = Logger.getLogger(HtmlRenderKitImpl.class.getName());

    // ~ Instance fields ----------------------------------------------------------------------------

    private Map> _renderers;
    private ResponseStateManager _responseStateManager;
    private Map _clientBehaviorRenderers;
    private MyfacesConfig myfacesConfig;
    
    // ~ Constructors -------------------------------------------------------------------------------

    public HtmlRenderKitImpl()
    {
        _renderers = new ConcurrentHashMap<>(64, 0.75f, 1);
        _responseStateManager = new HtmlResponseStateManager();
        _clientBehaviorRenderers = new HashMap<>();
        myfacesConfig = MyfacesConfig.getCurrentInstance();
    }

    // ~ Methods ------------------------------------------------------------------------------------

    @Override
    public void addClientBehaviorRenderer(String type, ClientBehaviorRenderer renderer)
    {
        Assert.notNull(type, "type");
        Assert.notNull(renderer, "renderer");
        
        _clientBehaviorRenderers.put(type, renderer);
    }
    
    @Override
    public ClientBehaviorRenderer getClientBehaviorRenderer(String type)
    {
        Assert.notNull(type, "type");
        
        return _clientBehaviorRenderers.get(type);
    }
    
    @Override
    public Iterator getClientBehaviorRendererTypes()
    {
        return _clientBehaviorRenderers.keySet().iterator();
    }
    
    @Override
    public Renderer getRenderer(String componentFamily, String rendererType)
    {
        Assert.notNull(componentFamily, "componentFamily");
        Assert.notNull(rendererType, "rendererType");
        
        Map  familyRendererMap = _renderers.get(componentFamily); 
        Renderer renderer = null;
        if (familyRendererMap != null)
        {
            renderer = familyRendererMap.get(rendererType);
        }
        if (renderer == null)
        {
            log.warning("Unsupported component-family/renderer-type: " + componentFamily + '/' + rendererType);
        }
        if (renderer instanceof LazyRendererWrapper)
        {
            renderer = ((LazyRendererWrapper)renderer).getWrapped();
            familyRendererMap.put(rendererType, renderer);
        }
        return renderer;
    }

    @Override
    public void addRenderer(String componentFamily, String rendererType, Renderer renderer)
    {
        Assert.notNull(componentFamily, "componentFamily");
        Assert.notNull(rendererType, "rendererType");
        Assert.notNull(renderer, "renderer");

        _put(componentFamily, rendererType, renderer);

        if (log.isLoggable(Level.FINEST))
        {
            log.finest("add Renderer family = " + componentFamily + " rendererType = " + rendererType
                    + " renderer class = " + renderer.getClass().getName());
        }
    }
    
    @Override
    public void addRenderer(String componentFamily, String rendererType, String rendererClass)
    {
        Assert.notNull(componentFamily, "componentFamily");
        Assert.notNull(rendererType, "rendererType");
        Assert.notNull(rendererClass, "rendererClass");

        _put(componentFamily, rendererType, new LazyRendererWrapper(rendererClass));

        if (log.isLoggable(Level.FINEST))
        {
            log.finest("add Renderer family = " + componentFamily + " rendererType = " + rendererType
                    + " renderer class = " + rendererClass);
        }
    }

    /**
     * Put the renderer on the double map
     * 
     * @param componentFamily
     * @param rendererType
     * @param renderer
     */
    synchronized private void _put(String componentFamily, String rendererType, Renderer renderer)
    {
        Map  familyRendererMap = _renderers.get(componentFamily);
        if (familyRendererMap == null)
        {
            familyRendererMap = new ConcurrentHashMap<>(8, 0.75f, 1);
            _renderers.put(componentFamily, familyRendererMap);
        }
        else
        {
            if (familyRendererMap.get(rendererType) != null)
            {
                // this is not necessarily an error, but users do need to be
                // very careful about jar processing order when overriding
                // some component's renderer with an alternate renderer.
                log.fine("Overwriting renderer with family = " + componentFamily +
                   " rendererType = " + rendererType +
                   " renderer class = " + renderer.getClass().getName());
            }
        }
        familyRendererMap.put(rendererType, renderer);
    }

    @Override
    public ResponseStateManager getResponseStateManager()
    {
        return _responseStateManager;
    }
    
    /**
     * @since JSF 2.0
     */
    @Override
    public Iterator getComponentFamilies()
    {
        //return _families.keySet().iterator();
        return _renderers.keySet().iterator();
    }
    
    /**
     * @since JSF 2.0
     */
    @Override
    public Iterator getRendererTypes(String componentFamily)
    {
        //Return an Iterator over the renderer-type entries for the given component-family.
        Map map = _renderers.get(componentFamily);
        if (map != null)
        {
            return map.keySet().iterator();
        }

        //If the specified componentFamily is not known to this RenderKit implementation, return an empty Iterator
        return Collections.emptySet().iterator();
    }

    @Override
    public ResponseWriter createResponseWriter(Writer writer, String contentTypeListString, String characterEncoding)
    {
        FacesContext facesContext = FacesContext.getCurrentInstance();
        String selectedContentType = null;
        String writerContentType = null;
        boolean isAjaxRequest = facesContext.getPartialViewContext().isAjaxRequest();
        String contentTypeListStringFromAccept = null;

        // To detect the right contentType, we need to check if the request is an ajax request or not.
        // If it is an ajax request, HTTP Accept header content type will be set for the ajax itself, which
        // is application/xml or text/xml. In that case, there are two response writers
        // (PartialResponseWriterImpl and HtmlResponseWriterImpl),
        
        //1. if there is a passed contentTypeListString, it takes precedence over accept header
        if (contentTypeListString != null)
        {
            selectedContentType = ContentTypeUtils.chooseWriterContentType(contentTypeListString, 
                    ContentTypeUtils.HTML_ALLOWED_CONTENT_TYPES, 
                    isAjaxRequest ? ContentTypeUtils.AJAX_XHTML_ALLOWED_CONTENT_TYPES :
                                    ContentTypeUtils.XHTML_ALLOWED_CONTENT_TYPES);
        }

        //2. If no selectedContentType
        //   try to derive it from accept header
        if (selectedContentType == null)
        {
            contentTypeListStringFromAccept = 
                ContentTypeUtils.getContentTypeFromAcceptHeader(facesContext);
            
            if (contentTypeListStringFromAccept != null)
            {
                selectedContentType = ContentTypeUtils.chooseWriterContentType(contentTypeListStringFromAccept,
                        ContentTypeUtils.HTML_ALLOWED_CONTENT_TYPES, 
                        isAjaxRequest ? ContentTypeUtils.AJAX_XHTML_ALLOWED_CONTENT_TYPES :
                                        ContentTypeUtils.XHTML_ALLOWED_CONTENT_TYPES);
            }
        }

        //3. if no selectedContentType was derived, set default from the param 
        if (selectedContentType == null)
        {
            if (contentTypeListString == null && contentTypeListStringFromAccept == null)
            {
                //If no contentTypeList, return the default
                selectedContentType = myfacesConfig.getDefaultResponseWriterContentTypeMode();
            }
            else
            {
                // If a contentTypeList was passed and we don't have direct matches, we still need
                // to check if */* is found and if that so return the default, otherwise throw
                // exception.
                if (contentTypeListString != null)
                {
                    String[] contentTypes = ContentTypeUtils.splitContentTypeListString(contentTypeListString);
                    if (ContentTypeUtils.containsContentType(ContentTypeUtils.ANY_CONTENT_TYPE, contentTypes))
                    {
                        selectedContentType = myfacesConfig.getDefaultResponseWriterContentTypeMode();
                    }
                }
                
                if (selectedContentType == null)
                {
                    if (contentTypeListStringFromAccept != null)
                    {
                        String[] contentTypes = ContentTypeUtils.splitContentTypeListString(
                                contentTypeListStringFromAccept);
                        if (ContentTypeUtils.containsContentType(ContentTypeUtils.ANY_CONTENT_TYPE, contentTypes))
                        {
                            selectedContentType = myfacesConfig.getDefaultResponseWriterContentTypeMode();
                        }
                    }
                    else if (isAjaxRequest)
                    {
                        // If is an ajax request, contentTypeListStringFromAccept == null and 
                        // contentTypeListString != null, contentTypeListString should not be taken 
                        // into account, because the final content type in this case is for PartialResponseWriter 
                        // implementation. In this case rfc2616-sec14 takes precedence:
                        // 
                        // http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
                        // 14.1 Accept 
                        // If no Accept header field is present, then it is assumed that the client 
                        // accepts all media types.
                        selectedContentType = myfacesConfig.getDefaultResponseWriterContentTypeMode();
                    }
                    if (selectedContentType == null)
                    {
                        // Note this case falls when contentTypeListStringFromAccept == null and 
                        // contentTypeListString != null, but since this not an ajax request, 
                        // contentTypeListString should be taken strictly and throw IllegalArgumentException
                        throw new IllegalArgumentException(
                                "ContentTypeList does not contain a supported content type: "
                                        + ((contentTypeListString != null) ? 
                                                contentTypeListString : contentTypeListStringFromAccept) );
                    }
                }
            }
        }
        if (isAjaxRequest)
        {
            // If HTTP Accept header has application/xml or text/xml, that does not means the writer
            // content type mode should be set to application/xhtml+xml.
            writerContentType = selectedContentType.contains(ContentTypeUtils.XHTML_CONTENT_TYPE) ?
                    ContentTypeUtils.XHTML_CONTENT_TYPE : ContentTypeUtils.HTML_CONTENT_TYPE;
        }
        else
        {
            writerContentType = ContentTypeUtils.isXHTMLContentType(selectedContentType) ? 
                    ContentTypeUtils.XHTML_CONTENT_TYPE : ContentTypeUtils.HTML_CONTENT_TYPE;
        }
        
        if (characterEncoding == null)
        {
            characterEncoding = ContentTypeUtils.DEFAULT_CHAR_ENCODING;
        }

        if (myfacesConfig.isEarlyFlushEnabled())
        {
            return new EarlyFlushHtmlResponseWriterImpl(writer, selectedContentType, characterEncoding, 
                myfacesConfig.isWrapScriptContentWithXmlCommentTag(), writerContentType);
        }
        else
        {
            return new HtmlResponseWriterImpl(writer, selectedContentType, characterEncoding, 
                myfacesConfig.isWrapScriptContentWithXmlCommentTag(), writerContentType);
        }
    }

    @Override
    public ResponseStream createResponseStream(OutputStream outputStream)
    {
        return new MyFacesResponseStream(outputStream);
    }

    private static class MyFacesResponseStream extends ResponseStream
    {
        private OutputStream output;

        public MyFacesResponseStream(OutputStream output)
        {
            this.output = output;
        }

        @Override
        public void write(int b) throws IOException
        {
            output.write(b);
        }

        @Override
        public void write(byte b[]) throws IOException
        {
            output.write(b);
        }

        @Override
        public void write(byte b[], int off, int len) throws IOException
        {
            output.write(b, off, len);
        }

        @Override
        public void flush() throws IOException
        {
            output.flush();
        }

        @Override
        public void close() throws IOException
        {
            output.close();
        }
    }
    
    private static class LazyRendererWrapper extends RendererWrapper
    {
        private String rendererClass;
        private Renderer delegate;
        
        public LazyRendererWrapper(String rendererClass)
        {
            this.rendererClass = rendererClass;
        }

        @Override
        public Renderer getWrapped()
        {
            if (delegate == null)
            {
                delegate = (Renderer) ClassUtils.newInstance(
                    ClassUtils.simpleClassForName(rendererClass));
            }
            return delegate;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy