View Javadoc
1   /*
2    *
3    * The DbUnit Database Testing Framework
4    * Copyright (C)2002-2004, DbUnit.org
5    *
6    * This library is free software; you can redistribute it and/or
7    * modify it under the terms of the GNU Lesser General Public
8    * License as published by the Free Software Foundation; either
9    * version 2.1 of the License, or (at your option) any later version.
10   *
11   * This library is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   * Lesser General Public License for more details.
15   *
16   * You should have received a copy of the GNU Lesser General Public
17   * License along with this library; if not, write to the Free Software
18   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19   *
20   */
21  
22  package org.dbunit.dataset.datatype;
23  
24  import org.dbunit.dataset.ITable;
25  import org.dbunit.util.Base64;
26  import org.slf4j.Logger;
27  import org.slf4j.LoggerFactory;
28  
29  import java.io.*;
30  import java.net.MalformedURLException;
31  import java.net.URL;
32  import java.sql.Blob;
33  import java.sql.PreparedStatement;
34  import java.sql.ResultSet;
35  import java.sql.SQLException;
36  import java.util.regex.Matcher;
37  import java.util.regex.Pattern;
38  
39  /**
40   * @author Manuel Laflamme
41   * @author Last changed by: $Author$
42   * @version $Revision$ $Date$
43   * @since 1.0 (Mar 20, 2002)
44   */
45  public class BytesDataType extends AbstractDataType
46  {
47  
48      /**
49       * Logger for this class
50       */
51      private static final Logger logger = LoggerFactory.getLogger(BytesDataType.class);
52  
53      private static final int MAX_URI_LENGTH = 256;
54      private static final Pattern inputPattern = Pattern.compile("^\\[(.*?)](.*)");
55  
56      public BytesDataType(String name, int sqlType)
57      {
58          super(name, sqlType, byte[].class, false);
59      }
60  
61      private byte[] toByteArray(InputStream in, int length) throws IOException
62      {
63          if (logger.isDebugEnabled())
64  		{
65  			logger.debug("toByteArray(in={}, length={}) - start", in, Integer.toString(length));
66  		}
67  
68          ByteArrayOutputStream out = new ByteArrayOutputStream(length);
69          in = new BufferedInputStream(in);
70          int i = in.read();
71          while (i != -1)
72          {
73              out.write(i);
74              i = in.read();
75          }
76          return out.toByteArray();
77      }
78  
79      public byte[] loadFile(String filename) throws IOException {
80          // Not an URL, try as file name
81          File file = new File(filename);
82          return toByteArray(new FileInputStream(file), (int)file.length());
83      }
84  
85      public byte[] loadURL(String urlAsString) throws IOException {
86          // Not an URL, try as file name
87          URL url = new URL(urlAsString);
88          return toByteArray(url.openStream(), 0);
89      }
90  
91      ////////////////////////////////////////////////////////////////////////////
92      // DataType class
93  
94      /**
95       * Casts the given value into a byte[] using different strategies. Note
96       * that this might sometimes result in undesired behavior when character
97       * data (Strings) are used.
98       *
99       * @see org.dbunit.dataset.datatype.DataType#typeCast(java.lang.Object)
100      */
101     public Object typeCast(Object value) throws TypeCastException
102     {
103         logger.debug("typeCast(value={}) - start", value);
104 
105         if (value == null || value == ITable.NO_VALUE)
106         {
107             return null;
108         }
109 
110         if (value instanceof byte[])
111         {
112             return value;
113         }
114 
115         if (value instanceof String)
116         {
117             String stringValue = (String) value;
118 
119             // If the string starts with <text [encoding id]>, it means that the user
120             // intentionally wants to transform the text into a blob.
121             //
122             // Example of a valid string:  "<text UTF-8>This is a valid string with the accent 'é'"
123             Matcher matcher = inputPattern.matcher(stringValue);
124             if (matcher.matches())
125             {
126                 String commandLine = matcher.group(1).toUpperCase();
127                 stringValue = matcher.group(2);
128 
129                 String[] split = commandLine.split(" ");
130                 String command = split[0];
131 
132                 if (command.equals("TEXT"))
133                 {
134                     String encoding = "UTF-8";  // Default
135 
136                     if (split.length > 1) {
137                         encoding = split[1];
138                     }
139                     logger.debug("Data explicitly states that given string is text encoded "
140                         + encoding);
141                     try {
142                         return stringValue.getBytes(encoding);
143                     } catch (UnsupportedEncodingException unsupportedEncodingException) {
144                         return "Error:  [text " + encoding + "] has an invalid encoding id.".getBytes();
145                     }
146                 }
147                 else if (command.equals("BASE64"))
148                 {
149                     logger.debug("Data explicitly states that given string is base46");
150                     return Base64.decode(stringValue);
151                 }
152                 else if (command.equals("FILE"))
153                 {
154                     try {
155                         logger.debug("Data explicitly states that given string is a file name");
156                         return loadFile(stringValue);
157                     } catch (IOException e) {
158                         String errMsg = "Could not load file following instruction >>" + value.toString() + "<<";
159                         logger.error(errMsg);
160                         return ("Error:  " + errMsg).getBytes();
161                     }
162                 }
163                 else if (command.equals("URL"))
164                 {
165                     try {
166                         logger.debug("Data explicitly states that given string is a URL");
167                         return loadURL(stringValue);
168                     } catch (IOException e) {
169                         String errMsg = "Could not load URL following instruction >>" + value.toString() + "<<";
170                         logger.error(errMsg);
171                         return ("Error:  " + errMsg).getBytes();
172                     }
173                 }
174             }
175 
176             // Assume not an uri if length greater than max uri length
177             if (stringValue.length() == 0 || stringValue.length() > MAX_URI_LENGTH)
178             {
179             	logger.debug("Assuming given string to be Base64 and not a URI");
180                 return Base64.decode((String) value);
181             }
182 
183             try
184             {
185               	logger.debug("Assuming given string to be a URI");
186                 try
187                 {
188                     // Try value as URL
189                     return loadURL(stringValue);
190                 }
191                 catch (MalformedURLException e1)
192                 {
193                 	logger.debug("Given string is not a valid URI - trying to resolve it as file...");
194                     try
195                     {
196                         // Not an URL, try as file name
197                         return loadFile(stringValue);
198                     }
199                     catch (FileNotFoundException e2)
200                     {
201                         logger.debug("Assuming given string to be Base64 and not a URI or File");
202                         // Not a file name either
203                         byte[] decodedBytes = Base64.decode(stringValue);
204                         if(decodedBytes == null && stringValue.length() > 0) {
205                             // Ok, here the user has not specified the "[text ...]" tag, but
206                             // it looks that its text that should be stored in the blob.  So
207                             // we make a last attempt at doing so.
208                             logger.debug("Assuming given string to be content of the blob, encoded with UTF-8.");
209                             return stringValue.getBytes();
210                         }
211                         else
212                             return decodedBytes;
213                     }
214                 }
215             }
216             catch (IOException e)
217             {
218                 throw new TypeCastException(value, this, e);
219             }
220         }
221 
222         if (value instanceof Blob)
223         {
224             try
225             {
226                 Blob blobValue = (Blob)value;
227                 if (blobValue.length() == 0) {
228                     return null;
229                 }
230                 return blobValue.getBytes(1, (int)blobValue.length());
231             }
232             catch (SQLException e)
233             {
234                 throw new TypeCastException(value, this, e);
235             }
236         }
237 
238         if (value instanceof URL)
239         {
240             try
241             {
242                 return toByteArray(((URL)value).openStream(), 0);
243             }
244             catch (IOException e)
245             {
246                 throw new TypeCastException(value, this, e);
247             }
248         }
249 
250         if (value instanceof File)
251         {
252             try
253             {
254                 File file = (File)value;
255                 return toByteArray(new FileInputStream(file), (int)file.length());
256             }
257             catch (IOException e)
258             {
259                 throw new TypeCastException(value, this, e);
260             }
261         }
262 
263         throw new TypeCastException(value, this);
264     }
265 
266 
267     protected int compareNonNulls(Object value1, Object value2) throws TypeCastException
268     {
269         logger.debug("compareNonNulls(value1={}, value2={}) - start", value1, value2);
270 
271         try
272         {
273             byte[] value1cast = (byte[])typeCast(value1);
274             byte[] value2cast = (byte[])typeCast(value2);
275 
276             return compare(value1cast, value2cast);
277         }
278         catch (ClassCastException e)
279         {
280             throw new TypeCastException(e);
281         }
282     }
283 
284     public int compare(byte[] v1, byte[] v2) throws TypeCastException
285     {
286         if (logger.isDebugEnabled())
287 		{
288 			logger.debug("compare(v1={}, v2={}) - start", v1, v2);
289 		}
290 
291         int len1 = v1.length;
292         int len2 = v2.length;
293         int n = Math.min(len1, len2);
294         int i = 0;
295         int j = 0;
296 
297         if (i == j)
298         {
299             int k = i;
300             int lim = n + i;
301             while (k < lim)
302             {
303                 byte c1 = v1[k];
304                 byte c2 = v2[k];
305                 if (c1 != c2)
306                 {
307                     return c1 - c2;
308                 }
309                 k++;
310             }
311         }
312         else
313         {
314             while (n-- != 0)
315             {
316                 byte c1 = v1[i++];
317                 byte c2 = v2[j++];
318                 if (c1 != c2)
319                 {
320                     return c1 - c2;
321                 }
322             }
323         }
324         return len1 - len2;
325     }
326 
327     public Object getSqlValue(int column, ResultSet resultSet)
328             throws SQLException, TypeCastException
329     {
330     	if(logger.isDebugEnabled())
331     		logger.debug("getSqlValue(column={}, resultSet={}) - start", new Integer(column), resultSet);
332 
333         byte[] value = resultSet.getBytes(column);
334         if (value == null || resultSet.wasNull())
335         {
336             return null;
337         }
338         return value;
339     }
340 
341     public void setSqlValue(Object value, int column, PreparedStatement statement)
342             throws SQLException, TypeCastException
343     {
344     	if (logger.isDebugEnabled())
345     	{
346     		logger.debug("setSqlValue(value={}, column={}, statement={}) - start",
347         		new Object[]{value, new Integer(column), statement} );
348     	}
349 
350         super.setSqlValue(value, column, statement);
351     }
352 
353 }