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      public static final int MAX_OUTPUT_CHARS = 512; 
35      
36      private static final int WRITE_CONV = 0;
37      private static final int WRITE_ISO1 = 1;
38      private static final int WRITE_UTF8 = 2;
39      
40      final HttpOutput _out;
41      final AbstractGenerator _generator;
42      int _writeMode;
43      int _surrogate;
44  
45      /* ------------------------------------------------------------ */
46      public HttpWriter(HttpOutput out)
47      {
48          _out=out;
49          _generator=_out._generator;
50          _surrogate=0; // AS lastUTF16CodePoint
51      }
52  
53      /* ------------------------------------------------------------ */
54      public void setCharacterEncoding(String encoding)
55      {
56          if (encoding == null || StringUtil.__ISO_8859_1.equalsIgnoreCase(encoding))
57          {
58              _writeMode = WRITE_ISO1;
59          }
60          else if (StringUtil.__UTF8.equalsIgnoreCase(encoding))
61          {
62              _writeMode = WRITE_UTF8;
63          }
64          else
65          {
66              _writeMode = WRITE_CONV;
67              if (_out._characterEncoding == null || !_out._characterEncoding.equalsIgnoreCase(encoding))
68                  _out._converter = null; // Set lazily in getConverter()
69          }
70          
71          _out._characterEncoding = encoding;
72          if (_out._bytes==null)
73              _out._bytes = new ByteArrayOutputStream2(MAX_OUTPUT_CHARS);
74      }
75  
76      /* ------------------------------------------------------------ */
77      @Override
78      public void close() throws IOException
79      {
80          _out.close();
81      }
82  
83      /* ------------------------------------------------------------ */
84      @Override
85      public void flush() throws IOException
86      {
87          _out.flush();
88      }
89  
90      /* ------------------------------------------------------------ */
91      @Override
92      public void write (String s,int offset, int length) throws IOException
93      {   
94          while (length > MAX_OUTPUT_CHARS)
95          {
96              write(s, offset, MAX_OUTPUT_CHARS);
97              offset += MAX_OUTPUT_CHARS;
98              length -= MAX_OUTPUT_CHARS;
99          }
100 
101         if (_out._chars==null)
102         {
103             _out._chars = new char[MAX_OUTPUT_CHARS]; 
104         }
105         char[] chars = _out._chars;
106         s.getChars(offset, offset + length, chars, 0);
107         write(chars, 0, length);
108     }
109 
110     /* ------------------------------------------------------------ */
111     @Override
112     public void write (char[] s,int offset, int length) throws IOException
113     {              
114         HttpOutput out = _out; 
115         
116         while (length > 0)
117         {  
118             out._bytes.reset();
119             int chars = length>MAX_OUTPUT_CHARS?MAX_OUTPUT_CHARS:length;
120             
121             switch (_writeMode)
122             {
123                 case WRITE_CONV:
124                 {
125                     Writer converter=getConverter();
126                     converter.write(s, offset, chars);
127                     converter.flush();
128                 }
129                 break;
130 
131                 case WRITE_ISO1:
132                 {
133                     byte[] buffer=out._bytes.getBuf();
134                     int bytes=out._bytes.getCount();
135                     
136                     if (chars>buffer.length-bytes)
137                         chars=buffer.length-bytes;
138 
139                     for (int i = 0; i < chars; i++)
140                     {
141                         int c = s[offset+i];
142                         buffer[bytes++]=(byte)(c<256?c:'?'); // ISO-1 and UTF-8 match for 0 - 255
143                     }
144                     if (bytes>=0)
145                         out._bytes.setCount(bytes);
146 
147                     break;
148                 }
149 
150                 case WRITE_UTF8:
151                 {
152                     byte[] buffer=out._bytes.getBuf();
153                     int bytes=out._bytes.getCount();
154 
155                     if (bytes+chars>buffer.length)
156                         chars=buffer.length-bytes;
157 
158                     for (int i = 0; i < chars; i++)
159                     {
160                         int code = s[offset+i];
161 
162                         // Do we already have a surrogate?
163                         if(_surrogate==0)
164                         {
165                             // No - is this char code a surrogate?
166                             if(Character.isHighSurrogate((char)code))
167                             {
168                                 _surrogate=code; // UCS-?
169                                 continue;
170                             }                            
171                         }
172                         // else handle a low surrogate
173                         else if(Character.isLowSurrogate((char)code))
174                         {
175                             code = Character.toCodePoint((char)_surrogate, (char)code); // UCS-4
176                             _surrogate=0; // USED
177                         }
178                         // else UCS-2
179                         else
180                         {
181                             code=_surrogate; // UCS-2
182                             _surrogate=0; // USED
183                             i--;
184                         }
185 
186                         if ((code & 0xffffff80) == 0) 
187                         {
188                             // 1b
189                             if (bytes>=buffer.length)
190                             {
191                                 chars=i;
192                                 break;
193                             }
194                             buffer[bytes++]=(byte)(code);
195                         }
196                         else
197                         {
198                             if((code&0xfffff800)==0)
199                             {
200                                 // 2b
201                                 if (bytes+2>buffer.length)
202                                 {
203                                     chars=i;
204                                     break;
205                                 }
206                                 buffer[bytes++]=(byte)(0xc0|(code>>6));
207                                 buffer[bytes++]=(byte)(0x80|(code&0x3f));
208                             }
209                             else if((code&0xffff0000)==0)
210                             {
211                                 // 3b
212                                 if (bytes+3>buffer.length)
213                                 {
214                                     chars=i;
215                                     break;
216                                 }
217                                 buffer[bytes++]=(byte)(0xe0|(code>>12));
218                                 buffer[bytes++]=(byte)(0x80|((code>>6)&0x3f));
219                                 buffer[bytes++]=(byte)(0x80|(code&0x3f));
220                             }
221                             else if((code&0xff200000)==0)
222                             {
223                                 // 4b
224                                 if (bytes+4>buffer.length)
225                                 {
226                                     chars=i;
227                                     break;
228                                 }
229                                 buffer[bytes++]=(byte)(0xf0|(code>>18));
230                                 buffer[bytes++]=(byte)(0x80|((code>>12)&0x3f));
231                                 buffer[bytes++]=(byte)(0x80|((code>>6)&0x3f));
232                                 buffer[bytes++]=(byte)(0x80|(code&0x3f));
233                             }
234                             else if((code&0xf4000000)==0)
235                             {
236                                 // 5b
237                                 if (bytes+5>buffer.length)
238                                 {
239                                     chars=i;
240                                     break;
241                                 }
242                                 buffer[bytes++]=(byte)(0xf8|(code>>24));
243                                 buffer[bytes++]=(byte)(0x80|((code>>18)&0x3f));
244                                 buffer[bytes++]=(byte)(0x80|((code>>12)&0x3f));
245                                 buffer[bytes++]=(byte)(0x80|((code>>6)&0x3f));
246                                 buffer[bytes++]=(byte)(0x80|(code&0x3f));
247                             }
248                             else if((code&0x80000000)==0)
249                             {
250                                 // 6b
251                                 if (bytes+6>buffer.length)
252                                 {
253                                     chars=i;
254                                     break;
255                                 }
256                                 buffer[bytes++]=(byte)(0xfc|(code>>30));
257                                 buffer[bytes++]=(byte)(0x80|((code>>24)&0x3f));
258                                 buffer[bytes++]=(byte)(0x80|((code>>18)&0x3f));
259                                 buffer[bytes++]=(byte)(0x80|((code>>12)&0x3f));
260                                 buffer[bytes++]=(byte)(0x80|((code>>6)&0x3f));
261                                 buffer[bytes++]=(byte)(0x80|(code&0x3f));
262                             }
263                             else
264                             {
265                                 buffer[bytes++]=(byte)('?');
266                             } 
267                             
268 
269                             if (bytes==buffer.length)
270                             {
271                                 chars=i+1;
272                                 break;
273                             }
274                         }
275                     }
276                     out._bytes.setCount(bytes);
277                     break;
278                 }
279                 default:
280                     throw new IllegalStateException();
281             }
282             
283             out._bytes.writeTo(out);
284             length-=chars;
285             offset+=chars;
286         }
287     }
288     
289     
290     /* ------------------------------------------------------------ */
291     private Writer getConverter() throws IOException
292     {
293         if (_out._converter == null)
294             _out._converter = new OutputStreamWriter(_out._bytes, _out._characterEncoding);
295         return _out._converter;
296     }   
297 }