package votorola.g.net; // Copyright 2011, Michael Allan. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Votorola Software"), to deal in the Votorola Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicence, and/or sell copies of the Votorola Software, and to permit persons to whom the Votorola Software is furnished to do so, subject to the following conditions: The preceding copyright notice and this permission notice shall be included in all copies or substantial portions of the Votorola Software. THE VOTOROLA SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE VOTOROLA SOFTWARE OR THE USE OR OTHER DEALINGS IN THE VOTOROLA SOFTWARE. import java.io.*; import java.net.*; import votorola.g.lang.*; /** A writer that outputs strings encoded in MIME format * application/x-www-form-urlencoded. It does only rudimentary buffering. Feeding it * truly massive strings will stretch the buffer to an inefficient size [FIX with * trimToSize]. * * @see URLEncoder */ public @ThreadSafe final class URLEncodedWriter extends Writer { /** Constructs a URLEncodedWriter. * * @see #encoding() * @param _out the underlying writer. */ public URLEncodedWriter( String _encoding, Writer _out ) { if( _out == null ) throw new NullPointerException(); // fail fast encoding = _encoding; out = _out; } // ------------------------------------------------------------------------------------ /** The assumed character encoding, as required by URLEncoder. * * @see URLEncoder */ public String encoding() { return encoding; } private final String encoding; // - A p p e n d a b l e -------------------------------------------------------------- public @Override URLEncodedWriter append( char c ) throws IOException { write( c ); return URLEncodedWriter.this; } public @Override URLEncodedWriter append( CharSequence csq ) throws IOException { synchronized( lock ) { b.append( csq ); bCheck(); } return URLEncodedWriter.this; } public @Override URLEncodedWriter append( CharSequence csq, int start, int end ) throws IOException { synchronized( lock ) { b.append( csq, start, end ); bCheck(); } return URLEncodedWriter.this; } // - C l o s e a b l e ---------------------------------------------------------------- public void close() throws IOException { synchronized( lock ) { if( !isClosed ) flush(); isClosed = true; } out.close(); } private @ThreadRestricted("holds lock") boolean isClosed; // - F l u s h a b l e ---------------------------------------------------------------- public void flush() throws IOException { synchronized( lock ) { openCheck(); bFlush(); } out.flush(); } // - W r i t e r ---------------------------------------------------------------------- public @Override void write( char[] cbuf ) throws IOException { synchronized( lock ) { b.append( cbuf ); bCheck(); } } public void write( char[] cbuf, int off, int len ) throws IOException { synchronized( lock ) { b.append( cbuf, off, len ); bCheck(); } } public @Override void write( int c ) throws IOException { synchronized( lock ) { b.append( (char)c ); bCheck(); } } public @Override void write( String str ) throws IOException { append( str ); } public @Override void write( String str, int off, int len ) throws IOException { append( str, off, off + len ); } //// P r i v a t e /////////////////////////////////////////////////////////////////////// @ThreadRestricted("holds lock") private final StringBuilder b = new StringBuilder( /*initial capacity*/FLUSH_THRESHOLD + 150 ); private @ThreadRestricted("holds lock") void bCheck() throws IOException { assert Thread.holdsLock( lock ); openCheck(); if( b.length() > FLUSH_THRESHOLD ) bFlush(); } private @ThreadRestricted("holds lock") void bFlush() throws IOException { assert Thread.holdsLock( lock ); out.write( URLEncoder.encode( b.toString(), encoding )); b.delete( 0, b.length() ); } private static final int FLUSH_THRESHOLD = 350; @ThreadRestricted("holds lock") private void openCheck() throws IOException // uphold the contract of Writer { assert Thread.holdsLock( lock ); if( isClosed ) throw new IOException( "attempt to operate on a closed writer" ); } private final Writer out; }