Coverage Report - org.webmacro.FastWriter
 
Classes in this File Line Coverage Branch Coverage Complexity
FastWriter
50%
59/116
46%
12/26
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  38
         String encoding = "UTF16-BE";
 71  
         try
 72  
         {
 73  38
             encoding.getBytes(encoding);
 74  
         }
 75  38
         catch (Exception e)
 76  
         {
 77  38
             encoding = "UTF8";
 78  0
         }
 79  38
         SAFE_UNICODE_ENCODING = encoding;
 80  38
     }
 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  1746
     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  1746
     {
 102  1746
         DEFAULT_BUFFER_SIZE = broker.getSettings().getIntegerSetting("FastWriter.DefaultBufferSize", 4096);
 103  1746
         _encoding = hackEncoding(encoding);
 104  1746
         _bstream = new ByteBufferOutputStream(DEFAULT_BUFFER_SIZE);
 105  1746
         _bwriter = new OutputStreamWriter(_bstream, _encoding);
 106  
 
 107  
         // fetch our encoder from the broker
 108  
         try
 109  
         {
 110  1746
             _encoder = (Encoder) broker.get(EncoderProvider.TYPE, _encoding);
 111  
         }
 112  0
         catch (ResourceException re)
 113  
         {
 114  0
             throw new UnsupportedEncodingException(re.getMessage());
 115  1746
         }
 116  
 
 117  1746
         _buffered = false;
 118  
 
 119  1746
         _out = out;
 120  1746
     }
 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  1746
         if (encoding.toLowerCase().startsWith("cp") &&
 134  
                 !encoding.startsWith("Cp"))
 135  
         {
 136  0
             encoding = "Cp".concat(encoding.substring(2));
 137  
         }
 138  1746
         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  6943
         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  0
         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  1982
         final int len = s.length();
 215  
         try
 216  
         {
 217  1982
             s.getChars(0, len, _cbuf, 0);
 218  
         }
 219  8
         catch (IndexOutOfBoundsException e)
 220  
         {
 221  8
             _cbuf = new char[len + (len - _cbuf.length)];
 222  8
             s.getChars(0, len, _cbuf, 0);
 223  1974
         }
 224  
 
 225  1982
         _bwriter.write(_cbuf, 0, len);
 226  1982
         _buffered = true;
 227  1982
     }
 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  0
             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  0
         }
 244  
 
 245  0
         _bwriter.write(_cbuf, 0, len);
 246  0
         _buffered = true;
 247  0
     }
 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  15975
         if (_buffered)
 292  
         {
 293  1975
             bflush();
 294  
         }
 295  15975
         _bstream.write(rawBytes, offset, len);
 296  15975
     }
 297  
 
 298  
     private void bflush ()
 299  
     {
 300  
         try
 301  
         {
 302  1981
             _bwriter.flush();
 303  1981
             _buffered = false;
 304  
         }
 305  0
         catch (IOException e)
 306  
         {
 307  0
             e.printStackTrace();
 308  1981
         }
 309  1981
     }
 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  1710
         if (_buffered)
 323  
         {
 324  0
             bflush();
 325  
         }
 326  
 
 327  1710
         if (_out != null)
 328  
         {
 329  14
             writeTo(_out);
 330  14
             _out.flush();
 331  
         }
 332  1710
         _bstream.reset();
 333  1710
     }
 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  3
         if (_buffered)
 355  
         {
 356  0
             bflush();
 357  
         }
 358  3
         return _bstream.getBytes();
 359  
     }
 360  
 
 361  
     /**
 362  
      * Copy the contents written so far into a String.
 363  
      */
 364  
     public String toString ()
 365  
     {
 366  1693
         if (_buffered)
 367  
         {
 368  6
             bflush();
 369  
         }
 370  
         try
 371  
         {
 372  1693
             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  14
         if (_buffered)
 387  
         {
 388  0
             bflush();
 389  
         }
 390  14
         _bstream.writeTo(out);
 391  14
     }
 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  1746
         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  0
         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  3
         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  1731
             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  1703
         flush();
 456  1703
         if (_out != null)
 457  
         {
 458  7
             _out.close();
 459  7
             _out = null;
 460  
         }
 461  1703
     }
 462  
 }
 463