February 19, 2013

Wildcard Character

Wildcard Character(The pattern for the wild card may be *, ? or   * a combination of *,? and alphanumeric character.)

Introduction

Have you ever searched for files and folders in Windows operating system? Generally most of the users try to search various *.doc files, *.pdf files, etc. We generally search by giving *.doc or *.pdf in the text box of the search window of the Windows OS. Of course, Windows OS provides some other more specific search utilities. However this kind of search is used in many applications. If an application provides this kind of facility, it is considered as the best feature of that application.

Background

It is always important for an application as to how the user is going to provide a search condition. All kinds of search conditions are not always feasible. Generally it has been universally endorsed that a user can search based upon certain specifications. As far this specification is concerned, we generally search by providing wild card characters. The wild card characters are * and ?. There is a concept behind these characters. I hope you know about it. This kind of search specification is recurrent in various applications. If you are working on an application, there will be a text field where a user has to search the name of the persons by giving * or ? or a combination of both and alphanumeric characters. For example, if I have to search a name, I can give J* so that my out may be “John”, “Johnson” or anything that starts with “J”. There is another case where I have to search exactly for four characters, so I can give my search condition as ???? . However, the user can give any kind of combination to search the String pattern. In case of Java based applications, you can achieve this feature by using regular expression. But the formation of regular expression depends upon you. Sometimes we may find it difficult to form the exact regular expression to meet the expectation. However if the requirement is only to find out the match is a String based upon the wild card pattern, I have provided an example below. You can use it anywhere that you want to search a name based upon the wild card pattern.

Using the Code

The following class is a utility class that you can use as string finder for wild card pattern. For example, you have your original String as “John” and you want to find whether the original string matches with the wild pattern as “J*”. You can do it easily. I have provided the test harness class to test the functionality. You can test in various ways.
package com.ddlabs.core.util;
import java.util.Vector;

/**This class is a utility for finding
 * the String based upon the wild card
 * pattern. For example if the actual
 * String "John" and your wild card pattern
 * is "J*", it will return true.
 * @author Debadatta Mishra(PIKU)
 *
 */
public class WildCardStringFinder 
{
	/**
	 * String variable for wild card pattern 
	 */
	private String wildCardPatternString;
	/**
	 * Variable for the length of the wild card pattern
	 */
	private int wildCardPatternLength;
	/**
	 * Boolean variable to for checking wild cards,
	 * It is false by default.
	 */
	private boolean ignoreWildCards;
	/**
	 * Boolean variable to know whether the pattern
	 * has leading * or not.
	 */
	private boolean hasLeadingStar;
	/**
	 * Boolean variable to know whether the pattern
	 * has * at the end.
	 */
	private boolean hasTrailingStar;
	/**
	 * A String array to contain chars
	 */
	private String charSegments[];
	/**
	 * Variable to maintain the boundary of the String.
	 */
	private int charBound;
	
	/**
	 * Default constructor
	 */
	public WildCardStringFinder()
	{
		super();
		ignoreWildCards = false;
	}
	
	/**This is the public method which will be called to match a String
	 * with the wild card pattern.
	 * @param actualString of type String indicating the String to be matched
	 * @param wildCardString of type String indicating the wild card String
	 * @return true if matches
	 */
	public boolean isStringMatching( String actualString , String wildCardString )
	{
		wildCardPatternString = wildCardString;
		wildCardPatternLength = wildCardString.length();
		setWildCards();
		return doesMatch( actualString, 0, actualString.length() );
	}
	
	/**
	 * This method is used to set the wild cards.
	 * The pattern for the wild card may be *, ? or 
	 * a combination of *,? and alphanumeric character.
	 */
	private void setWildCards()
	{
		if(wildCardPatternString.startsWith("*"))
		{
			hasLeadingStar = true;
		}
		if (wildCardPatternString.endsWith("*") && wildCardPatternLength > 1 )
		{
			hasTrailingStar = true;
		}
		Vector temp = new Vector();
		int pos = 0;
		StringBuffer buf = new StringBuffer();
		while(pos < wildCardPatternLength) 
		{
			char c = wildCardPatternString.charAt(pos++);
			switch(c)
			{
			case 42: // It refers to *
				if(buf.length() > 0)
				{
					temp.addElement(buf.toString());
					charBound += buf.length();
					buf.setLength(0);
				}
				break;
			case 63: // It refers to ? 
				buf.append('\0');
				break;

			default:
				buf.append(c);
			break;
			}
		}
		if(buf.length() > 0)
		{
			temp.addElement(buf.toString());
			charBound += buf.length();
		}
		charSegments = new String[temp.size()];
		temp.copyInto(charSegments);
	}
	
	/**This is the actual method which makes comparison
	 * with the wild card pattern.
	 * @param text of type String indicating the actual String
	 * @param startPoint of type int indicating the start index
	 * of the String
	 * @param endPoint of type int indicating the end index of
	 * the String
	 * @return true if matches.
	 */
	private final boolean doesMatch(String text, int startPoint, int endPoint)
	{
		int textLength = text.length();
		
		if(startPoint > endPoint)
		{
			return false;
		}
		if(ignoreWildCards)
		{
			return endPoint - startPoint == wildCardPatternLength
			    && wildCardPatternString.regionMatches(false, 0, text,
			    startPoint, wildCardPatternLength);
		}
		int charCount = charSegments.length;
		if(charCount == 0 && ( hasLeadingStar || hasTrailingStar ) )
		{
			return true;
		}
		if(startPoint == endPoint)
		{
			return wildCardPatternLength == 0;
		}
		if(wildCardPatternLength == 0)
		{
			return startPoint == endPoint;
		}
		if(startPoint < 0)
		{
			startPoint = 0;
		}
		if(endPoint > textLength)
		{
			endPoint = textLength;
		}
		int currPosition = startPoint;
		int bound = endPoint - charBound;
		if(bound < 0)
		{
			return false;
		}
		int i = 0;
		String currString = charSegments[i];
		int currStringLength = currString.length();
		if( !hasLeadingStar )
		{
			if(!isExpressionMatching(text, startPoint, 
					currString, 0, currStringLength))
			{
				return false;
			}
			i++;
			currPosition += currStringLength;
		}
		if(charSegments.length == 1 && !hasLeadingStar && !hasTrailingStar)
		{
			return currPosition == endPoint;
		}
		for(; i < charCount; i++)
		{
			currString = charSegments[i];
			int k = currString.indexOf('\0');
			int currentMatch;
			currentMatch = getTextPosition(text, currPosition, 
							endPoint, currString);
			if(k < 0)
			{
				if(currentMatch < 0)
				{
					return false;
				}
			}
			currPosition = currentMatch + currString.length();
		}
		if(!hasTrailingStar && currPosition != endPoint)
		{
			int clen = currString.length();
			return isExpressionMatching(text, 
				endPoint - clen, currString, 0, clen);
		}
		return i == charCount;
	}
	
	/**This method finds the position of the String based upon the
	 * wild card pattern. It also considers some special case
	 * like *.* and ???.? and their combination.
	 * @param textString of type String indicating the String
	 * @param start of type int indicating the start index of the String
	 * @param end of type int indicating the end index of the String
	 * @param posString of type indicating the position after wild card
	 * @return the position of the String
	 */
	private final int getTextPosition(String textString, int start, 
						int end, String posString)
	{
		/*
		 * String after *
		 */
		int plen = posString.length();
		int max = end - plen;
		int position = -1;
		int i = textString.indexOf(posString, start);
		/*
		 * The following conditions are met for the
		 * special case where user give *.* 
		 */
		if( posString.equals("."))
		{
			position = 1;
		}
		if(i == -1 || i > max)
		{
			position = -1;
		}
		else
		{
			position = i;
		}
		return position;
	}

	/**This method is used to match the wild card with the String
	 * based upon the start and end index.
	 * @param textString of type String indicating the String
	 * @param stringStartIndex of type int indicating the start
	 * index of the String.
	 * @param patternString of type String indicating the pattern
	 * @param patternStartIndex of type int indicating the start index
	 * @param length of type int indicating the length of pattern
	 * @return true if matches otherwise false
	 */
	private boolean isExpressionMatching(String textString, int stringStartIndex,
			String patternString, int patternStartIndex, int length)
	{
		while(length-- > 0) 
		{
			char textChar = textString.charAt(stringStartIndex++);
			char patternChar = patternString.charAt(patternStartIndex++);
			if ((ignoreWildCards || patternChar != 0)
					&& patternChar != textChar
					&& (textChar != patternChar 
					&& textChar != patternChar))
			{
				return false;
			}
		}
		return true;
	}

}
The following class is a test harness class for the above class.
package com.ddlabs.core.util;

/**This is a test harness class to test
 * the wild card pattern on a particular
 * String.
 * @author Debadatta Mishra(PIKU)
 *
 */
public class WildCardTestharness
{
	public static void main(String[] args) 
	{
		String name = "John";
		String searchString = "John";
		
		WildCardStringFinder finder = new WildCardStringFinder();
		boolean flag = finder.isStringMatching(name, searchString);
		System.out.println(flag);
	}
}		

Ref: http://www.codeproject.com/Articles/28051/Match-your-String-with-Wild-Card-Characters

No comments:

Post a Comment

I'm certainly not an expert, but I'll try my hardest to explain what I do know and research what I don't know.

My Favorite Site's List

#update below script more than 500 posts