View Javadoc

1   // ========================================================================
2   // Copyright (c) 2008-2009 Mort Bay Consulting Pty. Ltd.
3   // ------------------------------------------------------------------------
4   // All rights reserved. This program and the accompanying materials
5   // are made available under the terms of the Eclipse Public License v1.0
6   // and Apache License v2.0 which accompanies this distribution.
7   // The Eclipse Public License is available at 
8   // http://www.eclipse.org/legal/epl-v10.html
9   // The Apache License v2.0 is available at
10  // http://www.opensource.org/licenses/apache2.0.php
11  // You may elect to redistribute this code under either of these licenses. 
12  // ========================================================================
13  
14  package org.eclipse.jetty.server;
15  
16  import java.io.IOException;
17  import java.io.OutputStreamWriter;
18  import java.io.Writer;
19  
20  import org.eclipse.jetty.http.AbstractGenerator;
21  import org.eclipse.jetty.util.ByteArrayOutputStream2;
22  import org.eclipse.jetty.util.StringUtil;
23  
24  /** OutputWriter.
25   * A writer that can wrap a {@link HttpOutput} stream and provide
26   * character encodings.
27   *
28   * The UTF-8 encoding is done by this class and no additional 
29   * buffers or Writers are used.
30   * The UTF-8 code was inspired by http://javolution.org
31   */
32  public class HttpWriter extends Writer
33  {
34      private static final int WRITE_CONV = 0;
35      private static final int WRITE_ISO1 = 1;
36      private static final int WRITE_UTF8 = 2;
37      
38      final HttpOutput _out;
39      final AbstractGenerator _generator;
40      int _writeMode;
41      int _surrogate;
42  
43      /* ------------------------------------------------------------ */
44      public HttpWriter(HttpOutput out)
45      {
46          _out=out;
47          _generator=_out._generator;
48           
49      }
50  
51      /* ------------------------------------------------------------ */
52      public void setCharacterEncoding(String encoding)
53      {
54          if (encoding == null || StringUtil.__ISO_8859_1.equalsIgnoreCase(encoding))
55          {
56              _writeMode = WRITE_ISO1;
57          }
58          else if (StringUtil.__UTF8.equalsIgnoreCase(encoding))
59          {
60              _writeMode = WRITE_UTF8;
61          }
62          else
63          {
64              _writeMode = WRITE_CONV;
65              if (_out._characterEncoding == null || !_out._characterEncoding.equalsIgnoreCase(encoding))
66                  _out._converter = null; // Set lazily in getConverter()
67          }
68          
69          _out._characterEncoding = encoding;
70          if (_out._bytes==null)
71              _out._bytes = new ByteArrayOutputStream2(AbstractGenerator.MAX_OUTPUT_CHARS);
72      }
73  
74      /* ------------------------------------------------------------ */
75      public void close() throws IOException
76      {
77          _out.close();
78      }
79  
80      /* ------------------------------------------------------------ */
81      public void flush() throws IOException
82      {
83          _out.flush();
84      }
85  
86      /* ------------------------------------------------------------ */
87      public void write (String s,int offset, int length) throws IOException
88      {   
89          while (length > AbstractGenerator.MAX_OUTPUT_CHARS)
90          {
91              write(s, offset, AbstractGenerator.MAX_OUTPUT_CHARS);
92              offset += AbstractGenerator.MAX_OUTPUT_CHARS;
93              length -= AbstractGenerator.MAX_OUTPUT_CHARS;
94          }
95  
96          if (_out._chars==null)
97          {
98              _out._chars = new char[AbstractGenerator.MAX_OUTPUT_CHARS]; 
99          }
100         char[] chars = _out._chars;
101         s.getChars(offset, offset + length, chars, 0);
102         write(chars, 0, length);
103     }
104 
105     /* ------------------------------------------------------------ */
106     public void write (char[] s,int offset, int length) throws IOException
107     {              
108         HttpOutput out = _out; 
109         
110         while (length > 0)
111         {  
112             out._bytes.reset();
113             int chars = length>AbstractGenerator.MAX_OUTPUT_CHARS?AbstractGenerator.MAX_OUTPUT_CHARS:length;
114 
115             switch (_writeMode)
116             {
117                 case WRITE_CONV:
118                 {
119                     Writer converter=getConverter();
120                     converter.write(s, offset, chars);
121                     converter.flush();
122                 }
123                 break;
124 
125                 case WRITE_ISO1:
126                 {
127                     byte[] buffer=out._bytes.getBuf();
128                     int bytes=out._bytes.getCount();
129                     
130                     if (chars>buffer.length-bytes)
131                         chars=buffer.length-bytes;
132 
133                     for (int i = 0; i < chars; i++)
134                     {
135                         int c = s[offset+i];
136                         buffer[bytes++]=(byte)(c<256?c:'?'); // ISO-1 and UTF-8 match for 0 - 255
137                     }
138                     if (bytes>=0)
139                         out._bytes.setCount(bytes);
140 
141                     break;
142                 }
143 
144                 case WRITE_UTF8:
145                 {
146                     byte[] buffer=out._bytes.getBuf();
147                     int bytes=out._bytes.getCount();
148      
149                     if (bytes+chars>buffer.length)
150                         chars=buffer.length-bytes;
151                     
152                     for (int i = 0; i < chars; i++)
153                     {
154                         int code = s[offset+i];
155 
156                         if ((code & 0xffffff80) == 0) 
157                         {
158                             // 1b
159                             buffer[bytes++]=(byte)(code);
160                         }
161                         else if((code&0xfffff800)==0)
162                         {
163                             // 2b
164                             if (bytes+2>buffer.length)
165                             {
166                                 chars=i;
167                                 break;
168                             }
169                             buffer[bytes++]=(byte)(0xc0|(code>>6));
170                             buffer[bytes++]=(byte)(0x80|(code&0x3f));
171 
172                             if (bytes+chars-i-1>buffer.length)
173                                 chars-=1;
174                         }
175                         else if((code&0xffff0000)==0)
176                         {
177                             // 3b
178                             if (bytes+3>buffer.length)
179                             {
180                                 chars=i;
181                                 break;
182                             }
183                             buffer[bytes++]=(byte)(0xe0|(code>>12));
184                             buffer[bytes++]=(byte)(0x80|((code>>6)&0x3f));
185                             buffer[bytes++]=(byte)(0x80|(code&0x3f));
186 
187                             if (bytes+chars-i-1>buffer.length)
188                                 chars-=2;
189                         }
190                         else if((code&0xff200000)==0)
191                         {
192                             // 4b
193                             if (bytes+4>buffer.length)
194                             {
195                                 chars=i;
196                                 break;
197                             }
198                             buffer[bytes++]=(byte)(0xf0|(code>>18));
199                             buffer[bytes++]=(byte)(0x80|((code>>12)&0x3f));
200                             buffer[bytes++]=(byte)(0x80|((code>>6)&0x3f));
201                             buffer[bytes++]=(byte)(0x80|(code&0x3f));
202 
203                             if (bytes+chars-i-1>buffer.length)
204                                 chars-=3;
205                         }
206                         else if((code&0xf4000000)==0)
207                         {
208                             // 5b
209                             if (bytes+5>buffer.length)
210                             {
211                                 chars=i;
212                                 break;
213                             }
214                             buffer[bytes++]=(byte)(0xf8|(code>>24));
215                             buffer[bytes++]=(byte)(0x80|((code>>18)&0x3f));
216                             buffer[bytes++]=(byte)(0x80|((code>>12)&0x3f));
217                             buffer[bytes++]=(byte)(0x80|((code>>6)&0x3f));
218                             buffer[bytes++]=(byte)(0x80|(code&0x3f));
219 
220                             if (bytes+chars-i-1>buffer.length)
221                                 chars-=4;
222                         }
223                         else if((code&0x80000000)==0)
224                         {
225                             // 6b
226                             if (bytes+6>buffer.length)
227                             {
228                                 chars=i;
229                                 break;
230                             }
231                             buffer[bytes++]=(byte)(0xfc|(code>>30));
232                             buffer[bytes++]=(byte)(0x80|((code>>24)&0x3f));
233                             buffer[bytes++]=(byte)(0x80|((code>>18)&0x3f));
234                             buffer[bytes++]=(byte)(0x80|((code>>12)&0x3f));
235                             buffer[bytes++]=(byte)(0x80|((code>>6)&0x3f));
236                             buffer[bytes++]=(byte)(0x80|(code&0x3f));
237 
238                             if (bytes+chars-i-1>buffer.length)
239                                 chars-=5;
240                         }
241                         else
242                         {
243                             buffer[bytes++]=(byte)('?');
244                         }
245                     }
246                     out._bytes.setCount(bytes);
247                     break;
248                 }
249                 default:
250                     throw new IllegalStateException();
251             }
252             
253             out._bytes.writeTo(out);
254             length-=chars;
255             offset+=chars;
256         }
257     }
258 
259     /* ------------------------------------------------------------ */
260     private Writer getConverter() throws IOException
261     {
262         if (_out._converter == null)
263             _out._converter = new OutputStreamWriter(_out._bytes, _out._characterEncoding);
264         return _out._converter;
265     }   
266 }