package jp.cssj.cti.resolver;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.StringTokenizer;

/**
 * URIを操作するためのユーティリティです。
 * 
 * @author <a href="mailto:miyabe at gnn.co.jp">MIYABE Tatsuhiko </a>
 * @version $Id: SourceUtils.java,v 1.7 2006/07/06 01:41:36 harumanx Exp $
 */
public final class SourceUtils {
	private SourceUtils() {
		// unused
	}

	/**
	 * 相対URIを絶対URIに変換します。
	 * 
	 * @param _base
	 *            基底URI。
	 * @param _href
	 *            相対URI。
	 * @return 相対URIを絶対URIに変換したもの。
	 */
	public static String absolutize(String base, String href) {
		if (base == null) {
			throw new NullPointerException("baseにnullは許可されません。");
		}
		href = href.trim().replace('\\', '/');

		// hrefを分解する
		String hrefProto;
		if (href == null) {
			href = "";
			hrefProto = null;
		} else {
			int colon = href.indexOf(':');
			if (colon != -1) {
				int slash = href.lastIndexOf('/', colon);
				if (slash != -1) {
					colon = -1;
				}
			}
			if (colon != -1) {
				hrefProto = href.substring(0, colon + 1);
				href = href.substring(colon + 1);
			} else {
				hrefProto = null;
			}
		}

		// baseを分解する
		String baseQuery = "";
		String baseProto;
		{
			int sharp = base.indexOf('#');
			if (sharp != -1) {
				base = base.substring(0, sharp);
			}
			int quest = base.indexOf('?');
			if (quest != -1) {
				baseQuery = base.substring(quest);
				base = base.substring(0, quest);
			}
			int colon = base.indexOf(':');
			if (colon != -1) {
				int slash = base.lastIndexOf('/', colon);
				if (slash != -1) {
					colon = -1;
				}
			}
			if (colon != -1) {
				baseProto = base.substring(0, colon + 1);
				base = base.substring(colon + 1);
			} else {
				baseProto = null;
			}
		}

		// hrefのスキーマが異なるなら、そっちを使う
		if (hrefProto != null && !hrefProto.equals(baseProto)) {
			return hrefProto + normalize(href);
		}

		String proto = ((baseProto == null) ? "" : baseProto);

		// hrefが#か?で始まるなら、それをbaseに付加したものを使う
		if (href.length() > 0) {
			switch (href.charAt(0)) {
			case '#':
				return proto + base + baseQuery + href;
			case '?':
				return proto + base + href;
			}
		}

		// [//host][/...] 形式を分解
		String host;
		if (base.startsWith("//")) {
			int slash = base.indexOf('/', 2);
			if (slash != -1) {
				host = base.substring(slash == 2 ? 2 : 0, slash);
				base = base.substring(slash);
			} else {
				host = base;
				base = "";
			}
		} else {
			host = "";
		}

		if (base.length() == 0) {
			return proto + host + normalizeInternal(href);
		} else if (href.length() == 0) {
			return proto + normalizeInternal(base);
		} else if (href.charAt(0) == '/') {
			if (href.length() > 1 && href.charAt(1) == '/') {
				return proto + normalize(href);
			}
			return proto + host + normalizeInternal(href);
		}

		boolean slash = (base.charAt(base.length() - 1) == '/');

		StringBuffer b = new StringBuffer();
		b.append(base);
		if (!slash) {
			int lastSlash = base.lastIndexOf('/');
			if (lastSlash != -1) {
				b.delete(lastSlash + 1, base.length());
			} else {
				b.delete(0, base.length());
			}
		}
		b.append(href);
		return proto + host + normalizeInternal(b.toString());
	}

	/**
	 * URIを正規化します。 URIに含まれる'..','./'などを除去します。
	 * 
	 * @param uri
	 * @return
	 */
	public static String normalize(String uri) {
		if (uri == null) {
			throw new NullPointerException("uriにnullは許可されません。");
		}
		uri = uri.trim().replace('\\', '/');
		String proto;
		int colon = uri.indexOf(':');
		if (colon != -1) {
			int slash = uri.lastIndexOf('/', colon);
			if (slash != -1) {
				colon = -1;
			}
		}
		if (colon != -1) {
			proto = uri.substring(0, colon + 1);
			uri = uri.substring(colon + 1);
		} else {
			proto = "";
		}

		String host;
		if (uri.startsWith("//")) {
			int slash = uri.indexOf('/', 2);
			if (slash != -1) {
				host = uri.substring(slash == 2 ? 2 : 0, slash);
				uri = uri.substring(slash);
			} else {
				host = uri;
				uri = "";
			}
		} else {
			host = "";
		}
		return proto + host + normalizeInternal(uri);
	}

	private static String normalizeInternal(String uri) {
		if (uri.length() == 0) {
			return uri;
		}
		boolean leadingSlash = uri.startsWith("/");
		boolean isDir = (uri.charAt(uri.length() - 1) == '/');
		StringTokenizer st = new StringTokenizer(uri, "/");
		LinkedList clean = new LinkedList();
		while (st.hasMoreTokens()) {
			String token = st.nextToken();
			if ("..".equals(token)) {
				if (!clean.isEmpty() && !"..".equals(clean.getLast())) {
					clean.removeLast();
					if (!st.hasMoreTokens()) {
						isDir = true;
					}
				}
			} else if (!".".equals(token) && !"".equals(token)) {
				clean.add(token);
			}
		}
		StringBuffer sb = new StringBuffer();
		if (leadingSlash) {
			sb.append('/');
		}
		for (Iterator it = clean.iterator(); it.hasNext();) {
			sb.append(it.next());
			if (it.hasNext()) {
				sb.append('/');
			}
		}
		if (isDir && sb.length() > 0 && sb.charAt(sb.length() - 1) != '/') {
			sb.append('/');
		}
		return sb.toString();
	}
	//
	// /**
	// * 絶対URIを相対URIに変換します。
	// *
	// * @param from
	// * リンク元
	// * @param to
	// * リンク先
	// * @return
	// */
	// public static String relativize(String from, String to) {
	// if (("".equals(from)) || (from == null))
	// return to;
	// if (from.charAt(from.length() - 1) != '/')
	// from += "/";
	// if (to.startsWith(from)) {
	// // resource is direct descentant
	// return to.substring(from.length());
	// } else {
	// // resource is not direct descendant
	// int index = SourceUtils.matchStrings(from, to);
	// if (index > 0 && from.charAt(index - 1) != '/') {
	// index = from.substring(0, index).lastIndexOf('/');
	// index++;
	// }
	// String pathDiff = from.substring(index);
	// String resource = to.substring(index);
	// int levels = SourceUtils.count(pathDiff, '/');
	// StringBuffer b = new StringBuffer();
	// for (int i = 0; i < levels; i++) {
	// b.append("../");
	// }
	// b.append(resource);
	// return b.toString();
	// }
	// }
	//
	// private static int matchStrings(String a, String b) {
	// int i;
	// char[] ca = a.toCharArray();
	// char[] cb = b.toCharArray();
	// int len = (ca.length < cb.length) ? ca.length : cb.length;
	//
	// for (i = 0; i < len; i++) {
	// if (ca[i] != cb[i])
	// break;
	// }
	//
	// return i;
	// }
	//
	// private static int count(String str, char c) {
	// int index = 0;
	// char[] chars = str.toCharArray();
	// for (int i = 0; i < chars.length; i++) {
	// if (chars[i] == c)
	// index++;
	// }
	// return index;
	// }
}
