protected void scan(URL url) {
    Vfs.Dir dir = Vfs.fromURL(url);

    try {
      for (final Vfs.File file : dir.getFiles()) {
        // scan if inputs filter accepts file relative path or fqn
        Predicate<String> inputsFilter = configuration.getInputsFilter();
        String path = file.getRelativePath();
        String fqn = path.replace('/', '.');
        if (inputsFilter == null || inputsFilter.apply(path) || inputsFilter.apply(fqn)) {
          Object classObject = null;
          for (Scanner scanner : configuration.getScanners()) {
            try {
              if (scanner.acceptsInput(path) || scanner.acceptResult(fqn)) {
                classObject = scanner.scan(file, classObject);
              }
            } catch (Exception e) {
              if (log != null && log.isDebugEnabled())
                log.debug(
                    "could not scan file "
                        + file.getRelativePath()
                        + " in url "
                        + url.toExternalForm()
                        + " with scanner "
                        + scanner.getClass().getSimpleName(),
                    e);
            }
          }
        }
      }
    } finally {
      dir.close();
    }
  }
  /**
   * merges saved Reflections resources from the given input stream, using the serializer configured
   * in this instance's Configuration <br>
   * useful if you know the serialized resource location and prefer not to look it up the classpath
   */
  public Reflections collect(final InputStream inputStream) {
    try {
      merge(configuration.getSerializer().read(inputStream));
      if (log != null)
        log.info(
            "Reflections collected metadata from input stream using serializer "
                + configuration.getSerializer().getClass().getName());
    } catch (Exception ex) {
      throw new ReflectionsException("could not merge input stream", ex);
    }

    return this;
  }
  /**
   * constructs a Reflections instance and scan according to given {@link
   * org.reflections.Configuration}
   *
   * <p>it is preferred to use {@link org.reflections.util.ConfigurationBuilder}
   */
  public Reflections(final Configuration configuration) {
    this.configuration = configuration;
    store = new Store(configuration);

    if (configuration.getScanners() != null && !configuration.getScanners().isEmpty()) {
      // inject to scanners
      for (Scanner scanner : configuration.getScanners()) {
        scanner.setConfiguration(configuration);
        scanner.setStore(store.getOrCreate(scanner.getClass().getSimpleName()));
      }

      scan();
    }
  }
 private ClassLoader[] loaders() {
   return configuration.getClassLoaders();
 }
 /**
  * serialize to a given directory and filename
  *
  * <p>* it is preferred to specify a designated directory (for example META-INF/reflections), so
  * that it could be found later much faster using the load method
  *
  * <p>see the documentation for the save method on the configured {@link
  * org.reflections.serializers.Serializer}
  */
 public File save(final String filename) {
   return save(filename, configuration.getSerializer());
 }
  protected void scan() {
    if (configuration.getUrls() == null || configuration.getUrls().isEmpty()) {
      if (log != null) log.warn("given scan urls are empty. set urls in the configuration");
      return;
    }

    if (log != null && log.isDebugEnabled()) {
      log.debug("going to scan these urls:\n" + Joiner.on("\n").join(configuration.getUrls()));
    }

    long time = System.currentTimeMillis();
    int scannedUrls = 0;
    ExecutorService executorService = configuration.getExecutorService();
    List<Future<?>> futures = Lists.newArrayList();

    for (final URL url : configuration.getUrls()) {
      try {
        if (executorService != null) {
          futures.add(
              executorService.submit(
                  new Runnable() {
                    public void run() {
                      if (log != null && log.isDebugEnabled())
                        log.debug("[" + Thread.currentThread().toString() + "] scanning " + url);
                      scan(url);
                    }
                  }));
        } else {
          scan(url);
        }
        scannedUrls++;
      } catch (ReflectionsException e) {
        if (log != null && log.isWarnEnabled())
          log.warn("could not create Vfs.Dir from url. ignoring the exception and continuing", e);
      }
    }

    // todo use CompletionService
    if (executorService != null) {
      for (Future future : futures) {
        try {
          future.get();
        } catch (Exception e) {
          throw new RuntimeException(e);
        }
      }
    }

    time = System.currentTimeMillis() - time;

    if (log != null) {
      int keys = 0;
      int values = 0;
      for (String index : store.keySet()) {
        keys += store.get(index).keySet().size();
        values += store.get(index).size();
      }

      log.info(
          format(
              "Reflections took %d ms to scan %d urls, producing %d keys and %d values %s",
              time,
              scannedUrls,
              keys,
              values,
              executorService != null && executorService instanceof ThreadPoolExecutor
                  ? format(
                      "[using %d cores]",
                      ((ThreadPoolExecutor) executorService).getMaximumPoolSize())
                  : ""));
    }
  }