package org.seasar.jsf.runtime;

import java.io.InputStream;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLConnection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import org.seasar.framework.util.JarFileUtil;
import org.seasar.framework.util.JarURLConnectionUtil;
import org.seasar.framework.util.URLUtil;
import org.seasar.jsf.TaglibConfig;
import org.seasar.jsf.TaglibManager;
import org.seasar.jsf.exception.UriNotFoundRuntimeException;

/**
 * @author higa
 *  
 */
public class TaglibManagerImpl implements TaglibManager {

	private static final String FILE_PROTOCOL = "file:";

	private static final String JAR_PROTOCOL = "jar:";

	private static final String JAR_PROTOCOL_SUFFIX = "!/";

	private static final String JAR_FILE_SUFFIX = ".jar";

	private Map taglibConfigs = new HashMap();

	private TaglibConfigBuilder builder = new TaglibConfigBuilder();

	public void init() {
		scanJars(Thread.currentThread().getContextClassLoader());
	}
	
	public void destroy() {
		taglibConfigs.clear();
	}
	
	public TaglibConfig getTaglibConfig(String uri)
			throws UriNotFoundRuntimeException {
		
		TaglibConfig taglibConfig = (TaglibConfig) taglibConfigs.get(uri);
		if (taglibConfig == null) {
			throw new UriNotFoundRuntimeException(uri);
		}
		return taglibConfig;
	}

	public boolean hasTaglibConfig(String uri) {
		return taglibConfigs.containsKey(uri);
	}

	public void scanJars(ClassLoader classLoader) {
		while (classLoader != null) {
			if (classLoader instanceof URLClassLoader) {
				URL[] urls = ((URLClassLoader) classLoader).getURLs();
				for (int i = 0; i < urls.length; ++i) {
					URLConnection conn = URLUtil.openConnection(urls[i]);
					if (conn instanceof JarURLConnection) {
						scanJar((JarURLConnection) conn);
					} else {
						String urlStr = urls[i].toString();
						if (urlStr.startsWith(FILE_PROTOCOL)
								&& urlStr.endsWith(JAR_FILE_SUFFIX)) {

							URL jarURL = URLUtil.create(JAR_PROTOCOL + urlStr
									+ JAR_PROTOCOL_SUFFIX);
							scanJar((JarURLConnection) URLUtil
									.openConnection(jarURL));
						}
					}
				}
			}
			classLoader = classLoader.getParent();
		}
	}

	public void scanJar(JarURLConnection conn) {
		conn.setUseCaches(false);
		JarFile jarFile = JarURLConnectionUtil.getJarFile(conn);
		Enumeration entries = jarFile.entries();
		while (entries.hasMoreElements()) {
			JarEntry entry = (JarEntry) entries.nextElement();
			String name = entry.getName();
			if (!name.startsWith("META-INF/")) {
				continue;
			}
			if (!name.endsWith(".tld")) {
				continue;
			}
			InputStream is = JarFileUtil.getInputStream(jarFile, entry);
			try {
				scanTld(is);
			} finally {
				if (is != null) {
					try {
						is.close();
					} catch (Throwable ignore) {
					}
				}
			}
		}
	}
	
	public void scanTld(InputStream is) {
		TaglibConfig taglibConfig = builder.build(is);
		taglibConfigs.put(taglibConfig.getUri(), taglibConfig);
	}
}