001    package org.apache.myfaces.tobago.renderkit;
002    
003    /*
004     * Licensed to the Apache Software Foundation (ASF) under one or more
005     * contributor license agreements.  See the NOTICE file distributed with
006     * this work for additional information regarding copyright ownership.
007     * The ASF licenses this file to You under the Apache License, Version 2.0
008     * (the "License"); you may not use this file except in compliance with
009     * the License.  You may obtain a copy of the License at
010     *
011     *      http://www.apache.org/licenses/LICENSE-2.0
012     *
013     * Unless required by applicable law or agreed to in writing, software
014     * distributed under the License is distributed on an "AS IS" BASIS,
015     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016     * See the License for the specific language governing permissions and
017     * limitations under the License.
018     */
019    
020    import org.apache.myfaces.tobago.ajax.AjaxUtils;
021    import org.apache.myfaces.tobago.application.ProjectStage;
022    import org.apache.myfaces.tobago.config.TobagoConfig;
023    import org.apache.myfaces.tobago.context.Capability;
024    import org.apache.myfaces.tobago.internal.webapp.DebugResponseWriterWrapper;
025    import org.apache.myfaces.tobago.internal.webapp.HtmlResponseWriter;
026    import org.apache.myfaces.tobago.internal.webapp.JsonResponseWriter;
027    import org.apache.myfaces.tobago.internal.webapp.XmlResponseWriter;
028    import org.apache.myfaces.tobago.util.VariableResolverUtils;
029    import org.apache.myfaces.tobago.webapp.TobagoResponseWriter;
030    import org.slf4j.Logger;
031    import org.slf4j.LoggerFactory;
032    
033    import javax.faces.FactoryFinder;
034    import javax.faces.context.FacesContext;
035    import javax.faces.context.ResponseStream;
036    import javax.faces.context.ResponseWriter;
037    import javax.faces.render.RenderKit;
038    import javax.faces.render.RenderKitFactory;
039    import javax.faces.render.Renderer;
040    import javax.faces.render.ResponseStateManager;
041    import java.io.OutputStream;
042    import java.io.Writer;
043    import java.util.HashMap;
044    import java.util.Map;
045    
046    public class TobagoRenderKit extends RenderKit {
047    
048      private static final Logger LOG = LoggerFactory.getLogger(TobagoRenderKit.class);
049    
050      public static final String RENDER_KIT_ID = "tobago";
051    
052      private ResponseStateManager responseStateManager = new TobagoResponseStateManager();
053    
054      private RenderKit htmlBasicRenderKit;
055    
056      private Map<Key, Renderer> renderers = new HashMap<Key, Renderer>();
057    
058      @Override
059      public Renderer getRenderer(String family, String rendererType) {
060        Renderer renderer = renderers.get(new Key(family, rendererType));
061        if (renderer == null) {
062          RenderKit renderKit = getHtmlBasicRenderKit();
063          renderer = renderKit.getRenderer(family, rendererType);
064          if (renderer != null) {
065            renderer = new RendererBaseWrapper(renderer);
066          }
067        }
068    
069        if (renderer == null) {
070          LOG.error("The class which was found by the ResourceManager cannot be "
071              + "found or instantiated: classname='" + rendererType + "'");
072        }
073    
074        return renderer;
075      }
076    
077      private RenderKit getHtmlBasicRenderKit() {
078        if (htmlBasicRenderKit == null) {
079          RenderKitFactory rkFactory = (RenderKitFactory) FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
080          htmlBasicRenderKit =
081              rkFactory.getRenderKit(FacesContext.getCurrentInstance(), RenderKitFactory.HTML_BASIC_RENDER_KIT);
082        }
083        return htmlBasicRenderKit;
084      }
085    
086      @Override
087      public ResponseWriter createResponseWriter(
088          Writer writer, String contentTypeList, String characterEncoding) {
089        String contentType;
090        FacesContext facesContext = FacesContext.getCurrentInstance();
091        if (AjaxUtils.isAjaxRequest(facesContext)) {
092          return new JsonResponseWriter(writer, "application/json", characterEncoding);
093        }
094        if (contentTypeList == null) {
095          contentType = "text/html";
096        } else if (contentTypeList.indexOf("text/html") > -1) {
097          contentType = "text/html";
098          LOG.warn("patching content type from " + contentTypeList + " to " + contentType + "'");
099        } else if (contentTypeList.indexOf("text/fo") > -1) {
100          contentType = "text/fo";
101          LOG.warn("patching content type from " + contentTypeList + " to " + contentType + "'");
102        } else if (contentTypeList.indexOf("application/json") > -1) {
103          return new JsonResponseWriter(writer, "application/json", characterEncoding);
104        } else {
105          contentType = "text/html";
106          LOG.warn("Content-Type '" + contentTypeList + "' not supported! Using text/html");
107        }
108    
109    // XXX enable xhtml here, by hand:
110    //    contentType = "application/xhtml+xml";
111    
112        boolean xml = false;
113        if ("application/xhtml+xml".equals(contentType)
114            || "application/xhtml".equals(contentType)
115            || "application/xml".equals(contentType)
116            || "text/xml".equals(contentType)) {
117          xml = true;
118        }
119    
120        // content type xhtml is not supported in every browser... e. g. IE 6, 7, 8
121        if (!VariableResolverUtils.resolveClientProperties(FacesContext.getCurrentInstance())
122            .getUserAgent().hasCapability(Capability.CONTENT_TYPE_XHTML)) {
123          contentType = "text/html";
124        }
125    
126        TobagoResponseWriter responseWriter;
127        if (xml) {
128          responseWriter = new XmlResponseWriter(writer, contentType, characterEncoding);
129        } else {
130          responseWriter = new HtmlResponseWriter(writer, contentType, characterEncoding);
131        }
132        if (TobagoConfig.getInstance(facesContext).getProjectStage() == ProjectStage.Development) {
133          responseWriter = new DebugResponseWriterWrapper(responseWriter);
134        }
135        return responseWriter;
136      }
137    
138      @Override
139      public void addRenderer(String family, String rendererType, Renderer renderer) {
140        renderers.put(new Key(family, rendererType), renderer);
141      }
142    
143      @Override
144      public ResponseStateManager getResponseStateManager() {
145        //return getHtmlBasicRenderKit().getResponseStateManager();
146        return responseStateManager;
147      }
148    
149      @Override
150      public ResponseStream createResponseStream(OutputStream outputStream) {
151        return getHtmlBasicRenderKit().createResponseStream(outputStream);
152      }
153    
154      private static final class Key {
155        private final String family;
156        private final String rendererType;
157    
158        private Key(String family, String rendererType) {
159          this.family = family;
160          this.rendererType = rendererType;
161        }
162    
163        public boolean equals(Object o) {
164          if (this == o) {
165            return true;
166          }
167          if (o == null || getClass() != o.getClass()) {
168            return false;
169          }
170    
171          Key key = (Key) o;
172    
173          if (!family.equals(key.family)) {
174            return false;
175          }
176          if (!rendererType.equals(key.rendererType)) {
177            return false;
178          }
179    
180          return true;
181        }
182    
183        public int hashCode() {
184          int result;
185          result = family.hashCode();
186          result = 31 * result + rendererType.hashCode();
187          return result;
188        }
189      }
190    }