/*
	Реализация спецификаций CLDC версии 1.1 (JSR-139), MIDP версии 2.1 (JSR-118)
	и других спецификаций для функционирования компактных приложений на языке
	Java (мидлетов) в среде программного обеспечения Малик Эмулятор.

	Copyright © 2016, 2019 Малик Разработчик

	Это свободная программа: вы можете перераспространять ее и/или изменять
	ее на условиях Меньшей Стандартной общественной лицензии GNU в том виде,
	в каком она была опубликована Фондом свободного программного обеспечения;
	либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.

	Эта программа распространяется в надежде, что она будет полезной,
	но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА
	или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННЫХ ЦЕЛЕЙ. Подробнее см. в Меньшей Стандартной
	общественной лицензии GNU.

	Вы должны были получить копию Меньшей Стандартной общественной лицензии GNU
	вместе с этой программой. Если это не так, см.
	<https://www.gnu.org/licenses/>.
*/


package malik.emulator.midp.protocol;

import java.io.*;

public class URLParser extends Object
{
	public static final int EMPTY_PORT = -1;
	public static final String SEPARATOR = "://";
	protected static final int PROTOCOL = 0;
	protected static final int USER_INFO = 1;
	protected static final int HOST_NAME = 2;
	protected static final int PATH = 3;
	protected static final int QUERY = 4;


	protected int port;
	protected String[] components;
	protected ByteArrayOutputStream data;

	public URLParser()
	{
		this.components = new String[5];
		this.data = new ByteArrayOutputStream();
	}

	public void parse(String url)
	{
		int i;
		int len;
		String[] c;
		if(url == null)
		{
			throw new IllegalArgumentException("Connector.open: " +
					"параметр url равен нулевой ссылке.");
		}
		port = -1;
		for(i = (c = components).length; i-- > 0; )
		{
			c[i] = null;
		}
		if((i = parsePath(url, parseTarget(url, parseSeparator(url,
				parseProtocol(url, 0))))) < (len = url.length()) &&
				url.charAt(i) == '?')
		{
			i = parseQuery(url, i);
		}
		if(i < len)
		{
			throw new IllegalArgumentException("Connector.open: " +
					"недопустимое значение параметра url.");
		}
	}

	public int getPort()
	{
		return port;
	}

	public String getHostName()
	{
		String[] c;
		return (c = components) == null ? null : c[HOST_NAME];
	}

	public String getProtocol()
	{
		String[] c;
		return (c = components) == null ? null : c[PROTOCOL];
	}

	public String getUserInfo()
	{
		String[] c;
		return (c = components) == null ? null : c[USER_INFO];
	}

	public String getPath()
	{
		String[] c;
		return (c = components) == null ? null : c[PATH];
	}

	public String getQuery()
	{
		String[] c;
		return (c = components) == null ? null : c[QUERY];
	}

	protected int parseProtocol(String url, int startIndex)
	{
		char c;
		int i;
		int len;
		ByteArrayOutputStream out;
		for((out = data).reset(), len = url.length(), i = startIndex; i <= len; i++)
		{
			c = i < len ? url.charAt(i) : 0;
			if(i == startIndex && ((c < 'A' || c > 'Z') && (c < 'a' || c > 'z')))
			{
				throw new IllegalArgumentException("Connector.open: " +
						"недопустимое значение параметра url.");
			}
			if(i > startIndex && ((c < '0' || c > '9') && (c < 'A' || c > 'Z') &&
					(c < 'a' || c > 'z') && c != '+' && c != '-' && c != '.'))
			{
				break;
			}
			out.write((int) c);
		}
		components[PROTOCOL] = out.toString();
		return i;
	}

	protected int parseSeparator(String url, int startIndex)
	{
		int i;
		int j;
		int len;
		if(url.length() - startIndex < (len = SEPARATOR.length()))
		{
			throw new IllegalArgumentException("Connector.open: " +
					"недопустимое значение параметра url.");
		}
		for(i = startIndex, j = 0; j < len; i++, j++)
		{
			if(url.charAt(i) != SEPARATOR.charAt(j))
			{
				throw new IllegalArgumentException("Connector.open: " +
						"недопустимое значение параметра url.");
			}
		}
		return i;
	}

	protected int parseUserInfo(String url, int startIndex)
	{
		char c;
		int i;
		int len;
		int val;
		ByteArrayOutputStream out;
		for((out = data).reset(), len = url.length(), i = startIndex; i <= len; i++)
		{
			if(((c = i < len ? url.charAt(i) : 0) >= '0' && c <= '9') ||
					(c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
					c == '-' || c == '_' || c == '.' || c == '!' || c == '~' || c == '*' ||
					c == '\'' || c == '(' || c == ')' || c == ';' || c == ':' || c == '&' ||
					c == '=' || c == '+' || c == '$' || c == ',')
			{
				out.write((int) c);
			}
			else if(c == '%')
			{
				if(++i > len - 2)
				{
					throw new IllegalArgumentException("Connector.open: " +
							"недопустимое значение параметра url.");
				}
				if((c = url.charAt(i++)) >= '0' && c <= '9')
				{
					val = c - '0';
				}
				else if(c >= 'A' && c <= 'F')
				{
					val = c - ('A' - 0x0a);
				}
				else if(c >= 'a' && c <= 'f')
				{
					val = c - ('a' - 0x0a);
				}
				else
				{
					throw new IllegalArgumentException("Connector.open: " +
							"недопустимое значение параметра url.");
				}
				if((c = url.charAt(i)) >= '0' && c <= '9')
				{
					val = (val << 4) | (c - '0');
				}
				else if(c >= 'A' && c <= 'F')
				{
					val = (val << 4) | (c - ('A' - 0x0a));
				}
				else if(c >= 'a' && c <= 'f')
				{
					val = (val << 4) | (c - ('a' - 0x0a));
				}
				else
				{
					throw new IllegalArgumentException("Connector.open: " +
							"недопустимое значение параметра url.");
				}
				out.write(val);
			}
			else
			{
				break;
			}
		}
		components[USER_INFO] = out.toString();
		return i;
	}

	protected int parseHostName(String url, int startIndex)
	{
		char c;
		int i;
		int len;
		ByteArrayOutputStream out;
		for((out = data).reset(), len = url.length(), i = startIndex; i <= len; i++)
		{
			if(((c = i < len ? url.charAt(i) : 0) >= '0' && c <= '9') ||
					(c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
					c == '+' || c == '-' || c == '.')
			{
				out.write((int) c);
			}
			else
			{
				break;
			}
		}
		components[HOST_NAME] = out.toString();
		return i;
	}

	protected int parsePort(String url, int startIndex)
	{
		char c;
		int i;
		int len;
		int val;
		for(val = 0, len = url.length(), i = startIndex; i <= len; i++)
		{
			c = i < len ? url.charAt(i) : 0;
			if(i == startIndex && (c < '0' || c > '9'))
			{
				throw new IllegalArgumentException("Connector.open: " +
						"недопустимое значение параметра url.");
			}
			if(i > startIndex && (c < '0' || c > '9'))
			{
				break;
			}
			if((val = (val * 10) + (c - '0')) > 0xffff)
			{
				throw new IllegalArgumentException("Connector.open: " +
						"недопустимое значение параметра url.");
			}
		}
		port = val;
		return i;
	}

	protected int parsePath(String url, int startIndex)
	{
		char c;
		int i;
		int len;
		int val;
		ByteArrayOutputStream out;
		for((out = data).reset(), len = url.length(), i = startIndex; i <= len; i++)
		{
			if(((c = i < len ? url.charAt(i) : 0) >= '0' && c <= '9') ||
					(c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
					c == '-' || c == '_' || c == '.' || c == '!' || c == '~' || c == '*' ||
					c == '\'' || c == '(' || c == ')' || c == ':' || c == '@' || c == '&' ||
					c == '=' || c == '+' || c == '$' || c == ',' || c == ';' || c == '/')
			{
				out.write((int) c);
			}
			else if(c == '%')
			{
				if(++i > len - 2)
				{
					throw new IllegalArgumentException("Connector.open: " +
							"недопустимое значение параметра url.");
				}
				if((c = url.charAt(i++)) >= '0' && c <= '9')
				{
					val = c - '0';
				}
				else if(c >= 'A' && c <= 'F')
				{
					val = c - ('A' - 0x0a);
				}
				else if(c >= 'a' && c <= 'f')
				{
					val = c - ('a' - 0x0a);
				}
				else
				{
					throw new IllegalArgumentException("Connector.open: " +
							"недопустимое значение параметра url.");
				}
				if((c = url.charAt(i)) >= '0' && c <= '9')
				{
					val = (val << 4) | (c - '0');
				}
				else if(c >= 'A' && c <= 'F')
				{
					val = (val << 4) | (c - ('A' - 0x0a));
				}
				else if(c >= 'a' && c <= 'f')
				{
					val = (val << 4) | (c - ('a' - 0x0a));
				}
				else
				{
					throw new IllegalArgumentException("Connector.open: " +
							"недопустимое значение параметра url.");
				}
				out.write(val);
			}
			else
			{
				break;
			}
		}
		components[PATH] = out.toString();
		return i;
	}

	protected int parseQuery(String url, int startIndex)
	{
		char c;
		int i;
		int len;
		int val;
		ByteArrayOutputStream out;
		for((out = data).reset(), len = url.length(), i = startIndex; i <= len; i++)
		{
			if(((c = i < len ? url.charAt(i) : 0) >= '0' && c <= '9') ||
					(c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
					c == '-' || c == '_' || c == '.' || c == '!' || c == '~' || c == '*' ||
					c == '\'' || c == '(' || c == ')' || c == ';' || c == '/' || c == '?' ||
					c == ':' || c == '@' || c == '&' || c == '=' || c == '+' || c == '$' ||
					c == ',')
			{
				out.write((int) c);
			}
			else if(c == '%')
			{
				if(++i > len - 2)
				{
					throw new IllegalArgumentException("Connector.open: " +
							"недопустимое значение параметра url.");
				}
				if((c = url.charAt(i++)) >= '0' && c <= '9')
				{
					val = c - '0';
				}
				else if(c >= 'A' && c <= 'F')
				{
					val = c - ('A' - 0x0a);
				}
				else if(c >= 'a' && c <= 'f')
				{
					val = c - ('a' - 0x0a);
				}
				else
				{
					throw new IllegalArgumentException("Connector.open: " +
							"недопустимое значение параметра url.");
				}
				if((c = url.charAt(i)) >= '0' && c <= '9')
				{
					val = (val << 4) | (c - '0');
				}
				else if(c >= 'A' && c <= 'F')
				{
					val = (val << 4) | (c - ('A' - 0x0a));
				}
				else if(c >= 'a' && c <= 'f')
				{
					val = (val << 4) | (c - ('a' - 0x0a));
				}
				else
				{
					throw new IllegalArgumentException("Connector.open: " +
							"недопустимое значение параметра url.");
				}
				out.write(val);
			}
			else
			{
				break;
			}
		}
		components[QUERY] = out.toString();
		return i;
	}

	protected int parseTarget(String url, int startIndex)
	{
		char c;
		int i;
		int len;
		int atIndex = -1;
		int colonIndex = -1;
		int finishIndex;
		for(len = url.length(), i = startIndex; i <= len; i++)
		{
			if((c = i < len ? url.charAt(i) : 0) == '/' || c == '?' || c == 0)
			{
				break;
			}
			switch(c)
			{
			case '@':
				atIndex = i;
				break;
			case ':':
				colonIndex = i;
				break;
			}
		}
		if(colonIndex < atIndex)
		{
			colonIndex = -1;
		}
		if(atIndex >= 0)
		{
			if(colonIndex >= 0)
			{
				if(parseUserInfo(url, startIndex) != atIndex ||
						parseHostName(url, atIndex + 1) != colonIndex)
				{
					throw new IllegalArgumentException("Connector.open: " +
							"недопустимое значение параметра url.");
				}
				finishIndex = parsePort(url, colonIndex + 1);
			} else
			{
				if(parseUserInfo(url, startIndex) != atIndex)
				{
					throw new IllegalArgumentException("Connector.open: " +
							"недопустимое значение параметра url.");
				}
				finishIndex = parseHostName(url, atIndex + 1);
			}
		} else
		{
			if(colonIndex >= 0)
			{
				if(parseHostName(url, startIndex) != colonIndex)
				{
					throw new IllegalArgumentException("Connector.open: " +
							"недопустимое значение параметра url.");
				}
				finishIndex = parsePort(url, colonIndex + 1);
			} else
			{
				finishIndex = parseHostName(url, startIndex);
			}
		}
		return finishIndex;
	}
}
