/*
 * $Id$
 *
 * Copyright 2003-2004 The Apache Software Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.opensymphony.xwork2.util;

import com.opensymphony.xwork2.XWorkException;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.HashMap;
import java.util.Vector;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

/**
 * This class is an utility class that will search through the classpath
 * for files whose names match the given pattern. The filename is tested
 * using the given implementation of {@link com.opensymphony.xwork2.util.PatternMatcher} by default it 
 * uses {@link com.opensymphony.xwork2.util.WildcardHelper}
 */
public class ClassPathFinder {
	
	/**
     * The String pattern to test against.
     */
	private String pattern ;
	
	private int[] compiledPattern ;
	
	/**
     * The PatternMatcher implementation to use
     */
	private PatternMatcher<int[]> patternMatcher = new WildcardHelper();

	private Vector<String> compared = new Vector<>();
	
	/**
	 * @return the pattern in use
	 */
	public String getPattern() {
		return pattern;
	}

	/**
	 * @param pattern the String pattern for comparing filenames
	 */
	public void setPattern(String pattern) {
		this.pattern = pattern;
	}

	/**
     * Builds a {@link java.util.Vector} containing Strings which each name a file
     * who's name matches the pattern set by setPattern(String). The classpath is 
     * searched recursively, so use with caution.
     *
     * @return Vector&lt;String&gt; containing matching filenames
     */
	public Vector<String> findMatches() {
		Vector<String> matches = new Vector<>();
		URLClassLoader cl = getURLClassLoader();
		if (cl == null ) {
			throw new XWorkException("unable to attain an URLClassLoader") ;
		}
		URL[] parentUrls = cl.getURLs();
		compiledPattern = patternMatcher.compilePattern(pattern);
		for (URL url : parentUrls) {
			if (!"file".equals(url.getProtocol())) {
				continue ;
			}
			URI entryURI ;
			try {
				entryURI = url.toURI();
			} catch (URISyntaxException e) {
				continue;
			}
			File entry = new File(entryURI);
			if (entry.isFile() && entry.toString().endsWith(".jar")) {
				try {
					ZipInputStream zip = new ZipInputStream(new FileInputStream(entry));
					for (ZipEntry zipEntry = zip.getNextEntry(); zipEntry != null; zipEntry = zip.getNextEntry()) {
						boolean doesMatch = patternMatcher.match(new HashMap<String, String>(), zipEntry.getName(), compiledPattern);
						if (doesMatch) {
							matches.add(zipEntry.getName());
						}
					}
				} catch (IOException e) {
					e.printStackTrace();
				}
			} else {
				Vector<String> results = checkEntries(entry.list(), entry, "");
				if (results != null) {
					matches.addAll(results);
				}
			}
		}
		return matches;
	}
	
	private Vector<String> checkEntries(String[] entries, File parent, String prefix) {
		
		if (entries == null ) {
			return null;
		}

		Vector<String> matches = new Vector<>();
		for (String listEntry : entries) {
			File tempFile ;
			if (!"".equals(prefix) ) {
				tempFile = new File(parent, prefix + "/" + listEntry);
			}
			else {
				tempFile = new File(parent, listEntry);
			}
			if (tempFile.isDirectory() && 
					!(".".equals(listEntry) || "..".equals(listEntry)) ) {
				if	(!"".equals(prefix) ) {
					matches.addAll(checkEntries(tempFile.list(), parent, prefix + "/" + listEntry));
				}
				else {
					matches.addAll(checkEntries(tempFile.list(), parent, listEntry));
				}
			}
			else {
				
				String entryToCheck ;
				if ("".equals(prefix)) {
					entryToCheck = listEntry ;
				}
				else {
					entryToCheck = prefix + "/" + listEntry ;
				}
				
				if (compared.contains(entryToCheck) ) {
					continue;
				}
				else {
					compared.add(entryToCheck) ;
				}
				
				boolean doesMatch = patternMatcher.match(new HashMap<String,String>(), entryToCheck, compiledPattern);
				if (doesMatch) {
					matches.add(entryToCheck);
				}
			}
		}
		return matches ;
	}

	/**
	 * @param patternMatcher the PatternMatcher implementation to use when comparing filenames
	 */
	public void setPatternMatcher(PatternMatcher<int[]> patternMatcher) {
		this.patternMatcher = patternMatcher;
	}

	private URLClassLoader getURLClassLoader() {
		URLClassLoader ucl = null;
		ClassLoader loader = Thread.currentThread().getContextClassLoader();
		
		if(! (loader instanceof URLClassLoader)) {
			loader = ClassPathFinder.class.getClassLoader();
			if (loader instanceof URLClassLoader) {
				ucl = (URLClassLoader) loader ;
			}
		}
		else {
			ucl = (URLClassLoader) loader;
		}
		
		return ucl ;
	}
}
