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( "&#x9;" );
063            else if( ch == '\n' ) sink.append( "&#xA;" );
064            else if( ch == '\r' ) sink.append( "&#xD;" );
065            else if( ch == '&' )  sink.append( "&amp;" );
066            else if( ch == '<' )  sink.append( "&lt;" );
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}