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.sql.Blob;
008    import java.sql.SQLException;
009    import java.util.ArrayList;
010    import java.util.List;
011    
012    import com.mockrunner.base.NestedApplicationException;
013    import com.mockrunner.util.common.ArrayUtil;
014    import com.mockrunner.util.common.CollectionUtil;
015    
016    /**
017     * Mock implementation of <code>Blob</code>.
018     */
019    public class MockBlob implements Blob, Cloneable
020    {
021        private List blobData;
022        private boolean wasFreeCalled;
023        
024        public MockBlob(byte[] data)
025        {
026            blobData = ArrayUtil.getListFromByteArray(data);
027            wasFreeCalled = false;
028        }
029        
030        public long length() throws SQLException
031        {
032            if(wasFreeCalled)
033            {
034                throw new SQLException("free() was called");
035            }
036            return blobData.size();
037        }
038    
039        public byte[] getBytes(long pos, int length) throws SQLException
040        {
041            if(wasFreeCalled)
042            {
043                throw new SQLException("free() was called");
044            }
045            length = verifyAndFixLength(pos, length);
046            return ArrayUtil.getByteArrayFromList(blobData, (int)(pos - 1), length);
047        }
048    
049        public InputStream getBinaryStream() throws SQLException
050        {
051            if(wasFreeCalled)
052            {
053                throw new SQLException("free() was called");
054            }
055            return new ByteArrayInputStream(ArrayUtil.getByteArrayFromList(blobData));
056        }
057    
058        public InputStream getBinaryStream(long pos, long length) throws SQLException
059        {
060            if(wasFreeCalled)
061            {
062                throw new SQLException("free() was called");
063            }
064            length = verifyAndFixLength(pos, (int)length);
065            return new ByteArrayInputStream(ArrayUtil.getByteArrayFromList(blobData, (int)(pos - 1), (int)length));
066        }
067    
068        public long position(byte[] pattern, long start) throws SQLException
069        {
070            if(wasFreeCalled)
071            {
072                throw new SQLException("free() was called");
073            }
074            byte[] data = ArrayUtil.getByteArrayFromList(blobData);
075            int index = ArrayUtil.indexOf(data, pattern, (int)(start - 1));
076            if(-1 != index) index += 1;
077            return index;
078        }
079    
080        public long position(Blob pattern, long start) throws SQLException
081        {
082            return position(pattern.getBytes(1, (int)pattern.length()), start);
083        }
084    
085        public int setBytes(long pos, byte[] bytes) throws SQLException
086        {
087            if(wasFreeCalled)
088            {
089                throw new SQLException("free() was called");
090            }
091            ArrayUtil.addBytesToList(bytes, blobData, (int)(pos - 1));
092            return bytes.length;
093        }
094    
095        public int setBytes(long pos, byte[] bytes, int offset, int len) throws SQLException
096        {
097            if(wasFreeCalled)
098            {
099                throw new SQLException("free() was called");
100            }
101            ArrayUtil.addBytesToList(bytes, offset, len, blobData, (int)(pos - 1));
102            return len;
103        }
104    
105        public OutputStream setBinaryStream(long pos) throws SQLException
106        {
107            if(wasFreeCalled)
108            {
109                throw new SQLException("free() was called");
110            }
111            return new BlobOutputStream((int)(pos - 1));
112        }
113    
114        public void truncate(long len) throws SQLException
115        {
116            if(wasFreeCalled)
117            {
118                throw new SQLException("free() was called");
119            }
120            blobData = CollectionUtil.truncateList(blobData, (int)len);
121        }
122        
123        public void free() throws SQLException
124        {
125            wasFreeCalled = true;
126        }
127    
128        /**
129         * Returns if {@link #free} has been called.
130         * @return <code>true</code> if {@link #free} has been called,
131         *         <code>false</code> otherwise
132         */
133        public boolean wasFreeCalled()
134        {
135            return wasFreeCalled;
136        }
137    
138        public boolean equals(Object obj)
139        {
140            if(null == obj) return false;
141            if(!obj.getClass().equals(this.getClass())) return false;
142            MockBlob other = (MockBlob)obj;
143            if(wasFreeCalled != other.wasFreeCalled()) return false;
144            return blobData.equals(other.blobData);
145        }
146    
147        public int hashCode()
148        {
149            int hashCode = blobData.hashCode();
150            hashCode = (31 * hashCode) + (wasFreeCalled ? 31 : 62);
151            return hashCode;
152        }
153    
154        public String toString()
155        {
156            return "Blob data: " + blobData.toString();     
157        }
158        
159        public Object clone()
160        {
161            try
162            {
163                MockBlob blob = (MockBlob)super.clone();
164                blob.blobData = new ArrayList(blobData);
165                return blob;
166            }
167            catch(CloneNotSupportedException exc)
168            {
169                throw new NestedApplicationException(exc);
170            }
171        }
172        
173        private int verifyAndFixLength(long pos, int length)
174        {
175            if(length < 0)
176            {
177                throw new IllegalArgumentException("length must be greater or equals 0");
178            }
179            if((length + (pos - 1)) > blobData.size())
180            {
181                return blobData.size() - (int)(pos - 1);
182            }
183            return length;
184        }
185        
186        private class BlobOutputStream extends OutputStream
187        {  
188            private int index;
189            
190            public BlobOutputStream(int index)
191            {
192                this.index = index;
193            }
194            
195            public void write(int byteValue) throws IOException
196            {
197                byte[] bytes = new byte[] {(byte)byteValue};
198                ArrayUtil.addBytesToList(bytes, blobData, index);
199                index++;
200            }
201        }
202    }