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           
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                         if ((code & 0xffffff80) == 0) 
163                         {
164                             // 1b
165                             if (bytes>=buffer.length)
166                             {
167                                 chars=i;
168                                 break;
169                             }
170                             buffer[bytes++]=(byte)(code);
171                         }
172                         else
173                         {
174                             if((code&0xfffff800)==0)
175                             {
176                                 // 2b
177                                 if (bytes+2>buffer.length)
178                                 {
179                                     chars=i;
180                                     break;
181                                 }
182                                 buffer[bytes++]=(byte)(0xc0|(code>>6));
183                                 buffer[bytes++]=(byte)(0x80|(code&0x3f));
184                             }
185                             else if((code&0xffff0000)==0)
186                             {
187                                 // 3b
188                                 if (bytes+3>buffer.length)
189                                 {
190                                     chars=i;
191                                     break;
192                                 }
193                                 buffer[bytes++]=(byte)(0xe0|(code>>12));
194                                 buffer[bytes++]=(byte)(0x80|((code>>6)&0x3f));
195                                 buffer[bytes++]=(byte)(0x80|(code&0x3f));
196                             }
197                             else if((code&0xff200000)==0)
198                             {
199                                 // 4b
200                                 if (bytes+4>buffer.length)
201                                 {
202                                     chars=i;
203                                     break;
204                                 }
205                                 buffer[bytes++]=(byte)(0xf0|(code>>18));
206                                 buffer[bytes++]=(byte)(0x80|((code>>12)&0x3f));
207                                 buffer[bytes++]=(byte)(0x80|((code>>6)&0x3f));
208                                 buffer[bytes++]=(byte)(0x80|(code&0x3f));
209                             }
210                             else if((code&0xf4000000)==0)
211                             {
212                                 // 5b
213                                 if (bytes+5>buffer.length)
214                                 {
215                                     chars=i;
216                                     break;
217                                 }
218                                 buffer[bytes++]=(byte)(0xf8|(code>>24));
219                                 buffer[bytes++]=(byte)(0x80|((code>>18)&0x3f));
220                                 buffer[bytes++]=(byte)(0x80|((code>>12)&0x3f));
221                                 buffer[bytes++]=(byte)(0x80|((code>>6)&0x3f));
222                                 buffer[bytes++]=(byte)(0x80|(code&0x3f));
223                             }
224                             else if((code&0x80000000)==0)
225                             {
226                                 // 6b
227                                 if (bytes+6>buffer.length)
228                                 {
229                                     chars=i;
230                                     break;
231                                 }
232                                 buffer[bytes++]=(byte)(0xfc|(code>>30));
233                                 buffer[bytes++]=(byte)(0x80|((code>>24)&0x3f));
234                                 buffer[bytes++]=(byte)(0x80|((code>>18)&0x3f));
235                                 buffer[bytes++]=(byte)(0x80|((code>>12)&0x3f));
236                                 buffer[bytes++]=(byte)(0x80|((code>>6)&0x3f));
237                                 buffer[bytes++]=(byte)(0x80|(code&0x3f));
238                             }
239                             else
240                             {
241                                 buffer[bytes++]=(byte)('?');
242                             }
243 
244                             if (bytes==buffer.length)
245                             {
246                                 chars=i+1;
247                                 break;
248                             }
249                         }
250                     }
251                     out._bytes.setCount(bytes);
252                     break;
253                 }
254                 default:
255                     throw new IllegalStateException();
256             }
257             
258             out._bytes.writeTo(out);
259             length-=chars;
260             offset+=chars;
261         }
262     }
263 
264     /* ------------------------------------------------------------ */
265     private Writer getConverter() throws IOException
266     {
267         if (_out._converter == null)
268             _out._converter = new OutputStreamWriter(_out._bytes, _out._characterEncoding);
269         return _out._converter;
270     }   
271 }