Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/sun/facelets/FaceletViewHandler.java


1   /**
2    * Licensed under the Common Development and Distribution License,
3    * you may not use this file except in compliance with the License.
4    * You may obtain a copy of the License at
5    * 
6    *   http://www.sun.com/cddl/
7    *   
8    * Unless required by applicable law or agreed to in writing, software
9    * distributed under the License is distributed on an "AS IS" BASIS,
10   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 
11   * implied. See the License for the specific language governing
12   * permissions and limitations under the License.
13   */
14  
15  package com.sun.facelets;
16  
17  import java.io.FileNotFoundException;
18  import java.io.IOException;
19  import java.io.Writer;
20  import java.net.MalformedURLException;
21  import java.net.URL;
22  import java.util.ArrayList;
23  import java.util.Arrays;
24  import java.util.Iterator;
25  import java.util.List;
26  import java.util.Locale;
27  import java.util.Map;
28  import java.util.logging.Level;
29  import java.util.logging.Logger;
30  
31  import javax.el.ELException;
32  import javax.faces.FacesException;
33  import javax.faces.application.StateManager;
34  import javax.faces.application.ViewHandler;
35  import javax.faces.component.UIComponent;
36  import javax.faces.component.UIViewRoot;
37  import javax.faces.context.ExternalContext;
38  import javax.faces.context.FacesContext;
39  import javax.faces.context.ResponseWriter;
40  import javax.faces.render.RenderKit;
41  import javax.portlet.PortletResponse;
42  import javax.servlet.ServletRequest;
43  import javax.servlet.ServletResponse;
44  import javax.servlet.http.HttpServletRequest;
45  import javax.servlet.http.HttpServletResponse;
46  
47  import com.sun.facelets.compiler.Compiler;
48  import com.sun.facelets.compiler.SAXCompiler;
49  import com.sun.facelets.compiler.TagLibraryConfig;
50  import com.sun.facelets.impl.DefaultFaceletFactory;
51  import com.sun.facelets.impl.DefaultResourceResolver;
52  import com.sun.facelets.impl.ResourceResolver;
53  import com.sun.facelets.tag.TagDecorator;
54  import com.sun.facelets.tag.TagLibrary;
55  import com.sun.facelets.tag.jsf.ComponentSupport;
56  import com.sun.facelets.tag.ui.UIDebug;
57  import com.sun.facelets.util.DevTools;
58  import com.sun.facelets.util.FacesAPI;
59  import com.sun.facelets.util.FastWriter;
60  import com.sun.facelets.util.Resource;
61  
62  /**
63   * ViewHandler implementation for Facelets
64   * 
65   * @author Jacob Hookom
66   * @version $Id: FaceletViewHandler.java,v 1.49.2.6 2006/03/20 07:22:00 jhook
67   *          Exp $
68   */
69  public class FaceletViewHandler extends ViewHandler {
70  
71      protected final static Logger log = Logger
72              .getLogger("facelets.viewhandler");
73  
74      public final static long DEFAULT_REFRESH_PERIOD = 2;
75  
76      public final static String PARAM_REFRESH_PERIO = "facelets.REFRESH_PERIOD";
77  
78      public final static String PARAM_SKIP_COMMENTS = "facelets.SKIP_COMMENTS";
79  
80      /**
81       * Context initialization parameter for defining what viewIds should be
82       * handled by Facelets, and what should not. When left unset, all URLs will
83       * be handled by Facelets. When set, it must be a semicolon separated list
84       * of either extension mappings or prefix mappings. For example:
85       * 
86       * <pre>
87       *    
88       *     
89       *      
90       *        &lt;context-param&gt;
91       *          &lt;param-name&gt;facelets.VIEW_MAPPINGS&lt;/param-name&gt;
92       *          &lt;param-value&gt;/demos/*; *.xhtml&lt;/param-value&gt;
93       *        &lt;/context-param&gt;
94       *       
95       *      
96       *     
97       * </pre>
98       * 
99       * would use Facelets for processing all viewIds in the "/demos" directory
100      * or that end in .xhtml, and use the standard JSP engine for all other
101      * viewIds.
102      * <p>
103      * <strong>NOTE</strong>: when using this parameter, you need to use
104      * prefix-mapping for the <code>FacesServlet</code> (that is,
105      * <code>/faces/*</code>, not <code>*.jsf</code>).
106      * </p>
107      */
108     public final static String PARAM_VIEW_MAPPINGS = "facelets.VIEW_MAPPINGS";
109 
110     public final static String PARAM_LIBRARIES = "facelets.LIBRARIES";
111 
112     public final static String PARAM_DECORATORS = "facelets.DECORATORS";
113 
114     public final static String PARAM_DEVELOPMENT = "facelets.DEVELOPMENT";
115 
116     public final static String PARAM_RESOURCE_RESOLVER = "facelets.RESOURCE_RESOLVER";
117 
118     public final static String PARAM_BUILD_BEFORE_RESTORE = "facelets.BUILD_BEFORE_RESTORE";
119 
120     public final static String PARAM_BUFFER_SIZE = "facelets.BUFFER_SIZE";
121 
122     private final static String STATE_KEY = "~facelets.VIEW_STATE~";
123 
124     private final static int STATE_KEY_LEN = STATE_KEY.length();
125 
126     private final static Object STATE_NULL = new Object();
127 
128     private final ViewHandler parent;
129 
130     private boolean developmentMode = false;
131 
132     private boolean buildBeforeRestore = false;
133 
134     private int bufferSize;
135 
136     private String defaultSuffix;
137 
138     private FaceletFactory faceletFactory;
139 
140     // Array of viewId extensions that should be handled by Facelets
141     private String[] extensionsArray;
142 
143     // Array of viewId prefixes that should be handled by Facelets
144     private String[] prefixesArray;
145 
146     /**
147      * 
148      */
149     public FaceletViewHandler(ViewHandler parent) {
150         this.parent = parent;
151     }
152 
153     /**
154      * Initialize the ViewHandler during its first request.
155      */
156     protected void initialize(FacesContext context) {
157         synchronized (this) {
158             if (this.faceletFactory == null) {
159                 log.fine("Initializing");
160                 Compiler c = this.createCompiler();
161                 this.initializeCompiler(c);
162                 this.faceletFactory = this.createFaceletFactory(c);
163 
164                 this.initializeMappings(context);
165                 this.initializeMode(context);
166                 this.initializeBuffer(context);
167 
168                 log.fine("Initialization Successful");
169             }
170         }
171     }
172 
173     private void initializeMode(FacesContext context) {
174         ExternalContext external = context.getExternalContext();
175         String param = external.getInitParameter(PARAM_DEVELOPMENT);
176         this.developmentMode = "true".equals(param);
177 
178         String restoreMode = external
179                 .getInitParameter(PARAM_BUILD_BEFORE_RESTORE);
180         this.buildBeforeRestore = "true".equals(restoreMode);
181     }
182 
183     private void initializeBuffer(FacesContext context) {
184         ExternalContext external = context.getExternalContext();
185         String param = external.getInitParameter(PARAM_BUFFER_SIZE);
186         this.bufferSize = (param != null && !"".equals(param)) ? Integer
187                 .parseInt(param) : -1;
188     }
189 
190     /**
191      * Initialize mappings, during the first request.
192      */
193     private void initializeMappings(FacesContext context) {
194         ExternalContext external = context.getExternalContext();
195         String viewMappings = external.getInitParameter(PARAM_VIEW_MAPPINGS);
196         if ((viewMappings != null) && (viewMappings.length() > 0)) {
197             String[] mappingsArray = viewMappings.split(";");
198 
199             List extensionsList = new ArrayList(mappingsArray.length);
200             List prefixesList = new ArrayList(mappingsArray.length);
201 
202             for (int i = 0; i < mappingsArray.length; i++) {
203                 String mapping = mappingsArray[i].trim();
204                 int mappingLength = mapping.length();
205                 if (mappingLength <= 1) {
206                     continue;
207                 }
208 
209                 if (mapping.charAt(0) == '*') {
210                     extensionsList.add(mapping.substring(1));
211                 } else if (mapping.charAt(mappingLength - 1) == '*') {
212                     prefixesList.add(mapping.substring(0, mappingLength - 1));
213                 }
214             }
215 
216             extensionsArray = new String[extensionsList.size()];
217             extensionsList.toArray(extensionsArray);
218 
219             prefixesArray = new String[prefixesList.size()];
220             prefixesList.toArray(prefixesArray);
221         }
222     }
223 
224     protected FaceletFactory createFaceletFactory(Compiler c) {
225 
226         // refresh period
227         long refreshPeriod = DEFAULT_REFRESH_PERIOD;
228         FacesContext ctx = FacesContext.getCurrentInstance();
229         String userPeriod = ctx.getExternalContext().getInitParameter(
230                 PARAM_REFRESH_PERIO);
231         if (userPeriod != null && userPeriod.length() > 0) {
232             refreshPeriod = Long.parseLong(userPeriod);
233         }
234 
235         // resource resolver
236         ResourceResolver resolver = new DefaultResourceResolver();
237         String resolverName = ctx.getExternalContext().getInitParameter(
238                 PARAM_RESOURCE_RESOLVER);
239         if (resolverName != null && resolverName.length() > 0) {
240             try {
241                 resolver = (ResourceResolver) Class.forName(resolverName, true,
242                         Thread.currentThread().getContextClassLoader())
243                         .newInstance();
244             } catch (Exception e) {
245                 throw new FacesException("Error Initializing ResourceResolver["
246                         + resolverName + "]", e);
247             }
248         }
249 
250         // Resource.getResourceUrl(ctx,"/")
251         return new DefaultFaceletFactory(c, resolver, refreshPeriod);
252     }
253 
254     protected Compiler createCompiler() {
255         return new SAXCompiler();
256     }
257 
258     protected void initializeCompiler(Compiler c) {
259         FacesContext ctx = FacesContext.getCurrentInstance();
260         ExternalContext ext = ctx.getExternalContext();
261 
262         // load libraries
263         String libParam = ext.getInitParameter(PARAM_LIBRARIES);
264         if (libParam != null) {
265             libParam = libParam.trim();
266             String[] libs = libParam.split(";");
267             URL src;
268             TagLibrary libObj;
269             for (int i = 0; i < libs.length; i++) {
270                 try {
271                     src = ext.getResource(libs[i].trim());
272                     if (src == null) {
273                         throw new FileNotFoundException(libs[i]);
274                     }
275                     libObj = TagLibraryConfig.create(src);
276                     c.addTagLibrary(libObj);
277                     log.fine("Successfully Loaded Library: " + libs[i]);
278                 } catch (IOException e) {
279                     log.log(Level.SEVERE, "Error Loading Library: " + libs[i],
280                             e);
281                 }
282             }
283         }
284 
285         // load decorators
286         String decParam = ext.getInitParameter(PARAM_DECORATORS);
287         if (decParam != null) {
288             decParam = decParam.trim();
289             String[] decs = decParam.split(";");
290             TagDecorator decObj;
291             for (int i = 0; i < decs.length; i++) {
292                 try {
293                     decObj = (TagDecorator) Class.forName(decs[i])
294                             .newInstance();
295                     c.addTagDecorator(decObj);
296                     log.fine("Successfully Loaded Decorator: " + decs[i]);
297                 } catch (Exception e) {
298                     log.log(Level.SEVERE,
299                             "Error Loading Decorator: " + decs[i], e);
300                 }
301             }
302         }
303 
304         // skip params?
305         String skipParam = ext.getInitParameter(PARAM_SKIP_COMMENTS);
306         if (skipParam != null && "true".equals(skipParam)) {
307             c.setTrimmingComments(true);
308         }
309     }
310 
311     public UIViewRoot restoreView(FacesContext context, String viewId) {
312         if (UIDebug.debugRequest(context)) {
313             return new UIViewRoot();
314         }
315 
316         if (!this.buildBeforeRestore || !handledByFacelets(viewId)) {
317             return this.parent.restoreView(context, viewId);
318         }
319 
320         if (this.faceletFactory == null) {
321             this.initialize(context);
322         }
323 
324         // In JSF 1.2, restoreView() will only be called on postback.
325         // But in JSF 1.1, it will be called for an initial request too,
326         // in which case we must return null in order to fall through
327         // to createView()
328         if (FacesAPI.getVersion() < 12) {
329             if (!isPostback11(context)) {
330                 return null;
331             }
332         }
333 
334         ViewHandler outerViewHandler = context.getApplication()
335                 .getViewHandler();
336         String renderKitId = outerViewHandler.calculateRenderKitId(context);
337 
338         UIViewRoot viewRoot = createView(context, viewId);
339         context.setViewRoot(viewRoot);
340         try {
341             this.buildView(context, viewRoot);
342         } catch (IOException ioe) {
343             log.log(Level.SEVERE, "Error Building View", ioe);
344         }
345         context.getApplication().getStateManager().restoreView(context, viewId,
346                 renderKitId);
347         return viewRoot;
348     }
349 
350     /*
351      * (non-Javadoc)
352      * 
353      * @see javax.faces.application.ViewHandlerWrapper#getWrapped()
354      */
355     protected ViewHandler getWrapped() {
356         return this.parent;
357     }
358 
359     protected ResponseWriter createResponseWriter(FacesContext context)
360             throws IOException, FacesException {
361         ExternalContext extContext = context.getExternalContext();
362         RenderKit renderKit = context.getRenderKit();
363         // Avoid a cryptic NullPointerException when the renderkit ID
364         // is incorrectly set
365         if (renderKit == null) {
366             String id = context.getViewRoot().getRenderKitId();
367             throw new IllegalStateException(
368                     "No render kit was available for id \"" + id + "\"");
369         }
370 
371         ServletResponse response = (ServletResponse) extContext.getResponse();
372 
373         // set the buffer for content
374         if (this.bufferSize != -1) {
375             response.setBufferSize(this.bufferSize);
376         }
377 
378         // get our content type
379         String contentType = null;
380 
381         // get the encoding
382         String encoding = null;
383 
384         // Create a dummy ResponseWriter with a bogus writer,
385         // so we can figure out what content type the ReponseWriter
386         // is really going to ask for
387         ResponseWriter writer = renderKit.createResponseWriter(
388                 NullWriter.Instance, contentType, encoding);
389 
390         contentType = getResponseContentType(context, writer.getContentType());
391         encoding = getResponseEncoding(context, writer.getCharacterEncoding());
392 
393         // apply them to the response
394         response.setContentType(contentType + "; charset=" + encoding);
395 
396         // removed 2005.8.23 to comply with J2EE 1.3
397         // response.setCharacterEncoding(encoding);
398 
399         // Now, clone with the real writer
400         writer = writer.cloneWithWriter(response.getWriter());
401 
402         return writer;
403     }
404 
405     /**
406      * Generate the encoding
407      * 
408      * @param context
409      * @param orig
410      * @return
411      */
412     protected String getResponseEncoding(FacesContext context, String orig) {
413         String encoding = orig;
414 
415         // see if we need to override the encoding
416         Map m = context.getExternalContext().getRequestMap();
417         Map sm = context.getExternalContext().getSessionMap();
418 
419         // 1. check the request attribute
420         if (m.containsKey("facelets.Encoding")) {
421             encoding = (String) m.get("facelets.Encoding");
422             if (log.isLoggable(Level.FINEST)) {
423                 log.finest("Facelet specified alternate encoding '" + encoding
424                         + "'");
425             }
426             sm.put(CHARACTER_ENCODING_KEY, encoding);
427         }
428 
429         // 2. get it from request
430         Object request = context.getExternalContext().getRequest();
431         if (encoding == null && request instanceof ServletRequest) {
432             encoding = ((ServletRequest) request).getCharacterEncoding();
433         }
434 
435         // 3. get it from the session
436         if (encoding == null) {
437             encoding = (String) sm.get(CHARACTER_ENCODING_KEY);
438             if (log.isLoggable(Level.FINEST)) {
439                 log.finest("Session specified alternate encoding '" + encoding
440                         + "'");
441             }
442         }
443 
444         // 4. default it
445         if (encoding == null) {
446             encoding = "UTF-8";
447             if (log.isLoggable(Level.FINEST)) {
448                 log
449                         .finest("ResponseWriter created had a null CharacterEncoding, defaulting to UTF-8");
450             }
451         }
452 
453         return encoding;
454     }
455 
456     /**
457      * Generate the content type
458      * 
459      * @param context
460      * @param orig
461      * @return
462      */
463     protected String getResponseContentType(FacesContext context, String orig) {
464         String contentType = orig;
465 
466         // see if we need to override the contentType
467         Map m = context.getExternalContext().getRequestMap();
468         if (m.containsKey("facelets.ContentType")) {
469             contentType = (String) m.get("facelets.ContentType");
470             if (log.isLoggable(Level.FINEST)) {
471                 log.finest("Facelet specified alternate contentType '"
472                         + contentType + "'");
473             }
474         }
475 
476         // safety check
477         if (contentType == null) {
478             contentType = "text/html";
479             if (log.isLoggable(Level.FINEST)) {
480                 log
481                         .finest("ResponseWriter created had a null ContentType, defaulting to text/html");
482             }
483         }
484 
485         return contentType;
486     }
487 
488     protected void buildView(FacesContext context, UIViewRoot viewToRender)
489             throws IOException, FacesException {
490         // setup our viewId
491         String renderedViewId = this.getRenderedViewId(context, viewToRender
492                 .getViewId());
493         viewToRender.setViewId(renderedViewId);
494 
495         if (log.isLoggable(Level.FINE)) {
496             log.fine("Building View: " + renderedViewId);
497         }
498 
499         // grab our FaceletFactory and create a Facelet
500         Facelet f = null;
501         FaceletFactory.setInstance(this.faceletFactory);
502         try {
503             f = this.faceletFactory.getFacelet(viewToRender.getViewId());
504         } finally {
505             FaceletFactory.setInstance(null);
506         }
507 
508         // populate UIViewRoot
509         long time = System.currentTimeMillis();
510         f.apply(context, viewToRender);
511         time = System.currentTimeMillis() - time;
512         if (log.isLoggable(Level.FINE)) {
513             log.fine("Took " + time + "ms to build view: "
514                     + viewToRender.getViewId());
515         }
516     }
517 
518     public void renderView(FacesContext context, UIViewRoot viewToRender)
519             throws IOException, FacesException {
520 
521         // lazy initialize so we have a FacesContext to use
522         if (this.faceletFactory == null) {
523             this.initialize(context);
524         }
525 
526         // exit if the view is not to be rendered
527         if (!viewToRender.isRendered()) {
528             return;
529         }
530 
531         // if facelets is not supposed to handle this request
532         if (!handledByFacelets(viewToRender.getViewId())) {
533             this.parent.renderView(context, viewToRender);
534             return;
535         }
536 
537         // log request
538         if (log.isLoggable(Level.FINE)) {
539             log.fine("Rendering View: " + viewToRender.getViewId());
540         }
541 
542         StateWriter stateWriter = null;
543         try {
544             // build view - but not if we're in "buildBeforeRestore"
545             // land and we've already got a populated view. Note
546             // that this optimizations breaks if there's a "c:if" in
547             // the page that toggles as a result of request processing -
548             // should that be handled? Or
549             // is this optimization simply so minor that it should just
550             // be trimmed altogether?
551             if (!this.buildBeforeRestore
552                     || viewToRender.getChildren().isEmpty()) {
553                 this.buildView(context, viewToRender);
554             }
555 
556             // setup writer and assign it to the context
557             ResponseWriter origWriter = this.createResponseWriter(context);
558             // QUESTION: should we use bufferSize? Or, since the
559             // StateWriter usually only needs a small bit at the end,
560             // should we always use a much smaller size?
561             stateWriter = new StateWriter(origWriter,
562                     this.bufferSize != -1 ? this.bufferSize : 1024);
563 
564             ResponseWriter writer = origWriter.cloneWithWriter(stateWriter);
565             context.setResponseWriter(writer);
566 
567             // force creation of session if saving state there
568             StateManager stateMgr = context.getApplication().getStateManager();
569             if (!stateMgr.isSavingStateInClient(context)) {
570                 context.getExternalContext().getSession(true);
571             }
572 
573             long time = System.currentTimeMillis();
574 
575             // render the view to the response
576             writer.startDocument();
577             if (FacesAPI.getVersion() >= 12) {
578                 viewToRender.encodeAll(context);
579             } else {
580                 ComponentSupport.encodeRecursive(context, viewToRender);
581             }
582             writer.endDocument();
583 
584             // finish writing
585             writer.close();
586 
587             // remove transients for older versions
588             if (FacesAPI.getVersion() < 12) {
589                 ComponentSupport.removeTransient(viewToRender);
590             }
591 
592             boolean writtenState = stateWriter.isStateWritten();
593             // flush to origWriter
594             if (writtenState) {
595                 String content = stateWriter.getAndResetBuffer();
596                 int end = content.indexOf(STATE_KEY);
597                 // See if we can find any trace of the saved state.
598                 // If so, we need to perform token replacement
599                 if (end >= 0) {
600                     // save state
601                     Object stateObj = stateMgr.saveSerializedView(context);
602                     String stateStr;
603                     if (stateObj == null) {
604                         stateStr = null;
605                     } else {
606                         stateMgr.writeState(context,
607                                        (StateManager.SerializedView) stateObj);
608                         stateStr = stateWriter.getAndResetBuffer();
609                     }
610 
611                     int start = 0;
612 
613                     while (end != -1) {
614                         origWriter.write(content, start, end - start);
615                         if (stateStr != null) {
616                             origWriter.write(stateStr);
617                         }
618                         start = end + STATE_KEY_LEN;
619                         end = content.indexOf(STATE_KEY, start);
620                     }
621                     origWriter.write(content, start, content.length() - start);
622                 // No trace of any saved state, so we just need to flush
623                 // the buffer
624                 } else {
625                     origWriter.write(content);
626                     // But for JSF 1.1 (actually, just 1.1_01 RI), if we didn't
627                     // detect any saved state, force a call to
628                     // saveSerializedView() in case we're using the old
629                     // pure-server-side state saving
630                     if ((FacesAPI.getVersion() < 12)
631                         && !stateMgr.isSavingStateInClient(context)) {
632                         stateMgr.saveSerializedView(context);
633                     }
634                 }
635             }
636 
637             time = System.currentTimeMillis() - time;
638             if (log.isLoggable(Level.FINE)) {
639                 log.fine("Took " + time + "ms to render view: "
640                         + viewToRender.getViewId());
641             }
642 
643         } catch (FileNotFoundException fnfe) {
644             this.handleFaceletNotFound(context, viewToRender.getViewId());
645         } catch (Exception e) {
646             this.handleRenderException(context, e);
647         } finally {
648             if (stateWriter != null)
649                 stateWriter.release();
650         }
651     }
652 
653     protected void handleRenderException(FacesContext context, Exception e)
654             throws IOException, ELException, FacesException {
655         Object resp = context.getExternalContext().getResponse();
656 
657         // always log
658         if (log.isLoggable(Level.SEVERE)) {
659             UIViewRoot root = context.getViewRoot();
660             StringBuffer sb = new StringBuffer(64);
661             sb.append("Error Rendering View");
662             if (root != null) {
663                 sb.append('[');
664                 sb.append(root.getViewId());
665                 sb.append(']');
666             }
667             log.log(Level.SEVERE, sb.toString(), e);
668         }
669 
670         // handle dev response
671         if (this.developmentMode && !context.getResponseComplete()
672                 && resp instanceof HttpServletResponse) {
673             HttpServletResponse httpResp = (HttpServletResponse) resp;
674             httpResp.reset();
675             httpResp.setContentType("text/html; charset=UTF-8");
676             Writer w = httpResp.getWriter();
677             DevTools.debugHtml(w, context, e);
678             w.flush();
679             context.responseComplete();
680         } else if (e instanceof RuntimeException) {
681             throw (RuntimeException) e;
682         } else if (e instanceof IOException) {
683             throw (IOException) e;
684         } else {
685             throw new FacesException(e.getMessage(), e);
686         }
687     }
688 
689     protected void handleFaceletNotFound(FacesContext context, String viewId)
690             throws FacesException, IOException {
691         String actualId = this.getActionURL(context, viewId);
692         Object respObj = context.getExternalContext().getResponse();
693         if (respObj instanceof HttpServletResponse) {
694             HttpServletResponse respHttp = (HttpServletResponse) respObj;
695             respHttp.sendError(HttpServletResponse.SC_NOT_FOUND, actualId);
696             context.responseComplete();
697         }
698     }
699 
700     /**
701      * Determine if Facelets needs to handle this request.
702      */
703     private boolean handledByFacelets(String viewId) {
704         // If there's no extensions array or prefixes array, then
705         // just make Facelets handle everything
706         if ((extensionsArray == null) && (prefixesArray == null)) {
707             return true;
708         }
709 
710         if (extensionsArray != null) {
711             for (int i = 0; i < extensionsArray.length; i++) {
712                 String extension = extensionsArray[i];
713                 if (viewId.endsWith(extension)) {
714                     return true;
715                 }
716             }
717         }
718 
719         if (prefixesArray != null) {
720             for (int i = 0; i < prefixesArray.length; i++) {
721                 String prefix = prefixesArray[i];
722                 if (viewId.startsWith(prefix)) {
723                     return true;
724                 }
725             }
726         }
727 
728         return false;
729     }
730 
731     public String getDefaultSuffix(FacesContext context) throws FacesException {
732         if (this.defaultSuffix == null) {
733             ExternalContext extCtx = context.getExternalContext();
734             String viewSuffix = extCtx
735                     .getInitParameter(ViewHandler.DEFAULT_SUFFIX_PARAM_NAME);
736             this.defaultSuffix = (viewSuffix != null) ? viewSuffix
737                     : ViewHandler.DEFAULT_SUFFIX;
738         }
739         return this.defaultSuffix;
740     }
741 
742     protected String getRenderedViewId(FacesContext context, String actionId) {
743         ExternalContext extCtx = context.getExternalContext();
744         String viewId = actionId;
745         if (extCtx.getRequestPathInfo() == null) {
746             String facesSuffix = actionId.substring(actionId.lastIndexOf('.'));
747             String viewSuffix = this.getDefaultSuffix(context);
748             viewId = actionId.replaceFirst(facesSuffix, viewSuffix);
749         }
750         if (log.isLoggable(Level.FINE)) {
751             log.fine("ActionId -> ViewId: " + actionId + " -> " + viewId);
752         }
753         return viewId;
754     }
755 
756     public void writeState(FacesContext context) throws IOException {
757         if (handledByFacelets(context.getViewRoot().getViewId())) {
758             // Tell the StateWriter that we're about to write state
759             StateWriter.getCurrentInstance().writingState();
760             // Write the STATE_KEY out.  Unfortunately, this will
761             // be wasteful for pure server-side state managers where nothing
762             // is actually written into the output, but this cannot
763             // programatically be discovered
764             context.getResponseWriter().write(STATE_KEY);
765         } else {
766             this.parent.writeState(context);
767         }
768     }
769 
770     public Locale calculateLocale(FacesContext context) {
771         return this.parent.calculateLocale(context);
772     }
773 
774     public String calculateRenderKitId(FacesContext context) {
775         return this.parent.calculateRenderKitId(context);
776     }
777 
778     public UIViewRoot createView(FacesContext context, String viewId) {
779         if (UIDebug.debugRequest(context)) {
780             return new UIViewRoot();
781         }
782         return this.parent.createView(context, viewId);
783     }
784 
785     public String getActionURL(FacesContext context, String viewId) {
786         return this.parent.getActionURL(context, viewId);
787     }
788 
789     public String getResourceURL(FacesContext context, String path) {
790         return this.parent.getResourceURL(context, path);
791     }
792 
793     /**
794      * Try to guess if this is a postback request. In JSF 1.2, this method is
795      * not needed, since ResponseStateManager can identify postbacks. We use a
796      * simple heuristic: for HttpServletRequests, "POST" and "PUT" are
797      * postbacks. For anything that isn't an HttpServletRequest, just guess that
798      * if there's a request parameter, it's probably a postback.
799      */
800     static private boolean isPostback11(FacesContext context) {
801         Object reqObject = context.getExternalContext().getRequest();
802         if (reqObject instanceof HttpServletRequest) {
803             HttpServletRequest request = (HttpServletRequest) reqObject;
804 
805             String method = request.getMethod();
806 
807             // Is this a POST or PUT request?
808             if ("POST".equals(method) || "PUT".equals(method)) {
809                 return true;
810             }
811 
812             return false;
813         } else {
814             Map paramMap = context.getExternalContext()
815                     .getRequestParameterMap();
816             return !paramMap.isEmpty();
817         }
818     }
819 
820     protected static class NullWriter extends Writer {
821 
822         static final NullWriter Instance = new NullWriter();
823 
824         public void write(char[] buffer) {
825         }
826 
827         public void write(char[] buffer, int off, int len) {
828         }
829 
830         public void write(String str) {
831         }
832 
833         public void write(int c) {
834         }
835 
836         public void write(String str, int off, int len) {
837         }
838 
839         public void close() {
840         }
841 
842         public void flush() {
843         }
844     }
845 }