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.view.facelets;
20  
21  import org.apache.myfaces.view.facelets.util.FastWriter;
22  
23  import javax.faces.context.FacesContext;
24  import java.io.IOException;
25  import java.io.Writer;
26  
27  /**
28   * A class for handling state insertion. Content is written directly to "out" until an attempt to write state; at that
29   * point, it's redirected into a buffer that can be picked through in theory, this buffer should be very small, since it
30   * only needs to be enough to contain all the content after the close of the first (and, hopefully, only) form.
31   * <p>
32   * Potential optimizations:
33   * <ul>
34   * <li>If we created a new FastWriter at each call to writingState(), and stored a List of them, then we'd know that
35   * state tokens could only possibly be near the start of each buffer (and might not be there at all). (There might be a
36   * close-element before the state token). Then, we'd only need to check the start of the buffer for the state token; if
37   * it's there, write out the real state, then blast the rest of the buffer out. This wouldn't even require toString(),
38   * which for large buffers is expensive. However, this optimization is only going to be especially meaningful for the
39   * multi-form case.</li>
40   * <li>More of a FastWriter optimization than a StateWriter, but: it is far faster to create a set of small 1K buffers
41   * than constantly reallocating one big buffer.</li>
42   * </ul>
43   * 
44   * @author Adam Winer
45   * @version $Id$
46   */
47  public final class StateWriter extends Writer
48  {
49  
50      private static final String CURRENT_WRITER_KEY = "org.apache.myfaces.view.facelets.StateWriter.CURRENT_WRITER";
51  
52      private int initialSize;
53      private Writer out;
54      private FastWriter fast;
55      private boolean writtenState;
56      private boolean writtenStateWithoutWrapper;
57  
58      static public StateWriter getCurrentInstance()
59      {
60          FacesContext facesContext = FacesContext.getCurrentInstance();
61  
62          return (StateWriter)facesContext.getAttributes().get(CURRENT_WRITER_KEY);
63      }
64          
65      static public StateWriter getCurrentInstance(FacesContext facesContext)
66      {
67          return (StateWriter)facesContext.getAttributes().get(CURRENT_WRITER_KEY);
68      }
69  
70      private static void setCurrentInstance(StateWriter stateWriter)
71      {
72          FacesContext facesContext = FacesContext.getCurrentInstance();
73  
74          if (stateWriter == null)
75          {
76              facesContext.getAttributes().remove(CURRENT_WRITER_KEY);
77          }
78          else
79          {
80              facesContext.getAttributes().put(CURRENT_WRITER_KEY, stateWriter);
81          }
82      }
83      
84      private static void setCurrentInstance(StateWriter stateWriter, FacesContext facesContext)
85      {
86          //FacesContext facesContext = FacesContext.getCurrentInstance();
87  
88          if (stateWriter == null)
89          {
90              facesContext.getAttributes().remove(CURRENT_WRITER_KEY);
91          }
92          else
93          {
94              facesContext.getAttributes().put(CURRENT_WRITER_KEY, stateWriter);
95          }
96      }
97  
98      public StateWriter(Writer initialOut, int initialSize)
99      {
100         if (initialSize < 0)
101         {
102             throw new IllegalArgumentException("Initial Size cannot be less than 0");
103         }
104 
105         this.initialSize = initialSize;
106         this.out = initialOut;
107         setCurrentInstance(this);
108     }
109     
110     public StateWriter(Writer initialOut, int initialSize, FacesContext facesContext)
111     {
112         if (initialSize < 0)
113         {
114             throw new IllegalArgumentException("Initial Size cannot be less than 0");
115         }
116 
117         this.initialSize = initialSize;
118         this.out = initialOut;
119         setCurrentInstance(this, facesContext);
120     }
121 
122     /**
123      * Mark that state is about to be written. Contrary to what you'd expect, we cannot and should not assume that this
124      * location is really going to have state; it is perfectly legit to have a ResponseWriter that filters out content,
125      * and ignores an attempt to write out state at this point. So, we have to check after the fact to see if there
126      * really are state markers.
127      */
128     public void writingState()
129     {
130         if (!this.writtenState)
131         {
132             this.writtenState = true;
133             this.writtenStateWithoutWrapper = false;
134             this.fast = new FastWriter(this.initialSize);
135             this.out = this.fast;
136         }
137     }
138     
139     public boolean isStateWritten()
140     {
141         return this.writtenState;
142     }
143 
144     public void writingStateWithoutWrapper()
145     {
146         if (!this.writtenState && !this.writtenStateWithoutWrapper)
147         {
148             this.writtenStateWithoutWrapper = true;
149         }
150     }    
151 
152     public boolean isStateWrittenWithoutWrapper()
153     {
154         return this.writtenStateWithoutWrapper;
155     }
156 
157     public void close() throws IOException
158     {
159         // do nothing
160     }
161 
162     public void flush() throws IOException
163     {
164         if (!this.writtenState)
165         {
166             this.out.flush();
167         }
168     }
169 
170     public void write(char[] cbuf, int off, int len) throws IOException
171     {
172         this.out.write(cbuf, off, len);
173     }
174 
175     public void write(char[] cbuf) throws IOException
176     {
177         this.out.write(cbuf);
178     }
179 
180     public void write(int c) throws IOException
181     {
182         this.out.write(c);
183     }
184 
185     public void write(String str, int off, int len) throws IOException
186     {
187         this.out.write(str, off, len);
188     }
189 
190     public void write(String str) throws IOException
191     {
192         this.out.write(str);
193     }
194 
195     public String getAndResetBuffer()
196     {
197         if (!this.writtenState)
198         {
199             throw new IllegalStateException("Did not write state;  no buffer is available");
200         }
201 
202         String result = this.fast.toString();
203         this.fast.reset();
204         return result;
205     }
206 
207     public void release()
208     {
209         // remove from FacesContext attribute Map
210         setCurrentInstance(null);
211     }
212     
213     public void release(FacesContext facesContext)
214     {
215         // remove from FacesContext attribute Map
216         setCurrentInstance(null, facesContext);
217     }
218 
219 }