package org.apache.logging.log4j.core.config.plugins.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Arrays;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.util.Charsets;
import org.apache.logging.log4j.core.util.Loader;
import org.apache.logging.log4j.status.StatusLogger;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.wiring.BundleWiring;

public class ResolverUtil {
   private static final Logger LOGGER = StatusLogger.getLogger();
   private static final String VFSZIP = "vfszip";
   private static final String BUNDLE_RESOURCE = "bundleresource";
   private final Set<Class<?>> classMatches = new HashSet();
   private final Set<URI> resourceMatches = new HashSet();
   private ClassLoader classloader;

   public Set<Class<?>> getClasses() {
      return this.classMatches;
   }

   public Set<URI> getResources() {
      return this.resourceMatches;
   }

   public ClassLoader getClassLoader() {
      return this.classloader != null ? this.classloader : (this.classloader = Loader.getClassLoader(ResolverUtil.class, (Class)null));
   }

   public void setClassLoader(ClassLoader classloader) {
      this.classloader = classloader;
   }

   public void find(ResolverUtil.Test test, String... packageNames) {
      if (packageNames != null) {
         String[] arr$ = packageNames;
         int len$ = packageNames.length;

         for(int i$ = 0; i$ < len$; ++i$) {
            String pkg = arr$[i$];
            this.findInPackage(test, pkg);
         }

      }
   }

   public void findInPackage(ResolverUtil.Test test, String packageName) {
      packageName = packageName.replace('.', '/');
      ClassLoader loader = this.getClassLoader();

      Enumeration urls;
      try {
         urls = loader.getResources(packageName);
      } catch (IOException var16) {
         LOGGER.warn((String)("Could not read package: " + packageName), (Throwable)var16);
         return;
      }

      while(urls.hasMoreElements()) {
         try {
            URL url = (URL)urls.nextElement();
            String urlPath = this.extractPath(url);
            LOGGER.info("Scanning for classes in [" + urlPath + "] matching criteria: " + test);
            if ("vfszip".equals(url.getProtocol())) {
               String path = urlPath.substring(0, urlPath.length() - packageName.length() - 2);
               URL newURL = new URL(url.getProtocol(), url.getHost(), path);
               JarInputStream stream = new JarInputStream(newURL.openStream());

               try {
                  this.loadImplementationsInJar(test, packageName, path, stream);
               } finally {
                  this.close(stream, newURL);
               }
            } else if ("bundleresource".equals(url.getProtocol())) {
               this.loadImplementationsInBundle(test, packageName);
            } else {
               File file = new File(urlPath);
               if (file.isDirectory()) {
                  this.loadImplementationsInDirectory(test, packageName, file);
               } else {
                  this.loadImplementationsInJar(test, packageName, file);
               }
            }
         } catch (IOException var15) {
            LOGGER.warn((String)"could not read entries", (Throwable)var15);
         }
      }

   }

   String extractPath(URL url) throws UnsupportedEncodingException {
      String urlPath = url.getPath();
      if (urlPath.startsWith("jar:")) {
         urlPath = urlPath.substring(4);
      }

      if (urlPath.startsWith("file:")) {
         urlPath = urlPath.substring(5);
      }

      if (urlPath.indexOf(33) > 0) {
         urlPath = urlPath.substring(0, urlPath.indexOf(33));
      }

      String protocol = url.getProtocol();
      List<String> neverDecode = Arrays.asList("vfszip", "bundleresource");
      if (neverDecode.contains(protocol)) {
         return urlPath;
      } else if ((new File(urlPath)).exists()) {
         return urlPath;
      } else {
         urlPath = URLDecoder.decode(urlPath, Charsets.UTF_8.name());
         return urlPath;
      }
   }

   private void loadImplementationsInBundle(ResolverUtil.Test test, String packageName) {
      BundleWiring wiring = (BundleWiring)FrameworkUtil.getBundle(ResolverUtil.class).adapt(BundleWiring.class);
      Collection<String> list = wiring.listResources(packageName, "*.class", 1);
      Iterator i$ = list.iterator();

      while(i$.hasNext()) {
         String name = (String)i$.next();
         this.addIfMatching(test, name);
      }

   }

   private void loadImplementationsInDirectory(ResolverUtil.Test test, String parent, File location) {
      File[] files = location.listFiles();
      if (files != null) {
         File[] arr$ = files;
         int len$ = files.length;

         for(int i$ = 0; i$ < len$; ++i$) {
            File file = arr$[i$];
            StringBuilder builder = new StringBuilder();
            builder.append(parent).append('/').append(file.getName());
            String packageOrClass = parent == null ? file.getName() : builder.toString();
            if (file.isDirectory()) {
               this.loadImplementationsInDirectory(test, packageOrClass, file);
            } else if (this.isTestApplicable(test, file.getName())) {
               this.addIfMatching(test, packageOrClass);
            }
         }

      }
   }

   private boolean isTestApplicable(ResolverUtil.Test test, String path) {
      return test.doesMatchResource() || path.endsWith(".class") && test.doesMatchClass();
   }

   private void loadImplementationsInJar(ResolverUtil.Test test, String parent, File jarFile) {
      JarInputStream jarStream = null;

      try {
         jarStream = new JarInputStream(new FileInputStream(jarFile));
         this.loadImplementationsInJar(test, parent, jarFile.getPath(), jarStream);
      } catch (FileNotFoundException var10) {
         LOGGER.error((String)("Could not search jar file '" + jarFile + "' for classes matching criteria: " + test + " file not found"), (Throwable)var10);
      } catch (IOException var11) {
         LOGGER.error((String)("Could not search jar file '" + jarFile + "' for classes matching criteria: " + test + " due to an IOException"), (Throwable)var11);
      } finally {
         this.close(jarStream, jarFile);
      }

   }

   private void close(JarInputStream jarStream, Object source) {
      if (jarStream != null) {
         try {
            jarStream.close();
         } catch (IOException var4) {
            LOGGER.error("Error closing JAR file stream for {}", source, var4);
         }
      }

   }

   private void loadImplementationsInJar(ResolverUtil.Test test, String parent, String path, JarInputStream stream) {
      while(true) {
         try {
            JarEntry entry;
            if ((entry = stream.getNextJarEntry()) != null) {
               String name = entry.getName();
               if (!entry.isDirectory() && name.startsWith(parent) && this.isTestApplicable(test, name)) {
                  this.addIfMatching(test, name);
               }
               continue;
            }
         } catch (IOException var7) {
            LOGGER.error((String)("Could not search jar file '" + path + "' for classes matching criteria: " + test + " due to an IOException"), (Throwable)var7);
         }

         return;
      }
   }

   protected void addIfMatching(ResolverUtil.Test test, String fqn) {
      try {
         ClassLoader loader = this.getClassLoader();
         if (test.doesMatchClass()) {
            String externalName = fqn.substring(0, fqn.indexOf(46)).replace('/', '.');
            if (LOGGER.isDebugEnabled()) {
               LOGGER.debug("Checking to see if class " + externalName + " matches criteria [" + test + ']');
            }

            Class<?> type = loader.loadClass(externalName);
            if (test.matches(type)) {
               this.classMatches.add(type);
            }
         }

         if (test.doesMatchResource()) {
            URL url = loader.getResource(fqn);
            if (url == null) {
               url = loader.getResource(fqn.substring(1));
            }

            if (url != null && test.matches(url.toURI())) {
               this.resourceMatches.add(url.toURI());
            }
         }
      } catch (Throwable var6) {
         LOGGER.warn("Could not examine class '" + fqn, var6);
      }

   }

   public interface Test {
      boolean matches(Class<?> var1);

      boolean matches(URI var1);

      boolean doesMatchClass();

      boolean doesMatchResource();
   }
}
