// Path: src/main/java/be/fror/common/base/Preconditions.java
// public static <T> T checkArgument(@Nullable T value, Predicate<? super T> predicate) {
//   if (predicate.test(value)) {
//     return value;
//   } else {
//     throw new IllegalArgumentException();
//   }
// }
// 
// Path: src/main/java/be/fror/common/base/Preconditions.java
// public static <T> T checkNotNull(@Nullable T reference) {
//   if (reference != null) {
//     return reference;
//   } else {
//     throw new NullPointerException();
//   }
// }
// 
// Path: src/main/java/be/fror/common/base/Preconditions.java
// public static void checkState(boolean expression) {
//   if (!expression) {
//     throw new IllegalStateException();
//   }
// }
// 
// Path: src/main/java/be/fror/common/base/Throwables.java
// public static RuntimeException propagate(Throwable throwable) {
//   propagateIfPossible(checkNotNull(throwable));
//   throw new RuntimeException(throwable);
// }
// 
// Path: src/main/java/be/fror/common/io/Resources.java
// public static ByteSource asByteSource(URL url) {
//   return new UrlByteSource(checkNotNull(url));
// }
// 
// Path: src/main/java/be/fror/common/io/Resources.java
// public static CharSource asCharSource(URL url, Charset charset) {
//   return asByteSource(url)
//       .asCharSource(charset);
// }


import static be.fror.common.base.Preconditions.checkArgument;
import static be.fror.common.base.Preconditions.checkNotNull;
import static be.fror.common.base.Preconditions.checkState;
import static be.fror.common.base.Throwables.propagate;
import static be.fror.common.io.Resources.asByteSource;
import static be.fror.common.io.Resources.asCharSource;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.nio.file.Files.isDirectory;
import static java.util.Arrays.asList;
import static java.util.Arrays.stream;
import static java.util.stream.Collectors.toList;
import com.google.common.base.CharMatcher;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.reflect.ClassPath;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.FileSystem;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.function.Predicate;
import java.util.jar.JarFile;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.annotation.concurrent.ThreadSafe;
/*
 * Copyright 2015 Olivier Grégoire.
 *
 * 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 be.fror.common.resource;





/**
 *
 * @author Olivier Grégoire
 */
@ThreadSafe
public final class ResourceLocator {

  // Notes:
  // * http://docs.spring.io/spring/docs/current/spring-framework-reference/html/resources.html
  private static final Set<String> supportedProtocols = Collections.unmodifiableSet(new LinkedHashSet<>(asList("file", "jar")));

  private final ImmutableMultimap<ClassLoader, ClassPath.ResourceInfo> resources;

  private ResourceLocator(ImmutableMultimap<ClassLoader, ClassPath.ResourceInfo> resources) {
    this.resources = resources;
  }

  /**
   * Locates resources matching the glob-pattern {@code namePattern}. Resources are
   * "folder-separated" with {@code /}.
   *
   * <p>
   * Pattern definition:
   * <ul>
   * <li>Follow the rules of {@link FileSystem#getPathMatcher(java.lang.String) }</li>
   * <li>remove the "glob:" prefix</li>
   * <li>regex is not supported</li>
   * <li>brackets expressions are not supported</li>
   * </ul>
   *
   * <p>
   * <b>Note:</b> the resource names don't start with {@code /}.
   *
   * <p>
   * Example of usage:
   *
   * <pre>{@code
   * locateResources("**&#47;*.properties") // Finds any resource whose name ends with ".properties".
   * locateResources("**&#47;*.{java,class}") // Finds any resource whose name ends with ".java" or ".class".
   * locateResources("**&#47;*.???") // Finds any resource whose name ends with a dot then three characters.
   * }</pre>
   *
   * @param namePattern
   * @return
   */
  public Stream<URL> locateResources(String namePattern) {
    checkNotNull(namePattern);
    Pattern p = Pattern.compile(globToRegex(namePattern));
    return doLocateResources((name) -> p.matcher(name).matches());
  }

  public Stream<URL> locateResources(Predicate<String> namePredicate) {
    checkNotNull(namePredicate);
    return doLocateResources(namePredicate);
  }

  /**
   * Returns a stream of resources which names match {@code namePattern} and that are transformed
   * into {@code T}s using {@code loader}.
   *
   * <p>
   * Using a terminal operation on the stream may throw an {@link UncheckedIOException} wrapping an
   * {@link IOException} thrown by {@code loader}.
   *
   * @param <T>
   * @param namePattern
   * @param loader
   * @return
   */
  public <T> Stream<T> loadResources(String namePattern, ResourceLoader<T> loader) {
// Path: src/main/java/be/fror/common/base/Preconditions.java
// public static <T> T checkArgument(@Nullable T value, Predicate<? super T> predicate) {
//   if (predicate.test(value)) {
//     return value;
//   } else {
//     throw new IllegalArgumentException();
//   }
// }
// 
// Path: src/main/java/be/fror/common/base/Preconditions.java
// public static <T> T checkNotNull(@Nullable T reference) {
//   if (reference != null) {
//     return reference;
//   } else {
//     throw new NullPointerException();
//   }
// }
// 
// Path: src/main/java/be/fror/common/base/Preconditions.java
// public static void checkState(boolean expression) {
//   if (!expression) {
//     throw new IllegalStateException();
//   }
// }
// 
// Path: src/main/java/be/fror/common/base/Throwables.java
// public static RuntimeException propagate(Throwable throwable) {
//   propagateIfPossible(checkNotNull(throwable));
//   throw new RuntimeException(throwable);
// }
// 
// Path: src/main/java/be/fror/common/io/Resources.java
// public static ByteSource asByteSource(URL url) {
//   return new UrlByteSource(checkNotNull(url));
// }
// 
// Path: src/main/java/be/fror/common/io/Resources.java
// public static CharSource asCharSource(URL url, Charset charset) {
//   return asByteSource(url)
//       .asCharSource(charset);
// }


// Path: src/main/java/be/fror/common/resource/ResourceLocator.java
import static be.fror.common.base.Preconditions.checkArgument;
import static be.fror.common.base.Preconditions.checkNotNull;
import static be.fror.common.base.Preconditions.checkState;
import static be.fror.common.base.Throwables.propagate;
import static be.fror.common.io.Resources.asByteSource;
import static be.fror.common.io.Resources.asCharSource;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.nio.file.Files.isDirectory;
import static java.util.Arrays.asList;
import static java.util.Arrays.stream;
import static java.util.stream.Collectors.toList;
import com.google.common.base.CharMatcher;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.reflect.ClassPath;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.FileSystem;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.function.Predicate;
import java.util.jar.JarFile;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.annotation.concurrent.ThreadSafe;

/*
 * Copyright 2015 Olivier Grégoire.
 *
 * 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 be.fror.common.resource;





/**
 *
 * @author Olivier Grégoire
 */
@ThreadSafe
public final class ResourceLocator {

  // Notes:
  // * http://docs.spring.io/spring/docs/current/spring-framework-reference/html/resources.html
  private static final Set<String> supportedProtocols = Collections.unmodifiableSet(new LinkedHashSet<>(asList("file", "jar")));

  private final ImmutableMultimap<ClassLoader, ClassPath.ResourceInfo> resources;

  private ResourceLocator(ImmutableMultimap<ClassLoader, ClassPath.ResourceInfo> resources) {
    this.resources = resources;
  }

  /**
   * Locates resources matching the glob-pattern {@code namePattern}. Resources are
   * "folder-separated" with {@code /}.
   *
   * <p>
   * Pattern definition:
   * <ul>
   * <li>Follow the rules of {@link FileSystem#getPathMatcher(java.lang.String) }</li>
   * <li>remove the "glob:" prefix</li>
   * <li>regex is not supported</li>
   * <li>brackets expressions are not supported</li>
   * </ul>
   *
   * <p>
   * <b>Note:</b> the resource names don't start with {@code /}.
   *
   * <p>
   * Example of usage:
   *
   * <pre>{@code
   * locateResources("**&#47;*.properties") // Finds any resource whose name ends with ".properties".
   * locateResources("**&#47;*.{java,class}") // Finds any resource whose name ends with ".java" or ".class".
   * locateResources("**&#47;*.???") // Finds any resource whose name ends with a dot then three characters.
   * }</pre>
   *
   * @param namePattern
   * @return
   */
  public Stream<URL> locateResources(String namePattern) {
    checkNotNull(namePattern);
    Pattern p = Pattern.compile(globToRegex(namePattern));
    return doLocateResources((name) -> p.matcher(name).matches());
  }

  public Stream<URL> locateResources(Predicate<String> namePredicate) {
    checkNotNull(namePredicate);
    return doLocateResources(namePredicate);
  }

  /**
   * Returns a stream of resources which names match {@code namePattern} and that are transformed
   * into {@code T}s using {@code loader}.
   *
   * <p>
   * Using a terminal operation on the stream may throw an {@link UncheckedIOException} wrapping an
   * {@link IOException} thrown by {@code loader}.
   *
   * @param <T>
   * @param namePattern
   * @param loader
   * @return
   */
  public <T> Stream<T> loadResources(String namePattern, ResourceLoader<T> loader) {
    return locateResources(namePattern).map(url -> loader.uncheckedLoad(asByteSource(url)));