/*
* Copyright (C) 2015 Red Hat, Inc. and/or its affiliates.
*
* 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 org.jboss.errai.reflections.vfs;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import org.jboss.errai.reflections.ReflectionsException;
import org.jboss.errai.reflections.util.Utils;
import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* a simple virtual file system bridge
* <p>use the {@link org.jboss.errai.reflections.vfs.Vfs#fromURL(java.net.URL)} to get a {@link org.jboss.errai.reflections.vfs.Vfs.Dir}
* and than use {@link org.jboss.errai.reflections.vfs.Vfs.Dir#getFiles()} to iterate over it's {@link org.jboss.errai.reflections.vfs.Vfs.File}
* <p>for example:
* <pre>
* Vfs.Dir dir = Vfs.fromURL(url);
* Iterable<Vfs.File> files = dir.getFiles();
* for (Vfs.File file : files) {
* InputStream is = file.openInputStream();
* }
* </pre>
* <p>use {@link org.jboss.errai.reflections.vfs.Vfs#findFiles(java.util.Collection, com.google.common.base.Predicate)} to get an
* iteration of files matching given name predicate over given list of urls
* <p><p>{@link org.jboss.errai.reflections.vfs.Vfs#fromURL(java.net.URL)} uses static {@link org.jboss.errai.reflections.vfs.Vfs.DefaultUrlTypes} to resolves URLs
* and it can be plugged in with {@link org.jboss.errai.reflections.vfs.Vfs#addDefaultURLTypes(org.jboss.errai.reflections.vfs.Vfs.UrlType)} or {@link org.jboss.errai.reflections.vfs.Vfs#setDefaultURLTypes(java.util.List)}.
* <p>for example:
* <pre>
* Vfs.addDefaultURLTypes(new Vfs.UrlType() {
* public boolean matches(URL url) {
* return url.getProtocol().equals("http");
* }
* public Vfs.Dir createDir(final URL url) {
* return new HttpDir(url); //implement this type... (check out a naive implementation on VfsTest)
* }
* });
*
* Vfs.Dir dir = Vfs.fromURL(new URL("http://mirrors.ibiblio.org/pub/mirrors/maven2/org/slf4j/slf4j-api/1.5.6/slf4j-api-1.5.6.jar"));
* </pre>
*/
public abstract class Vfs {
private static List<UrlType> defaultUrlTypes = Lists.<UrlType>newArrayList(DefaultUrlTypes.values());
/**
* an abstract vfs dir
*/
public interface Dir {
String getPath();
Iterable<File> getFiles();
void close();
}
/**
* an abstract vfs file
*/
public interface File {
String getName();
String getRelativePath();
String getFullPath();
InputStream openInputStream() throws IOException;
}
/**
* a matcher and factory for a url
*/
public interface UrlType {
boolean matches(URL url);
Dir createDir(URL url);
}
/**
* the default url types that will be used when issuing {@link org.jboss.errai.reflections.vfs.Vfs#fromURL(java.net.URL)}
*/
public static List<UrlType> getDefaultUrlTypes() {
return defaultUrlTypes;
}
/**
* sets the static default url types. can be used to statically plug in urlTypes
*/
public static void setDefaultURLTypes(final List<UrlType> urlTypes) {
defaultUrlTypes = urlTypes;
}
/**
* add a static default url types. can be used to statically plug in urlTypes
*/
public static void addDefaultURLTypes(UrlType urlType) {
defaultUrlTypes.add(urlType);
}
/**
* tries to create a Dir from the given url, using the defaultUrlTypes
*/
public static Dir fromURL(final URL url) {
return fromURL(url, defaultUrlTypes);
}
/**
* tries to create a Dir from the given url, using the given urlTypes
*/
public static Dir fromURL(final URL url, final List<UrlType> urlTypes) {
for (UrlType type : urlTypes) {
if (type.matches(url)) {
try {
return type.createDir(url);
}
catch (Exception e) {
throw new ReflectionsException("could not create Dir using " + type.getClass().getName() + " from url " + url.toExternalForm(), e);
}
}
}
throw new ReflectionsException("could not create Vfs.Dir from url, no matching UrlType was found [" + url.toExternalForm() + "]\n" +
"Available types: " + urlTypes + "\n" +
"Do you need to add app server specific support to your deployment? (For example, errai-jboss-as-support for AS7)");
}
/**
* tries to create a Dir from the given url, using the given urlTypes
*/
public static Dir fromURL(final URL url, final UrlType... urlTypes) {
return fromURL(url, Lists.<UrlType>newArrayList(urlTypes));
}
/**
* return an iterable of all {@link org.jboss.errai.reflections.vfs.Vfs.File} in given urls, matching filePredicate
*/
public static Iterable<File> findFiles(final Collection<URL> inUrls, final Predicate<File> filePredicate) {
Iterable<File> result = new ArrayList<File>();
for (URL url : inUrls) {
Iterable<File> iterable = Iterables.filter(fromURL(url).getFiles(), filePredicate);
result = Iterables.concat(result, iterable);
}
return result;
}
/**
* return an iterable of all {@link org.jboss.errai.reflections.vfs.Vfs.File} in given urls, starting with given packagePrefix and matching nameFilter
*/
public static Iterable<File> findFiles(final Collection<URL> inUrls, final String packagePrefix, final Predicate<String> nameFilter) {
Predicate<File> fileNamePredicate = new Predicate<File>() {
public boolean apply(File file) {
String path = file.getRelativePath();
if (path.startsWith(packagePrefix)) {
String filename = path.substring(path.indexOf(packagePrefix) + packagePrefix.length());
return !Utils.isEmpty(filename) && nameFilter.apply(filename.substring(1));
}
else {
return false;
}
}
};
return findFiles(inUrls, fileNamePredicate);
}
/**
* default url types used by {@link org.jboss.errai.reflections.vfs.Vfs#fromURL(java.net.URL)}
* <p/>
* <p>jarfile - creates a {@link org.jboss.errai.reflections.vfs.ZipDir} over jar file
* <p>jarUrl - creates a {@link org.jboss.errai.reflections.vfs.ZipDir} over a jar url (contains ".jar!/" in it's name)
* <p>directory - creates a {@link org.jboss.errai.reflections.vfs.SystemDir} over a file system directory
* <p>vfsfile and vfszip - creates a {@link org.jboss.errai.reflections.vfs.ZipDir} over jboss vfs types
*/
public static enum DefaultUrlTypes implements UrlType {
jarfile {
public boolean matches(URL url) {
return url.getProtocol().equals("file") && url.toExternalForm().endsWith(".jar");
}
public Dir createDir(final URL url) {
return new ZipDir(url);
}
},
jarUrl {
public boolean matches(URL url) {
return url.toExternalForm().contains(".jar!");
}
public Dir createDir(URL url) {
return new ZipDir(url);
}
},
directory {
public boolean matches(URL url) {
return url.getProtocol().equals("file") && new java.io.File(normalizePath(url)).isDirectory();
}
public Dir createDir(final URL url) {
return new SystemDir(url);
}
},
vfsfile {
public boolean matches(URL url) {
return url.getProtocol().equals("vfsfile") && url.toExternalForm().endsWith(".jar");
}
public Dir createDir(URL url) {
return new ZipDir(url.toString().replaceFirst("vfsfile:", "file:"));
}
},
vfszip {
public boolean matches(URL url) {
return url.getProtocol().equals("vfszip") && url.toExternalForm().endsWith(".jar");
}
public Dir createDir(URL url) {
return new ZipDir(url.toString().replaceFirst("vfszip:", "file:"));
}
}
}
//
public static String normalizePath(final URL url) {
try {
return url.toURI().getPath();
}
catch (URISyntaxException e) {
String actualFilePath = url.getPath();
String nestedPath = "";
if (actualFilePath.startsWith("file:")) {
actualFilePath = actualFilePath.substring(5);
}
int nestedSeperator = actualFilePath.indexOf('!');
if (nestedSeperator != -1) {
nestedPath = actualFilePath.substring(nestedSeperator + 1);
actualFilePath = actualFilePath.substring(0, nestedSeperator);
if (nestedPath.equals("/")) {
nestedPath = "";
}
}
return actualFilePath;
}
}
}