package org.scannotation;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.StringTokenizer;

/**
 * Various functions to locate URLs to scan
 *
 * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
 * @version $Revision: 1 $
 */
public class ClasspathUrlFinder
{

   /**
    * Find the classpath URLs for a specific classpath resource.  The classpath URL is extracted
    * from loader.getResources() using the baseResource.
    *
    * @param baseResource
    * @return
    */
   public static URL[] findResourceBases(String baseResource, ClassLoader loader)
   {
      ArrayList<URL> list = new ArrayList<URL>();
      try
      {
         Enumeration<URL> urls = loader.getResources(baseResource);
         while (urls.hasMoreElements())
         {
            URL url = urls.nextElement();
            list.add(findResourceBase(url, baseResource));
         }
      }
      catch (IOException e)
      {
         throw new RuntimeException(e);
      }
      return list.toArray(new URL[list.size()]);
   }

   /**
    * Find the classpath URLs for a specific classpath resource.  The classpath URL is extracted
    * from loader.getResources() using the baseResource.
    *
    * @param baseResource
    * @return
    */
   public static URL[] findResourceBases(String baseResource)
   {
      return findResourceBases(baseResource, Thread.currentThread().getContextClassLoader());
   }

   private static URL findResourceBase(URL url, String baseResource)
   {
      String urlString = url.toString();
      int idx = urlString.lastIndexOf(baseResource);
      urlString = urlString.substring(0, idx);
      URL deployUrl = null;
      try
      {
         deployUrl = new URL(urlString);
      }
      catch (MalformedURLException e)
      {
         throw new RuntimeException(e);
      }
      return deployUrl;
   }

   /**
    * Find the classpath URL for a specific classpath resource.  The classpath URL is extracted
    * from Thread.currentThread().getContextClassLoader().getResource() using the baseResource.
    *
    * @param baseResource
    * @return
    */
   public static URL findResourceBase(String baseResource)
   {
      return findResourceBase(baseResource, Thread.currentThread().getContextClassLoader());
   }

   /**
    * Find the classpath URL for a specific classpath resource.  The classpath URL is extracted
    * from loader.getResource() using the baseResource.
    *
    * @param baseResource
    * @param loader
    * @return
    */
   public static URL findResourceBase(String baseResource, ClassLoader loader)
   {
      URL url = loader.getResource(baseResource);
      return findResourceBase(url, baseResource);
   }

   /**
    * Find the classpath for the particular class
    *
    * @param clazz
    * @return
    */
   public static URL findClassBase(Class clazz)
   {
      String resource = clazz.getName().replace('.', '/') + ".class";
      return findResourceBase(resource, clazz.getClassLoader());
   }

   /**
    * Uses the java.class.path system property to obtain a list of URLs that represent the CLASSPATH
    *
    * @return
    */
   public static URL[] findClassPaths()
   {
      List<URL> list = new ArrayList<URL>();
      String classpath = System.getProperty("java.class.path");
      StringTokenizer tokenizer = new StringTokenizer(classpath, File.pathSeparator);

      while (tokenizer.hasMoreTokens())
      {
         String path = tokenizer.nextToken();
         File fp = new File(path);
         if (!fp.exists()) throw new RuntimeException("File in java.class.path does not exist: " + fp);
         try
         {
            list.add(fp.toURL());
         }
         catch (MalformedURLException e)
         {
            throw new RuntimeException(e);
         }
      }
      return list.toArray(new URL[list.size()]);
   }

   /**
    * Uses the java.class.path system property to obtain a list of URLs that represent the CLASSPATH
    * <p/>
    * paths is used as a filter to only include paths that have the specific relative file within it
    *
    * @param paths comma list of files that should exist in a particular path
    * @return
    */
   public static URL[] findClassPaths(String... paths)
   {
      ArrayList<URL> list = new ArrayList<URL>();

      String classpath = System.getProperty("java.class.path");
      StringTokenizer tokenizer = new StringTokenizer(classpath, File.pathSeparator);
      for (int i = 0; i < paths.length; i++)
      {
         paths[i] = paths[i].trim();
      }

      while (tokenizer.hasMoreTokens())
      {
         String path = tokenizer.nextToken().trim();
         boolean found = false;
         for (String wantedPath : paths)
         {
            if (path.endsWith(File.separator + wantedPath))
            {
               found = true;
               break;
            }
         }
         if (!found) continue;
         File fp = new File(path);
         if (!fp.exists()) throw new RuntimeException("File in java.class.path does not exists: " + fp);
         try
         {
            list.add(fp.toURL());
         }
         catch (MalformedURLException e)
         {
            throw new RuntimeException(e);
         }
      }
      return list.toArray(new URL[list.size()]);
   }


}

