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