001package votorola.g.sql; // Copyright 2007-2009, 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 javax.xml.stream.*; 005import votorola.g.lang.*; 006import votorola.g.xml.stream.*; 007 008import static votorola.g.xml.stream.XMLInputFactoryX.SIMPLE_INPUT_FACTORY; 009 010 011/** An XML serializer for the storage of non-relational data in an 'xml' table column. 012 */ 013public @ThreadSafe class XMLColumnAppender 014{ 015 016 017 /** Constructs an XMLColumnAppender. 018 * 019 * @see #sink() 020 */ 021 public XMLColumnAppender( Appendable _sink ) { sink = _sink; } 022 023 024 025 // ------------------------------------------------------------------------------------ 026 027 028 /** Serializes the specified datum as a space character followed by an attribute 029 * declaration and appends it to the sink, or does nothing if the value is null. 030 * 031 * @param name a valid XML name for the attribute. 032 * @param value the value of the datum, or null to append nothing. 033 * 034 * @throws IllegalArgumentException if the value is empty, as currently that is 035 * not supported. 036 */ 037 public void appendAttribute( final String name, final String value ) throws IOException 038 { 039 if( value == null ) return; 040 041 if( value.length() == 0 ) throw new IllegalArgumentException( "empty value" ); 042 043 sink.append( ' ' ).append( name ).append( '=' ); 044 final char quote; 045 final String quoteEscaped; 046 // if( value.contains( "'" )) 047 // { 048 quote = '"'; 049 quoteEscaped = """; 050 // } 051 // else 052 // { 053 // quote = '\''; 054 // quoteEscaped = "'"; 055 // } 056 ///// dubious optimization, assumes time is less valuable than space 057 sink.append( quote ); 058 for( int c = 0, cN = value.length(); c < cN; ++c ) 059 { 060 char ch = value.charAt( c ); 061 if( ch == quote ) sink.append( quoteEscaped ); 062 else if( ch == '\t' ) sink.append( "	" ); 063 else if( ch == '\n' ) sink.append( "
" ); 064 else if( ch == '\r' ) sink.append( "
" ); 065 else if( ch == '&' ) sink.append( "&" ); 066 else if( ch == '<' ) sink.append( "<" ); 067 else sink.append( ch ); 068 } 069 sink.append( quote ); 070 } 071 072 073 074 /** Returns a non-empty string if b is true, or null otherwise. 075 * 076 * @see #stringToBoolean(String) 077 */ 078 public static String booleanToString( final boolean b ) { return b? "t": null; } 079 080 081 /** Returns true if s is non-null and non-empty, or false otherwise. 082 * 083 * @see #booleanToString(boolean) 084 */ 085 public static boolean stringToBoolean( final String s ) 086 { 087 return s != null && s.length() > 0; 088 } 089 090 091 092 /** Returns null if n is 0, otherwise the standard string representation of n. 093 * 094 * @see #stringToByte(String) 095 */ 096 public static String byteToString( final byte n ) 097 { 098 return n == 0? null: Byte.toString( n ); 099 } 100 101 102 /** Returns 0 if s is null or empty, otherwise the parsed value of s. 103 * 104 * @see #byteToString(byte) 105 */ 106 public static byte stringToByte( final String s ) 107 { 108 return s == null || s.length() == 0? 0: Byte.parseByte( s ); 109 } 110 111 112 113 /** Returns null if n is 0L, otherwise the standard string representation of n. 114 * 115 * @see #stringToLong(String) 116 */ 117 public static String longToString( final long n ) 118 { 119 return n == 0L? null: Long.toString( n ); 120 } 121 122 123 /** Returns 0L if s is null or empty, otherwise the parsed value of s. 124 * 125 * @see #longToString(long) 126 */ 127 public static long stringToLong( final String s ) 128 { 129 return s == null || s.length() == 0? 0L: Long.parseLong( s ); 130 } 131 132 133 134 /** Constructs a stream reader configured to read the serialized XML. 135 */ 136 public static XMLStreamReader newStreamReader( final Reader reader ) 137 throws XMLStreamException 138 { 139 synchronized( XMLInputFactoryX.class ) 140 { 141 return SIMPLE_INPUT_FACTORY.createXMLStreamReader( reader ); 142 } 143 } 144 145 146 147 /** Constructs a stream reader configured to read the serialized XML. 148 */ 149 public static XMLStreamReader newStreamReader( final String systemId, final InputStream in ) 150 throws XMLStreamException 151 { 152 synchronized( XMLInputFactoryX.class ) 153 { 154 return SIMPLE_INPUT_FACTORY.createXMLStreamReader( systemId, in ); 155 } 156 } 157 158 159 160 /** Constructs a stream reader configured to read the serialized XML. 161 */ 162 public static XMLStreamReader newStreamReader( final String systemId, final Reader reader ) 163 throws XMLStreamException 164 { 165 synchronized( XMLInputFactoryX.class ) 166 { 167 return SIMPLE_INPUT_FACTORY.createXMLStreamReader( systemId, reader ); 168 } 169 } 170 171 172 173 /** The underlying sink for the XML serialization. 174 */ 175 public Appendable sink() { return sink; } 176 177 178 private final Appendable sink; 179 180 181}