PatternMatcher.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.filter;

  22. import org.slf4j.Logger;
  23. import org.slf4j.LoggerFactory;

  24. import java.util.Set;
  25. import java.util.HashSet;
  26. import java.util.Iterator;

  27. /**
  28.  * @author Manuel Laflamme
  29.  * @since Apr 17, 2004
  30.  * @version $Revision$
  31.  */
  32. class PatternMatcher
  33. {

  34.     /**
  35.      * Logger for this class
  36.      */
  37.     private static final Logger logger = LoggerFactory.getLogger(PatternMatcher.class);

  38.     private final Set _acceptedNames = new HashSet();
  39.     private final Set _acceptedPatterns = new HashSet();

  40.     /**
  41.      * Add a new accepted pattern.
  42.      * The following wildcard characters are supported:
  43.      * '*' matches zero or more characters,
  44.      * '?' matches one character.
  45.      */
  46.     public void addPattern(String patternName)
  47.     {
  48.         logger.debug("addPattern(patternName={}) - start", patternName);

  49.         if (patternName.indexOf("*") != -1 || patternName.indexOf("?") != -1)
  50.         {
  51.             _acceptedPatterns.add(patternName);
  52.         }
  53.         else
  54.         {
  55.             _acceptedNames.add(patternName.toUpperCase());
  56.         }
  57.     }

  58.     public boolean isEmpty()
  59.     {
  60.         logger.debug("isEmpty() - start");

  61.         if (_acceptedNames.isEmpty() && _acceptedPatterns.isEmpty())
  62.         {
  63.             return true;
  64.         }

  65.         return false;
  66.     }

  67.     public boolean accept(String name)
  68.     {
  69.         logger.debug("accept(name={}) - start", name);

  70.         if (_acceptedNames.contains(name.toUpperCase()))
  71.         {
  72.             return true;
  73.         }

  74.         if (_acceptedPatterns.size() > 0)
  75.         {
  76.             for (Iterator it = _acceptedPatterns.iterator(); it.hasNext();)
  77.             {
  78.                 String pattern = (String)it.next();
  79.                 if (match(pattern, name, false))
  80.                 {
  81.                     return true;
  82.                 }
  83.             }
  84.         }

  85.         return false;
  86.     }

  87.     /**
  88.      * Matches a string against a pattern. The pattern contains two special
  89.      * characters:
  90.      * '*' which means zero or more characters,
  91.      * '?' which means one and only one character.
  92.      *
  93.      * @param pattern the (non-null) pattern to match against
  94.      * @param str     the (non-null) string that must be matched against the
  95.      *                pattern
  96.      *
  97.      * @return <code>true</code> when the string matches against the pattern,
  98.      *         <code>false</code> otherwise.
  99.      */
  100.     private boolean match(String pattern, String str, boolean isCaseSensitive)
  101.     {
  102.         if(logger.isDebugEnabled())
  103.             logger.debug("match(pattern={}, str={}, isCaseSensitive={}) - start",
  104.                     new Object[]{pattern, str, String.valueOf(isCaseSensitive)});

  105.         /* Following pattern matching code taken from the Apache Ant project. */

  106.         char[] patArr = pattern.toCharArray();
  107.         char[] strArr = str.toCharArray();
  108.         int patIdxStart = 0;
  109.         int patIdxEnd = patArr.length - 1;
  110.         int strIdxStart = 0;
  111.         int strIdxEnd = strArr.length - 1;
  112.         char ch;

  113.         boolean containsStar = false;
  114.         for (int i = 0; i < patArr.length; i++)
  115.         {
  116.             if (patArr[i] == '*')
  117.             {
  118.                 containsStar = true;
  119.                 break;
  120.             }
  121.         }

  122.         if (!containsStar)
  123.         {
  124.             // No '*'s, so we make a shortcut
  125.             if (patIdxEnd != strIdxEnd)
  126.             {
  127.                 return false; // Pattern and string do not have the same size
  128.             }
  129.             for (int i = 0; i <= patIdxEnd; i++)
  130.             {
  131.                 ch = patArr[i];
  132.                 if (ch != '?')
  133.                 {
  134.                     if (isCaseSensitive && ch != strArr[i])
  135.                     {
  136.                         return false;// Character mismatch
  137.                     }
  138.                     if (!isCaseSensitive && Character.toUpperCase(ch) !=
  139.                             Character.toUpperCase(strArr[i]))
  140.                     {
  141.                         return false; // Character mismatch
  142.                     }
  143.                 }
  144.             }
  145.             return true; // String matches against pattern
  146.         }

  147.         if (patIdxEnd == 0)
  148.         {
  149.             return true; // Pattern contains only '*', which matches anything
  150.         }

  151.         // Process characters before first star
  152.         while ((ch = patArr[patIdxStart]) != '*' && strIdxStart <= strIdxEnd)
  153.         {
  154.             if (ch != '?')
  155.             {
  156.                 if (isCaseSensitive && ch != strArr[strIdxStart])
  157.                 {
  158.                     return false;// Character mismatch
  159.                 }
  160.                 if (!isCaseSensitive && Character.toUpperCase(ch) !=
  161.                         Character.toUpperCase(strArr[strIdxStart]))
  162.                 {
  163.                     return false;// Character mismatch
  164.                 }
  165.             }
  166.             patIdxStart++;
  167.             strIdxStart++;
  168.         }
  169.         if (strIdxStart > strIdxEnd)
  170.         {
  171.             // All characters in the string are used. Check if only '*'s are
  172.             // left in the pattern. If so, we succeeded. Otherwise failure.
  173.             for (int i = patIdxStart; i <= patIdxEnd; i++)
  174.             {
  175.                 if (patArr[i] != '*')
  176.                 {
  177.                     return false;
  178.                 }
  179.             }
  180.             return true;
  181.         }

  182.         // Process characters after last star
  183.         while ((ch = patArr[patIdxEnd]) != '*' && strIdxStart <= strIdxEnd)
  184.         {
  185.             if (ch != '?')
  186.             {
  187.                 if (isCaseSensitive && ch != strArr[strIdxEnd])
  188.                 {
  189.                     return false;// Character mismatch
  190.                 }
  191.                 if (!isCaseSensitive && Character.toUpperCase(ch) !=
  192.                         Character.toUpperCase(strArr[strIdxEnd]))
  193.                 {
  194.                     return false;// Character mismatch
  195.                 }
  196.             }
  197.             patIdxEnd--;
  198.             strIdxEnd--;
  199.         }
  200.         if (strIdxStart > strIdxEnd)
  201.         {
  202.             // All characters in the string are used. Check if only '*'s are
  203.             // left in the pattern. If so, we succeeded. Otherwise failure.
  204.             for (int i = patIdxStart; i <= patIdxEnd; i++)
  205.             {
  206.                 if (patArr[i] != '*')
  207.                 {
  208.                     return false;
  209.                 }
  210.             }
  211.             return true;
  212.         }

  213.         // process pattern between stars. padIdxStart and patIdxEnd point
  214.         // always to a '*'.
  215.         while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd)
  216.         {
  217.             int patIdxTmp = -1;
  218.             for (int i = patIdxStart + 1; i <= patIdxEnd; i++)
  219.             {
  220.                 if (patArr[i] == '*')
  221.                 {
  222.                     patIdxTmp = i;
  223.                     break;
  224.                 }
  225.             }
  226.             if (patIdxTmp == patIdxStart + 1)
  227.             {
  228.                 // Two stars next to each other, skip the first one.
  229.                 patIdxStart++;
  230.                 continue;
  231.             }
  232.             // Find the pattern between padIdxStart & padIdxTmp in str between
  233.             // strIdxStart & strIdxEnd
  234.             int patLength = (patIdxTmp - patIdxStart - 1);
  235.             int strLength = (strIdxEnd - strIdxStart + 1);
  236.             int foundIdx = -1;
  237.             strLoop:
  238.             for (int i = 0; i <= strLength - patLength; i++)
  239.             {
  240.                 for (int j = 0; j < patLength; j++)
  241.                 {
  242.                     ch = patArr[patIdxStart + j + 1];
  243.                     if (ch != '?')
  244.                     {
  245.                         if (isCaseSensitive && ch != strArr[strIdxStart + i + j])
  246.                         {
  247.                             continue strLoop;
  248.                         }
  249.                         if (!isCaseSensitive && Character.toUpperCase(ch) !=
  250.                                 Character.toUpperCase(strArr[strIdxStart + i + j]))
  251.                         {
  252.                             continue strLoop;
  253.                         }
  254.                     }
  255.                 }

  256.                 foundIdx = strIdxStart + i;
  257.                 break;
  258.             }

  259.             if (foundIdx == -1)
  260.             {
  261.                 return false;
  262.             }

  263.             patIdxStart = patIdxTmp;
  264.             strIdxStart = foundIdx + patLength;
  265.         }

  266.         // All characters in the string are used. Check if only '*'s are left
  267.         // in the pattern. If so, we succeeded. Otherwise failure.
  268.         for (int i = patIdxStart; i <= patIdxEnd; i++)
  269.         {
  270.             if (patArr[i] != '*')
  271.             {
  272.                 return false;
  273.             }
  274.         }
  275.         return true;
  276.     }
  277.    
  278.    
  279.     public String toString()
  280.     {
  281.         final StringBuilder sb = new StringBuilder();
  282.         sb.append(getClass().getName()).append("[");
  283.         sb.append("_acceptedNames=").append(_acceptedNames);
  284.         sb.append(", _acceptedPatterns=").append(_acceptedPatterns);
  285.         sb.append("]");
  286.         return sb.toString();
  287.     }


  288. }