package com.one;

import com.classpath.util.Characters;
import java.util.Vector;
import java.util.Enumeration;

/**
 * Класс для сравнения строк на соответствие шаблону.
 *
 * В шаблонах возможно использование спецсимволов:
 * '*' - заменяет ноль или более любых символов,
 * ';' - служит для разделения нескольких шаблонов.
 *
 * @author SilentKnight
 */
public class StringPattern
{
	public static final char WILDCARD = '*';
	public static final char SEPARATOR = ';';
	
	protected String[] prefix;
	protected String[] suffix;
	protected Vector[] middle;

	protected boolean ignorecase;
	protected int count;

	protected int hashcode;
	
	/**
	 * Создать строковый шаблон.
	 *
	 * @param pattern собственно спецификатор шаблона
	 * @param ignorecase true, если не нужно учитывать регистр при сравнении
	 */
	public StringPattern(String pattern, boolean ignorecase)
	{
		this.ignorecase = ignorecase;
		
		if(ignorecase)
		{
			pattern = Characters.toLowerCase(pattern);
		}

		hashcode = pattern.hashCode();
		
		Vector patterns = tokenizeString(pattern, SEPARATOR);
		count = patterns.size();
		
		prefix = new String[count];
		suffix = new String[count];
		middle = new Vector[count];
		
		for(int i = 0; i < count; i++)
		{
			pattern = (String)patterns.elementAt(i);
			
			int index = pattern.indexOf(WILDCARD);
			
			if(index < 0)
			{
				prefix[i] = pattern;
				pattern = "";
			}
			else
			{
				prefix[i] = pattern.substring(0, index);
				pattern = pattern.substring(index + 1);
			}
			
			index = pattern.lastIndexOf(WILDCARD);
			
			if(index < 0)
			{
				suffix[i] = pattern;
				pattern = "";
			}
			else
			{
				suffix[i] = pattern.substring(index + 1, pattern.length());
				pattern = pattern.substring(0, index);
			}
			
			middle[i] = tokenizeString(pattern, WILDCARD);
		}
	}
	
	/**
	 * Проверить строку на соотвествие шаблону.
	 *
	 * Если шаблон состоит из нескольких частей (разделенных ';'), то строка
	 * проходит проверку, если она соответствует хотя бы одной части.
	 *
	 * @param str проверяемая строка
	 * @return true, ести строка соответствует шаблону или его части
	 */
	public boolean matchesWith(String str)
	{
		if(ignorecase)
		{
			str = Characters.toLowerCase(str);
		}
		
		for(int i = 0; i < count; i++)
		{
			boolean flag = str.startsWith(prefix[i]);
			
			if(flag)
			{
				str = str.substring(prefix[i].length());
				
				if(str.length() > 0)
				{
					flag = str.endsWith(suffix[i]);
					
					if(flag)
					{
						str = str.substring(0, str.length() - suffix[i].length());
						
						Enumeration mid = middle[i].elements();
						String cmp;
						int index;
						
						while(str.length() > 0 && mid.hasMoreElements())
						{
							cmp = (String)mid.nextElement();
							index = str.indexOf(cmp);
							
							if(index >= 0)
							{
								str = str.substring(index + cmp.length());
							}
							else
							{
								flag = false;
								break;
							}
						}
					}
				}
			}
			
			if(flag)
			{
				return true;
			}
		}
		
		return false;
	}

	public boolean equals(Object obj)
	{
		if(obj == null)
		{
			return false;
		}

		if(getClass() != obj.getClass())
		{
			return false;
		}

		final StringPattern other = (StringPattern)obj;

		if(this.hashcode != other.hashcode)
		{
			return false;
		}
		
		return true;
	}

	public int hashCode()
	{
		return hashcode;
	}
	
	/**
	 * Разбить строку на подстроки.
	 *
	 * Несколько последовательных разделителей считаются за один.
	 * В результат разбиения разделители не включаюся.
	 *
	 * @param str разделяемая строка
	 * @param sep символ-разделитель
	 * @return вектор с результатами разбиения
	 */
	public static Vector tokenizeString(String str, char sep)
	{
		Vector res = new Vector();
		int index;
		
		while(str.length() > 0)
		{
			index = str.indexOf(sep);
			
			if(index > 0)
			{
				res.addElement(str.substring(0, index));
				str = str.substring(index + 1);
			}
			else if(index == 0)
			{
				str = str.substring(1);
			}
			else
			{
				res.addElement(str);
				break;
			}
		}
		
		return res;
	}
}
