001    package com.mockrunner.mock.jdbc;
002    
003    import java.io.ByteArrayInputStream;
004    import java.io.IOException;
005    import java.io.InputStream;
006    import java.io.OutputStream;
007    import java.io.Reader;
008    import java.io.StringReader;
009    import java.io.UnsupportedEncodingException;
010    import java.io.Writer;
011    import java.sql.Clob;
012    import java.sql.SQLException;
013    
014    import com.mockrunner.base.NestedApplicationException;
015    
016    /**
017     * Mock implementation of <code>Clob</code>.
018     */
019    public class MockClob implements Clob, Cloneable
020    {
021        private StringBuffer clobData;
022        private boolean wasFreeCalled;
023        
024        public MockClob(String data)
025        {
026            clobData = new StringBuffer(data);
027            wasFreeCalled = false;
028        }
029    
030        public long length() throws SQLException
031        {
032            return clobData.length();
033        }
034    
035        public void truncate(long len) throws SQLException
036        {
037            if(wasFreeCalled)
038            {
039                throw new SQLException("free() was called");
040            }
041            clobData.setLength((int)len);
042        }
043    
044        public InputStream getAsciiStream() throws SQLException
045        {
046            if(wasFreeCalled)
047            {
048                throw new SQLException("free() was called");
049            }
050            try
051            {
052                return new ByteArrayInputStream(clobData.toString().getBytes("ISO-8859-1"));
053            } 
054            catch(UnsupportedEncodingException exc)
055            {
056                throw new NestedApplicationException(exc);
057            }
058        }
059    
060        public OutputStream setAsciiStream(long pos) throws SQLException
061        {
062            if(wasFreeCalled)
063            {
064                throw new SQLException("free() was called");
065            }
066            return new ClobOutputStream((int)(pos - 1));
067        }
068    
069        public Reader getCharacterStream() throws SQLException
070        {
071            if(wasFreeCalled)
072            {
073                throw new SQLException("free() was called");
074            }
075            return new StringReader(clobData.toString());
076        }
077    
078        public Reader getCharacterStream(long pos, long length) throws SQLException
079        {
080            if(wasFreeCalled)
081            {
082                throw new SQLException("free() was called");
083            }
084            length = verifyAndFixLength(pos, (int)length);
085            return new StringReader(getSubString(pos, (int)length));
086        }
087    
088        public Writer setCharacterStream(long pos) throws SQLException
089        {
090            if(wasFreeCalled)
091            {
092                throw new SQLException("free() was called");
093            }
094            return new ClobWriter((int)(pos - 1));
095        }
096    
097        public String getSubString(long pos, int length) throws SQLException
098        {
099            if(wasFreeCalled)
100            {
101                throw new SQLException("free() was called");
102            }
103            length = verifyAndFixLength(pos, length);
104            return clobData.substring((int)(pos - 1), (int)(pos - 1) + length);
105        }
106    
107        public int setString(long pos, String str) throws SQLException
108        {
109            return setString(pos, str, 0, str.length());
110        }
111    
112        public int setString(long pos, String str, int offset, int len) throws SQLException
113        {
114            if(wasFreeCalled)
115            {
116                throw new SQLException("free() was called");
117            }
118            str = str.substring(offset, offset + len);
119            clobData.replace((int)(pos - 1), (int)(pos - 1) + str.length(), str);
120            return len;
121        }
122    
123        public long position(String searchstr, long start) throws SQLException
124        {
125            if(wasFreeCalled)
126            {
127                throw new SQLException("free() was called");
128            }
129            int index = clobData.toString().indexOf(searchstr, (int)(start - 1));
130            if(-1 != index) index += 1;
131            return index;
132        }
133    
134        public long position(Clob searchClob, long start) throws SQLException
135        {
136            return position(searchClob.getSubString(1, (int)searchClob.length()), start);
137        }
138        
139        public void free() throws SQLException
140        {
141            wasFreeCalled = true;
142        }
143    
144        /**
145         * Returns if {@link #free} has been called.
146         * @return <code>true</code> if {@link #free} has been called,
147         *         <code>false</code> otherwise
148         */
149        public boolean wasFreeCalled()
150        {
151            return wasFreeCalled;
152        }
153    
154        public boolean equals(Object obj)
155        {
156            if(null == obj) return false;
157            if(!obj.getClass().equals(this.getClass())) return false;
158            MockClob other = (MockClob)obj;
159            if(wasFreeCalled != other.wasFreeCalled()) return false;
160            return clobData.toString().equals(other.clobData.toString());
161        }
162    
163        public int hashCode()
164        {
165            int hashCode = clobData.toString().hashCode();
166            hashCode = (31 * hashCode) + (wasFreeCalled ? 31 : 62);
167            return hashCode;
168        }
169    
170        public String toString()
171        {
172            return "Clob data: " + clobData.toString();
173        }
174        
175        public Object clone()
176        {
177            try
178            {
179                MockClob clone = (MockClob)super.clone();
180                clone.clobData = new StringBuffer(clobData.toString());
181                return clone;
182            }
183            catch(CloneNotSupportedException exc)
184            {
185                throw new NestedApplicationException(exc);
186            }
187        }
188        
189        private int verifyAndFixLength(long pos, int length)
190        {
191            if(length < 0)
192            {
193                throw new IllegalArgumentException("length must be greater or equals 0");
194            }
195            if((length + (pos - 1)) > clobData.length())
196            {
197                return clobData.length() - (int)(pos - 1);
198            }
199            return length;
200        }
201        
202        private class ClobWriter extends Writer
203        {  
204            private int index;
205            
206            public ClobWriter(int index)
207            {
208                this.index = index;
209            }
210            
211            public void close() throws IOException
212            {
213    
214            }
215    
216            public void flush() throws IOException
217            {
218    
219            }
220    
221            public void write(char[] cbuf, int off, int len) throws IOException
222            {
223                try
224                {
225                    setString(index + 1, new String(cbuf, off, len));
226                }
227                catch(SQLException exc)
228                {
229                    throw new IOException(exc.getMessage());
230                }
231                index++;
232            }
233        }
234        
235        private class ClobOutputStream extends OutputStream
236        {  
237            private int index;
238        
239            public ClobOutputStream(int index)
240            {
241                this.index = index;
242            }
243        
244            public void write(int byteValue) throws IOException
245            {
246                byte[] bytes = new byte[] {(byte)byteValue};
247                try
248                {
249                    setString(index + 1, new String(bytes));
250                }
251                catch(SQLException exc)
252                {
253                    throw new IOException(exc.getMessage());
254                }
255                index++;
256            }
257        }
258    }