/**
  * Creates a new URLClassPath for the given URLs. The URLs will be searched in the order specified
  * for classes and resources. A URL ending with a '/' is assumed to refer to a directory.
  * Otherwise, the URL is assumed to refer to a JAR file.
  *
  * @param urls the directory and JAR file URLs to search for classes and resources
  * @param factory the URLStreamHandlerFactory to use when creating new URLs
  */
 public URLClassPath(URL[] urls, URLStreamHandlerFactory factory) {
   for (int i = 0; i < urls.length; i++) {
     path.add(urls[i]);
   }
   push(urls);
   if (factory != null) {
     jarHandler = factory.createURLStreamHandler("jar");
   }
 }
 /*
  * Returns the Loader at the specified position in the URL search
  * path. The URLs are opened and expanded as needed. Returns null
  * if the specified index is out of range.
  */
 private synchronized Loader getLoader(int index) {
   // Expand URL search path until the request can be satisfied
   // or the URL stack is empty.
   while (loaders.size() < index + 1) {
     // Pop the next URL from the URL stack
     URL url;
     synchronized (urls) {
       if (urls.empty()) {
         return null;
       } else {
         url = (URL) urls.pop();
       }
     }
     // Skip this URL if it already has a Loader. (Loader
     // may be null in the case where URL has not been opened
     // but is referenced by a JAR index.)
     if (lmap.containsKey(url)) {
       continue;
     }
     // Otherwise, create a new Loader for the URL.
     Loader loader;
     try {
       loader = getLoader(url);
       // If the loader defines a local class path then add the
       // URLs to the list of URLs to be opened.
       URL[] urls = loader.getClassPath();
       if (urls != null) {
         push(urls);
       }
     } catch (IOException e) {
       // Silently ignore for now...
       continue;
     }
     // Finally, add the Loader to the search path.
     loaders.add(loader);
     lmap.put(url, loader);
   }
   return (Loader) loaders.get(index);
 }