BytesDataType.java

  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. package org.dbunit.dataset.datatype;

  22. import java.io.BufferedInputStream;
  23. import java.io.ByteArrayOutputStream;
  24. import java.io.File;
  25. import java.io.FileInputStream;
  26. import java.io.FileNotFoundException;
  27. import java.io.IOException;
  28. import java.io.InputStream;
  29. import java.net.MalformedURLException;
  30. import java.net.URL;
  31. import java.nio.charset.Charset;
  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. import org.dbunit.dataset.ITable;
  39. import org.dbunit.util.Base64;
  40. import org.slf4j.Logger;
  41. import org.slf4j.LoggerFactory;

  42. /**
  43.  * @author Manuel Laflamme
  44.  * @author Last changed by: $Author$
  45.  * @version $Revision$ $Date$
  46.  * @since 1.0 (Mar 20, 2002)
  47.  */
  48. public class BytesDataType extends AbstractDataType
  49. {
  50.     private static final Logger logger =
  51.             LoggerFactory.getLogger(BytesDataType.class);

  52.     private static final int MAX_URI_LENGTH = 256;
  53.     private static final Pattern inputPattern =
  54.             Pattern.compile("^\\[(.*?)](.*)");

  55.     public BytesDataType(final String name, final int sqlType)
  56.     {
  57.         super(name, sqlType, byte[].class, false);
  58.     }

  59.     private byte[] toByteArray(InputStream in, final int length)
  60.             throws IOException
  61.     {
  62.         if (logger.isDebugEnabled())
  63.         {
  64.             logger.debug("toByteArray(in={}, length={}) - start", in, length);
  65.         }

  66.         final ByteArrayOutputStream out = new ByteArrayOutputStream(length);
  67.         in = new BufferedInputStream(in);
  68.         int i = in.read();
  69.         while (i != -1)
  70.         {
  71.             out.write(i);
  72.             i = in.read();
  73.         }
  74.         return out.toByteArray();
  75.     }

  76.     public byte[] loadFile(final String filename) throws IOException
  77.     {
  78.         // Not an URL, try as file name
  79.         final File file = new File(filename);
  80.         return toByteArray(new FileInputStream(file), (int) file.length());
  81.     }

  82.     public byte[] loadURL(final String urlAsString) throws IOException
  83.     {
  84.         // Not an URL, try as file name
  85.         final URL url = new URL(urlAsString);
  86.         return toByteArray(url.openStream(), 0);
  87.     }

  88.     ////////////////////////////////////////////////////////////////////////////
  89.     // DataType class

  90.     /**
  91.      * Casts the given value into a byte[] using different strategies. Note that
  92.      * this might sometimes result in undesired behavior when character data
  93.      * (Strings) are used.
  94.      *
  95.      * @see org.dbunit.dataset.datatype.DataType#typeCast(java.lang.Object)
  96.      */
  97.     @Override
  98.     public Object typeCast(final Object value) throws TypeCastException
  99.     {
  100.         logger.debug("typeCast(value={}) - start", value);

  101.         if (value == null || value == ITable.NO_VALUE)
  102.         {
  103.             return null;
  104.         }

  105.         if (value instanceof byte[])
  106.         {
  107.             return value;
  108.         }

  109.         if (value instanceof String)
  110.         {
  111.             String stringValue = (String) value;

  112.             // If the string starts with <text [encoding id]>, it means that the
  113.             // user
  114.             // intentionally wants to transform the text into a blob.
  115.             //
  116.             // Example of a valid string: "<text UTF-8>This is a valid string
  117.             // with the accent 'é'"
  118.             if (isExtendedSyntax(stringValue))
  119.             {
  120.                 final Matcher matcher = inputPattern.matcher(stringValue);
  121.                 if (matcher.matches())
  122.                 {
  123.                     final String commandLine = matcher.group(1).toUpperCase();
  124.                     stringValue = matcher.group(2);

  125.                     final String[] split = commandLine.split(" ");
  126.                     final String command = split[0];

  127.                     if ("TEXT".equals(command))
  128.                     {
  129.                         String encoding = "UTF-8"; // Default

  130.                         if (split.length > 1)
  131.                         {
  132.                             encoding = split[1];
  133.                         }
  134.                         logger.debug(
  135.                                 "Data explicitly states that given string is text encoded {}",
  136.                                 encoding);
  137.                         try
  138.                         {
  139.                             final Charset charset = Charset.forName(encoding);
  140.                             return stringValue.getBytes(charset);
  141.                         } catch (final IllegalArgumentException e)
  142.                         {
  143.                             return "Error:  [text " + encoding
  144.                                     + "] has an invalid encoding id.";
  145.                         }
  146.                     } else if ("BASE64".equals(command))
  147.                     {
  148.                         logger.debug(
  149.                                 "Data explicitly states that given string is base46");
  150.                         return Base64.decode(stringValue);
  151.                     } else if ("FILE".equals(command))
  152.                     {
  153.                         try
  154.                         {
  155.                             logger.debug(
  156.                                     "Data explicitly states that given string is a file name");
  157.                             return loadFile(stringValue);
  158.                         } catch (final IOException e)
  159.                         {
  160.                             final String errMsg =
  161.                                     "Could not load file following instruction >>"
  162.                                             + value + "<<";
  163.                             logger.error(errMsg);
  164.                             return ("Error:  " + errMsg).getBytes();
  165.                         }
  166.                     } else if ("URL".equals(command))
  167.                     {
  168.                         try
  169.                         {
  170.                             logger.debug(
  171.                                     "Data explicitly states that given string is a URL");
  172.                             return loadURL(stringValue);
  173.                         } catch (final IOException e)
  174.                         {
  175.                             final String errMsg =
  176.                                     "Could not load URL following instruction >>"
  177.                                             + value + "<<";
  178.                             logger.error(errMsg);
  179.                             return ("Error:  " + errMsg).getBytes();
  180.                         }
  181.                     }
  182.                 }
  183.             }

  184.             // Assume not an uri if length greater than max uri length
  185.             if (stringValue.length() == 0
  186.                     || stringValue.length() > MAX_URI_LENGTH)
  187.             {
  188.                 logger.debug(
  189.                         "Assuming given string to be Base64 and not a URI");
  190.                 return Base64.decode((String) value);
  191.             }

  192.             try
  193.             {
  194.                 logger.debug("Assuming given string to be a URI");
  195.                 try
  196.                 {
  197.                     // Try value as URL
  198.                     return loadURL(stringValue);
  199.                 } catch (final MalformedURLException e1)
  200.                 {
  201.                     logger.debug(
  202.                             "Given string is not a valid URI - trying to resolve it as file...");
  203.                     try
  204.                     {
  205.                         // Not an URL, try as file name
  206.                         return loadFile(stringValue);
  207.                     } catch (final FileNotFoundException e2)
  208.                     {
  209.                         logger.debug(
  210.                                 "Assuming given string to be Base64 and not a URI or File");
  211.                         // Not a file name either
  212.                         final byte[] decodedBytes = Base64.decode(stringValue);
  213.                         if (decodedBytes == null && stringValue.length() > 0)
  214.                         {
  215.                             // Ok, here the user has not specified the "[text
  216.                             // ...]" tag, but
  217.                             // it looks that its text that should be stored in
  218.                             // the blob. So
  219.                             // we make a last attempt at doing so.
  220.                             logger.debug(
  221.                                     "Assuming given string to be content of the blob, encoded with UTF-8.");
  222.                             return stringValue.getBytes();
  223.                         } else
  224.                         {
  225.                             return decodedBytes;
  226.                         }
  227.                     }
  228.                 }
  229.             } catch (final IOException e)
  230.             {
  231.                 throw new TypeCastException(value, this, e);
  232.             }
  233.         }

  234.         if (value instanceof Blob)
  235.         {
  236.             try
  237.             {
  238.                 final Blob blobValue = (Blob) value;
  239.                 if (blobValue.length() == 0)
  240.                 {
  241.                     return null;
  242.                 }
  243.                 return blobValue.getBytes(1, (int) blobValue.length());
  244.             } catch (final SQLException e)
  245.             {
  246.                 throw new TypeCastException(value, this, e);
  247.             }
  248.         }

  249.         if (value instanceof URL)
  250.         {
  251.             try
  252.             {
  253.                 return toByteArray(((URL) value).openStream(), 0);
  254.             } catch (final IOException e)
  255.             {
  256.                 throw new TypeCastException(value, this, e);
  257.             }
  258.         }

  259.         if (value instanceof File)
  260.         {
  261.             try
  262.             {
  263.                 final File file = (File) value;
  264.                 return toByteArray(new FileInputStream(file),
  265.                         (int) file.length());
  266.             } catch (final IOException e)
  267.             {
  268.                 throw new TypeCastException(value, this, e);
  269.             }
  270.         }

  271.         throw new TypeCastException(value, this);
  272.     }

  273.     @Override
  274.     protected int compareNonNulls(final Object value1, final Object value2)
  275.             throws TypeCastException
  276.     {
  277.         logger.debug("compareNonNulls(value1={}, value2={}) - start", value1,
  278.                 value2);

  279.         try
  280.         {
  281.             final byte[] value1cast = (byte[]) typeCast(value1);
  282.             final byte[] value2cast = (byte[]) typeCast(value2);

  283.             return compare(value1cast, value2cast);
  284.         } catch (final ClassCastException e)
  285.         {
  286.             throw new TypeCastException(e);
  287.         }
  288.     }

  289.     public int compare(final byte[] v1, final byte[] v2)
  290.             throws TypeCastException
  291.     {
  292.         if (logger.isDebugEnabled())
  293.         {
  294.             logger.debug("compare(v1={}, v2={}) - start", v1, v2);
  295.         }

  296.         final int len1 = v1.length;
  297.         final int len2 = v2.length;
  298.         int n = Math.min(len1, len2);
  299.         int i = 0;
  300.         int j = 0;

  301.         if (i == j)
  302.         {
  303.             int k = i;
  304.             final int lim = n + i;
  305.             while (k < lim)
  306.             {
  307.                 final byte c1 = v1[k];
  308.                 final byte c2 = v2[k];
  309.                 if (c1 != c2)
  310.                 {
  311.                     return c1 - c2;
  312.                 }
  313.                 k++;
  314.             }
  315.         } else
  316.         {
  317.             while (n-- != 0)
  318.             {
  319.                 final byte c1 = v1[i++];
  320.                 final byte c2 = v2[j++];
  321.                 if (c1 != c2)
  322.                 {
  323.                     return c1 - c2;
  324.                 }
  325.             }
  326.         }
  327.         return len1 - len2;
  328.     }

  329.     @Override
  330.     public Object getSqlValue(final int column, final ResultSet resultSet)
  331.             throws SQLException, TypeCastException
  332.     {
  333.         logger.debug("getSqlValue(column={}, resultSet={}) - start", column,
  334.                 resultSet);
  335.         final byte[] rawValue = resultSet.getBytes(column);
  336.         final byte[] value = resultSet.wasNull() ? null : rawValue;
  337.         logger.debug("getSqlValue: column={}, value={}", column, value);
  338.         return value;
  339.     }

  340.     @Override
  341.     public void setSqlValue(final Object value, final int column,
  342.             final PreparedStatement statement)
  343.             throws SQLException, TypeCastException
  344.     {
  345.         logger.debug("setSqlValue(value={}, column={}, statement={}) - start",
  346.                 value, column, statement);

  347.         super.setSqlValue(value, column, statement);
  348.     }
  349. }