##### Coverage Report - org.webmacro.FastWriter

Classes in this File | Line Coverage | Branch Coverage | Complexity
FastWriter |
55% | 64/116
42% | 11/26
| 1.8846153846153846;1.885

1 |  |
     /*
2 |  |
      * Copyright (C) 1998-2000 Semiotek Inc.  All Rights Reserved.
3 |  |
      *
4 |  |
      * Redistribution and use in source and binary forms, with or without
5 |  |
      * modification, are permitted under the terms of either of the following
6 |  |
      * Open Source licenses:
7 |  |
      *
8 |  |
      * The GNU General Public License, version 2, or any later version, as
9 |  |
      * published by the Free Software Foundation
10 |  |
      * (http://www.fsf.org/copyleft/gpl.html);
11 |  |
      *
12 |  |
      *  or
13 |  |
      *
14 |  |
      * The Semiotek Public License (http://webmacro.org/LICENSE.)
15 |  |
      *
16 |  |
      * This software is provided "as is", with NO WARRANTY, not even the
17 |  |
      * implied warranties of fitness to purpose, or merchantability. You
18 |  |
      * assume all risks and liabilities associated with its use.
19 |  |
      *
20 |  |
      * See www.webmacro.org for more information on the WebMacro project.
21 |  |
      */
22 |  |
23 |  |
24 |  |
     package org.webmacro;
25 |  |
26 |  |
     import java.io.IOException;
27 |  |
     import java.io.OutputStream;
28 |  |
     import java.io.OutputStreamWriter;
29 |  |
     import java.io.UnsupportedEncodingException;
30 |  |
     import java.io.Writer;
31 |  |
32 |  |
     import org.webmacro.util.ByteBufferOutputStream;
33 |  |
     import org.webmacro.util.Encoder;
34 |  |
     import org.webmacro.util.EncoderProvider;
35 |  |
36 |  |
37 |  |
     /**
38 |  |
      * FastWriter attempts to optimize output speed in a WebMacro template
39 |  |
      * through several specific optimizations:
40 |  |
      * <ul>
41 |  |
      *   <li> FastWriter caches the output in a byte array until you
42 |  |
      *        call reset(). You can access the output by one of several
43 |  |
      *        methods: toString(), toByteArray(), or writeTo(OutputStream)
44 |  |
      *   <li> you can use a unicode conversion cache by calling writeStatic()
45 |  |
      *   <li> you can get the contents written to the FastWriter back
46 |  |
      *        as an array of bytes INSTEAD of writing to the output stream
47 |  |
      * </ul>
48 |  |
      * <p>
49 |  |
      * <b>Note that the FastWriter requires an explicit flush</b>
50 |  |
      * <p>
51 |  |
      *
52 |  |
      * @author Marcel Huijkman
53 |  |
      *
54 |  |
      * @version        27-07-2002
55 |  |
      */
56 |  |
57 |  |
     public class FastWriter extends Writer
58 |  |
     {
59 |  |
60 |  |
         /**
61 |  |
          * This encoding is either UTF16-BE or, if the platform does not
62 |  |
          * support it, UTF8. It is a Unicode encoding which can have
63 |  |
          * encoded strings concatenated together.
64 |  |
          */
65 |  |
         public static final String SAFE_UNICODE_ENCODING;
66 |  |
67 |  |
         // find the safe encoding
68 |  |
         static
69 |  |
         {
70 | 2 |
             String encoding = "UTF16-BE";
71 |  |
             try
72 |  |
             {
73 | 2 |
                 encoding.getBytes(encoding);
74 |  |
             }
75 | 2 |
             catch (Exception e)
76 |  |
             {
77 | 2 |
                 encoding = "UTF8";
78 | 0 |
             }
79 | 2 |
             SAFE_UNICODE_ENCODING = encoding;
80 | 2 |
         }
81 |  |
82 |  |
83 |  |
         private final int DEFAULT_BUFFER_SIZE;
84 |  |
         private final String _encoding;      // what encoding we use
85 |  |
         private final Writer _bwriter;
86 |  |
         private final ByteBufferOutputStream _bstream;
87 |  |
         private final Encoder _encoder;
88 |  |
89 |  |
         private OutputStream _out;
90 |  |
91 | 1734 |
         private char[] _cbuf = new char[512];
92 |  |
         private boolean _buffered;
93 |  |
94 |  |
         /**
95 |  |
          * Create a FastWriter to the target outputstream. You must specify
96 |  |
          * a character encoding. You can also call writeTo(), toString(),
97 |  |
          * and toByteArray() to access any un-flush()ed contents.
98 |  |
          */
99 |  |
         public FastWriter (Broker broker, OutputStream out, String encoding)
100 |  |
                 throws UnsupportedEncodingException
101 | 1734 |
         {
102 | 1734 |
             DEFAULT_BUFFER_SIZE = broker.getSettings().getIntegerSetting("FastWriter.DefaultBufferSize", 4096);
103 | 1734 |
             _encoding = hackEncoding(encoding);
104 | 1734 |
             _bstream = new ByteBufferOutputStream(DEFAULT_BUFFER_SIZE);
105 | 1734 |
             _bwriter = new OutputStreamWriter(_bstream, _encoding);
106 |  |
107 |  |
             // fetch our encoder from the broker
108 |  |
             try
109 |  |
             {
110 | 1734 |
                 _encoder = (Encoder) broker.get(EncoderProvider.TYPE, _encoding);
111 |  |
             }
112 | 0 |
             catch (ResourceException re)
113 |  |
             {
114 | 0 |
                 throw new UnsupportedEncodingException(re.getMessage());
115 | 1734 |
             }
116 |  |
117 | 1734 |
             _buffered = false;
118 |  |
119 | 1734 |
             _out = out;
120 | 1734 |
         }
121 |  |
122 |  |
         /**
123 |  |
          * Workaround for problems with resin-2.0.3, which
124 |  |
          * gives CPxxxx as a character encoding, but java
125 |  |
          * knows only Cpxxxx. This method converts encoding
126 |  |
          * to a form understood by java.
127 |  |
          * <br>
128 |  |
          * We should remove it some time after resin
129 |  |
          * has been fixed
130 |  |
          */
131 |  |
         private static String hackEncoding (String encoding)
132 |  |
         {
133 | 1734 |
             if (encoding.toLowerCase().startsWith("cp") &&
134 |  |
                     !encoding.startsWith("Cp"))
135 |  |
             {
136 | 0 |
                 encoding = "Cp".concat(encoding.substring(2));
137 |  |
             }
138 | 1734 |
             return encoding;
139 |  |
         }
140 |  |
141 |  |
         /**
142 |  |
          * Create a new FastWriter with no output stream target. You can
143 |  |
          * still call writeTo(), toString(), and toByteArray().
144 |  |
          */
145 |  |
         public FastWriter (Broker broker, String encoding)
146 |  |
                 throws java.io.UnsupportedEncodingException
147 |  |
         {
148 | 0 |
             this(broker, null, encoding);
149 | 0 |
         }
150 |  |
151 |  |
152 |  |
         /**
153 |  |
          * Get the character encoding this FastWriter uses to convert
154 |  |
          * characters to byte[]
155 |  |
          */
156 |  |
         public String getEncoding ()
157 |  |
         {
158 | 0 |
             return _encoding;
159 |  |
         }
160 |  |
161 |  |
         /**
162 |  |
          * Get the encoder used by this FastWriter to transform
163 |  |
          * char[] data into byte[] data.
164 |  |
          */
165 |  |
         public Encoder getEncoder ()
166 |  |
         {
167 | 56066 |
             return _encoder;
168 |  |
         }
169 |  |
170 |  |
         /**
171 |  |
          * Get the output stream this FastWriter sends output to. It
172 |  |
          * may be null, in which case output is not sent anywhere.
173 |  |
          */
174 |  |
         public OutputStream getOutputStream ()
175 |  |
         {
176 | 16 |
             return _out;
177 |  |
         }
178 |  |
179 |  |
         /**
180 |  |
          * Write characters to the output stream performing slow unicode
181 |  |
          * conversion unless AsciiHack is on.
182 |  |
          */
183 |  |
         public void write (char[] cbuf) throws java.io.IOException
184 |  |
         {
185 | 0 |
             _bwriter.write(cbuf, 0, cbuf.length);
186 | 0 |
             _buffered = true;
187 | 0 |
         }
188 |  |
189 |  |
         /**
190 |  |
          * Write characters to to the output stream performing slow unicode
191 |  |
          * conversion.
192 |  |
          */
193 |  |
         public void write (char[] cbuf, int offset, int len) throws java.io.IOException
194 |  |
         {
195 | 0 |
             _bwriter.write(cbuf, offset, len);
196 | 0 |
             _buffered = true;
197 | 0 |
         }
198 |  |
199 |  |
         /**
200 |  |
          * Write a single character, performing slow unicode conversion
201 |  |
          */
202 |  |
         public void write (int c) throws java.io.IOException
203 |  |
         {
204 | 0 |
             _bwriter.write(c);
205 | 0 |
             _buffered = true;
206 | 0 |
         }
207 |  |
208 |  |
         /**
209 |  |
          * Write a string to the underlying output stream, performing
210 |  |
          * unicode conversion.
211 |  |
          */
212 |  |
         public void write (final String s) throws java.io.IOException
213 |  |
         {
214 | 64116 |
             final int len = s.length();
215 |  |
             try
216 |  |
             {
217 | 64116 |
                 s.getChars(0, len, _cbuf, 0);
218 |  |
             }
219 | 72 |
             catch (IndexOutOfBoundsException e)
220 |  |
             {
221 | 72 |
                 _cbuf = new char[len + (len - _cbuf.length)];
222 | 72 |
                 s.getChars(0, len, _cbuf, 0);
223 | 64044 |
             }
224 |  |
225 | 64116 |
             _bwriter.write(_cbuf, 0, len);
226 | 64116 |
             _buffered = true;
227 | 64116 |
         }
228 |  |
229 |  |
         /*
230 |  |
     * Write a string to the underlying output stream, performing
231 |  |
     * unicode conversion.
232 |  |
     */
233 |  |
         public void write (final String s, final int off, final int len) throws java.io.IOException
234 |  |
         {
235 |  |
             try
236 |  |
             {
237 | 152 |
                 s.getChars(off, off + len, _cbuf, 0);
238 |  |
             }
239 | 0 |
             catch (IndexOutOfBoundsException e)
240 |  |
             {
241 | 0 |
                 _cbuf = new char[len + (len - _cbuf.length)];
242 | 0 |
                 s.getChars(off, off + len, _cbuf, 0);
243 | 152 |
             }
244 |  |
245 | 152 |
             _bwriter.write(_cbuf, 0, len);
246 | 152 |
             _buffered = true;
247 | 152 |
         }
248 |  |
249 |  |
         /**
250 |  |
          * Write a string to the underlying output stream, performing
251 |  |
          * unicode conversion if necessary--try and read the encoding
252 |  |
          * from an encoding cache if possible.
253 |  |
          */
254 |  |
         public void writeStatic (final String s)
255 |  |
         {
256 | 0 |
             if (_buffered)
257 |  |
             {
258 | 0 |
                 bflush();
259 |  |
             }
260 |  |
             try
261 |  |
             {
262 | 0 |
                 byte[] b = _encoder.encode(s);
263 | 0 |
                 _bstream.write(b, 0, b.length);
264 |  |
             }
265 | 0 |
             catch (UnsupportedEncodingException uee)
266 |  |
             {
267 |  |
                 // this should never happen
268 | 0 |
                 uee.printStackTrace();
269 | 0 |
             }
270 | 0 |
         }
271 |  |
272 |  |
         /**
273 |  |
          * Write raw bytes to the underlying stream. These bytes must be
274 |  |
          * properly encoded with the encoding returned by getEncoding().
275 |  |
          */
276 |  |
         public void write (byte[] rawBytes)
277 |  |
         {
278 | 0 |
             if (_buffered)
279 |  |
             {
280 | 0 |
                 bflush();
281 |  |
             }
282 | 0 |
             _bstream.write(rawBytes);
283 | 0 |
         }
284 |  |
285 |  |
         /**
286 |  |
          * Write raw bytes to the underlying stream. Tehse bytes must be
287 |  |
          * properly encoded witht he encoding returned by getEncoding()
288 |  |
          */
289 |  |
         public void write (byte[] rawBytes, int offset, int len)
290 |  |
         {
291 | 171018 |
             if (_buffered)
292 |  |
             {
293 | 63962 |
                 bflush();
294 |  |
             }
295 | 171018 |
             _bstream.write(rawBytes, offset, len);
296 | 171018 |
         }
297 |  |
298 |  |
         private void bflush ()
299 |  |
         {
300 |  |
             try
301 |  |
             {
302 | 63968 |
                 _bwriter.flush();
303 | 63968 |
                 _buffered = false;
304 |  |
             }
305 | 0 |
             catch (IOException e)
306 |  |
             {
307 | 0 |
                 e.printStackTrace();
308 | 63968 |
             }
309 | 63968 |
         }
310 |  |
311 |  |
312 |  |
         /**
313 |  |
          * Flush all data out to the OutputStream, if any, clearing
314 |  |
          * the internal buffers. Note that data is ONLY written to
315 |  |
          * the output stream on a flush() operation, and never at
316 |  |
          * any other time. Consequently this is one of the few places
317 |  |
          * that you may actually encounter an IOException when using
318 |  |
          * the FastWriter class.
319 |  |
          */
320 |  |
         public void flush () throws IOException
321 |  |
         {
322 | 1802 |
             if (_buffered)
323 |  |
             {
324 | 6 |
                 bflush();
325 |  |
             }
326 |  |
327 | 1802 |
             if (_out != null)
328 |  |
             {
329 | 1150 |
                 writeTo(_out);
330 | 1136 |
                 _out.flush();
331 |  |
             }
332 | 1788 |
             _bstream.reset();
333 | 1788 |
         }
334 |  |
335 |  |
         /**
336 |  |
          * Return the number of bytes that would be written out if flush()
337 |  |
          * is called.
338 |  |
          */
339 |  |
         public int size () throws IOException
340 |  |
         {
341 | 0 |
             if (_buffered)
342 |  |
             {
343 | 0 |
                 bflush();
344 |  |
             }
345 |  |
346 | 0 |
             return _bstream.size();
347 |  |
         }
348 |  |
349 |  |
         /**
350 |  |
          * Copy the contents written so far into a byte array.
351 |  |
          */
352 |  |
         public byte[] toByteArray ()
353 |  |
         {
354 | 0 |
             if (_buffered)
355 |  |
             {
356 | 0 |
                 bflush();
357 |  |
             }
358 | 0 |
             return _bstream.getBytes();
359 |  |
         }
360 |  |
361 |  |
         /**
362 |  |
          * Copy the contents written so far into a String.
363 |  |
          */
364 |  |
         public String toString ()
365 |  |
         {
366 | 642 |
             if (_buffered)
367 |  |
             {
368 | 0 |
                 bflush();
369 |  |
             }
370 |  |
             try
371 |  |
             {
372 | 642 |
                 return _bstream.toString(_encoding);
373 |  |
             }
374 | 0 |
             catch (UnsupportedEncodingException e)
375 |  |
             {
376 | 0 |
                 e.printStackTrace(); // never happen: we already used it
377 | 0 |
                 return null;
378 |  |
             }
379 |  |
         }
380 |  |
381 |  |
         /**
382 |  |
          * Copy the contents written so far to the suppiled output stream
383 |  |
          */
384 |  |
         public void writeTo (OutputStream out) throws IOException
385 |  |
         {
386 | 1150 |
             if (_buffered)
387 |  |
             {
388 | 0 |
                 bflush();
389 |  |
             }
390 | 1150 |
             _bstream.writeTo(out);
391 | 1136 |
         }
392 |  |
393 |  |
         /**
394 |  |
          * Reset the fastwriter, clearing any contents that have
395 |  |
          * been generated so far.
396 |  |
          */
397 |  |
         public void reset (OutputStream out)
398 |  |
         {
399 | 0 |
             if (_buffered)
400 |  |
             {
401 | 0 |
                 bflush();
402 |  |
             }
403 | 0 |
             _bstream.reset();
404 | 0 |
             _out = out;
405 | 0 |
         }
406 |  |
407 |  |
         /**
408 |  |
          * Get a new FastWriter. You must then call writeTo(..) before
409 |  |
          * attempting to write to the FastWriter.
410 |  |
          */
411 |  |
         public static FastWriter getInstance (Broker broker, OutputStream out,
412 |  |
                                               String encoding)
413 |  |
                 throws UnsupportedEncodingException
414 |  |
         {
415 | 1718 |
             return new FastWriter(broker, out, encoding);
416 |  |
         }
417 |  |
418 |  |
         /**
419 |  |
          * Get a new FastWriter. You must then call writeTo(..) before
420 |  |
          * attempting to write to the FastWriter.
421 |  |
          */
422 |  |
         public static FastWriter getInstance (Broker broker, OutputStream out)
423 |  |
                 throws UnsupportedEncodingException
424 |  |
         {
425 | 16 |
             return getInstance(broker, out, SAFE_UNICODE_ENCODING);
426 |  |
         }
427 |  |
428 |  |
         /**
429 |  |
          * Return a FastWriter with the specified encoding and no output stream.
430 |  |
          */
431 |  |
         public static FastWriter getInstance (Broker broker, String encoding)
432 |  |
                 throws UnsupportedEncodingException
433 |  |
         {
434 | 626 |
             return getInstance(broker, null, encoding);
435 |  |
         }
436 |  |
437 |  |
         /**
438 |  |
          * Return a FastWriter with default encoding and no output stream.
439 |  |
          */
440 |  |
         public static FastWriter getInstance (Broker broker)
441 |  |
         {
442 |  |
             try
443 |  |
             {
444 | 30 |
                 return getInstance(broker, null, SAFE_UNICODE_ENCODING);
445 |  |
             }
446 | 0 |
             catch (UnsupportedEncodingException e)
447 |  |
             {
448 | 0 |
                 e.printStackTrace(); // never gonna happen
449 | 0 |
                 return null;
450 |  |
             }
451 |  |
         }
452 |  |
453 |  |
         public void close () throws IOException
454 |  |
         {
455 | 1722 |
             flush();
456 | 1708 |
             if (_out != null)
457 |  |
             {
458 | 1062 |
                 _out.close();
459 | 1062 |
                 _out = null;
460 |  |
             }
461 | 1708 |
         }
462 |  |
     }
463 |  |