001package votorola.g.script; // Copyright 2011-2012, 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. 002 003import java.io.*; 004import votorola.g.lang.*; 005 006 007/** A writer that outputs JSON string content, properly escaping any characters that 008 * require it. 009 * 010 * @see <a href='http://tools.ietf.org/html/rfc4627'>RFC 4627</a> 011 */ 012public @ThreadSafe final class JSONStringWriter extends Writer 013{ 014 015 016 /** Constructs a JSONStringWriter. 017 * 018 * @param _out the underlying writer. 019 */ 020 public JSONStringWriter( Writer _out ) 021 { 022 if( _out == null ) throw new NullPointerException(); // fail fast 023 024 out = _out; 025 } 026 027 028 029 // - A p p e n d a b l e -------------------------------------------------------------- 030 031 032 public @Override Writer append( final char c ) throws IOException 033 { 034 switch( c ) 035 { 036 case '\\': 037 case '"': 038 out.append( '\\' ).append( c ); 039 break; 040 case '\b': 041 out.append( '\\' ).append( 'b' ); 042 break; 043 case '\f': 044 out.append( '\\' ).append( 'f' ); 045 break; 046 case '\n': 047 out.append( '\\' ).append( 'n' ); 048 break; 049 case '\r': 050 out.append( '\\' ).append( 'r' ); 051 break; 052 case '\t': 053 out.append( '\\' ).append( 't' ); 054 break; 055 default: 056 if( c > 0x1F ) out.append( c ); 057 else 058 { 059 // Note that JSON string content, like Java, is always Unicode. So no 060 // escaping need be done for out-of-range characters, only for these 061 // control characters. 062 out.append( '\\' ).append( 'u' ); 063 out.append( String.format( "%04x", (int)c )); 064 } 065 } 066 return JSONStringWriter.this; 067 } 068 069 070 071 public @Override Writer append( final CharSequence csq ) throws IOException 072 { 073 final int cN = csq.length(); 074 for( int c = 0; c < cN; ++c ) append( csq.charAt( c )); 075 return JSONStringWriter.this; 076 } 077 078 079 080 /** Throws {@linkplain UnsupportedOperationException UnsupportedOperationException} 081 * because it is not yet needed. 082 */ 083 public @Override Writer append( CharSequence _csq, int _start, int _end ) throws IOException 084 { 085 throw new UnsupportedOperationException(); 086 } 087 088 089 090 // - C l o s e a b l e ---------------------------------------------------------------- 091 092 093 /** Does nothing but close the underlying writer. This writer itself need not be 094 * closed. 095 */ 096 public @Override void close() throws IOException { out.close(); } 097 // Closed state is never tested during operations on this writer. Closure has no 098 // effect on its state. The underlying writer (out) is relied on to fulfill the 099 // contract in this regard. 100 101 102 103 // - F l u s h a b l e ---------------------------------------------------------------- 104 105 106 /** Does nothing but flush the underlying writer. This writer itself need not be 107 * flushed. 108 */ 109 public @Override void flush() throws IOException { out.flush(); } 110 111 112 113 // - W r i t e r ---------------------------------------------------------------------- 114 115 116 /** Throws {@linkplain UnsupportedOperationException UnsupportedOperationException} 117 * because it is not yet needed. 118 */ 119 public @Override void write( char[] _cbuf ) throws IOException 120 { 121 throw new UnsupportedOperationException(); 122 } 123 124 125 126 /** Throws {@linkplain UnsupportedOperationException UnsupportedOperationException} 127 * because it is not yet needed. 128 */ 129 public @Override void write( char[] _cbuf, int _off, int _len ) throws IOException 130 { 131 throw new UnsupportedOperationException(); 132 } 133 134 135 136 public @Override void write( final int c ) throws IOException { append( (char)c ); } 137 138 139 140 /** Throws {@linkplain UnsupportedOperationException UnsupportedOperationException} 141 * because it is not yet needed. 142 */ 143 public @Override void write( String _str, int _off, int _len ) throws IOException 144 { 145 throw new UnsupportedOperationException(); 146 } 147 148 149 150 public @Override void write( final String str ) throws IOException { append( str ); } 151 152 153 154//// P r i v a t e /////////////////////////////////////////////////////////////////////// 155 156 157 private final Writer out; 158 159 160 161}