Embedded esoteric wildcard project to provide access to internal fields and rename methods to follow a more "standard" naming scheme

This commit is contained in:
nathan 2017-12-29 09:37:26 +01:00
parent b98d9b1c53
commit 4cee6c1d34
15 changed files with 1175 additions and 76 deletions

View File

@ -16,6 +16,8 @@
<orderEntry type="library" name="logging slf4j-api" level="application" />
<orderEntry type="library" name="dorkbox minlog_slf4j" level="application" />
<orderEntry type="library" name="dorkbox annotations" level="application" />
<orderEntry type="library" name="dorkbox console" level="application" />
<orderEntry type="library" name="dorkbox shell_executor" level="application" />
<orderEntry type="library" name="bouncyCastle bcpg-jdk15on" level="application" />
<orderEntry type="library" name="bouncyCastle bcpkix-jdk15on" level="application" />
<orderEntry type="library" name="bouncyCastle bcprov-jdk15on" level="application" />
@ -30,7 +32,6 @@
<SOURCES />
</library>
</orderEntry>
<orderEntry type="library" name="wildcard" level="application" />
<orderEntry type="library" name="javaparser" level="application" />
<orderEntry type="library" name="javatar" level="application" />
<orderEntry type="library" name="async-http-client" level="application" />
@ -39,4 +40,4 @@
<orderEntry type="library" name="yamlbeans" level="application" />
<orderEntry type="library" name="kryo" level="application" />
</component>
</module>
</module>

View File

@ -42,53 +42,6 @@ class Build {
} catch (Exception ignored) {
System.err.println("CRITICAL:: Can't load javaFX to the classpath URLS");
}
File runLocation = get();
if (runLocation != null) {
// we want to look for the libraries, because they are OFTEN going to be in the incorrect path.
// this is only necessary if they aren't correctly loaded.
try {
Class.forName("com.esotericsoftware.wildcard.Paths");
} catch (Exception e) {
// whoops. can't find it on the path
File parent = runLocation.getParentFile();
File libDir = new File(parent, "libs");
if (!libDir.isDirectory()) {
libDir = new File(parent.getParentFile(), "libs");
}
if (!libDir.isDirectory()) {
throw new RuntimeException("Unable to find the libs directory for execution: " + runLocation);
}
Class<?>[] parameters = new Class[] {URL.class};
Class<URLClassLoader> sysclass = URLClassLoader.class;
URLClassLoader sysloader = (URLClassLoader) ClassLoader.getSystemClassLoader();
try {
Method method = sysclass.getDeclaredMethod("addURL", parameters);
method.setAccessible(true);
// add lib dir jars
for (File f : libDir.listFiles()) {
final String name = f.getName();
if (!f.isDirectory() && f.canRead() && name.endsWith(".jar") && !name.contains("source") && !name.contains("src")) {
// System.err.println("adding url " + f.getAbsolutePath());
method.invoke(sysloader, new Object[] {f.toURI().toURL()});
}
}
// try to load the library again to make sure the libs loaded
Class.forName("com.esotericsoftware.wildcard.Paths");
} catch (Throwable t) {
t.printStackTrace();
throw new RuntimeException("Unable to load the libs directory for execution: " + libDir);
}
}
}
}
public static

View File

@ -10,9 +10,8 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import com.esotericsoftware.wildcard.Paths;
import dorkbox.build.util.BuildLog;
import dorkbox.build.util.wildcard.Paths;
import dorkbox.util.FileUtil;
import dorkbox.util.IO;
import dorkbox.util.OS;

View File

@ -37,8 +37,6 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import com.esotericsoftware.wildcard.Paths;
import dorkbox.annotation.AnnotationDefaults;
import dorkbox.annotation.AnnotationDetector;
import dorkbox.build.Project;
@ -50,6 +48,7 @@ import dorkbox.build.util.FileNotFoundRuntimeException;
import dorkbox.build.util.classloader.ByteClassloader;
import dorkbox.build.util.classloader.ClassByteIterator;
import dorkbox.build.util.jar.Pack200Util;
import dorkbox.build.util.wildcard.Paths;
import dorkbox.util.FileUtil;
import dorkbox.util.IO;
import dorkbox.util.LZMA;

View File

@ -3,13 +3,12 @@ package dorkbox.build;
import java.io.File;
import java.io.IOException;
import com.esotericsoftware.wildcard.Paths;
import dorkbox.build.util.BuildLog;
import dorkbox.build.util.PreJarAction;
import dorkbox.build.util.jar.JarOptions;
import dorkbox.build.util.jar.JarSigner;
import dorkbox.build.util.jar.JarUtil;
import dorkbox.build.util.wildcard.Paths;
@SuppressWarnings("unused")
public

View File

@ -15,7 +15,7 @@
*/
package dorkbox.build.util;
import com.esotericsoftware.wildcard.Paths;
import dorkbox.build.util.wildcard.Paths;
public
class CrossCompileClass {

View File

@ -28,11 +28,10 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.esotericsoftware.wildcard.Paths;
import dorkbox.BuildOptions;
import dorkbox.Builder;
import dorkbox.build.Project;
import dorkbox.build.util.wildcard.Paths;
import dorkbox.util.Base64Fast;
import dorkbox.util.IO;

View File

@ -17,9 +17,8 @@ package dorkbox.build.util;
import java.io.IOException;
import com.esotericsoftware.wildcard.Paths;
import dorkbox.Builder;
import dorkbox.build.util.wildcard.Paths;
public
class ShutdownHook implements Runnable {

View File

@ -15,14 +15,7 @@
*/
package dorkbox.build.util.classloader;
import com.esotericsoftware.wildcard.Paths;
import javax.tools.FileObject;
import javax.tools.ForwardingJavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.StandardLocation;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
@ -31,6 +24,15 @@ import java.net.URI;
import java.util.HashMap;
import java.util.Map.Entry;
import javax.tools.FileObject;
import javax.tools.ForwardingJavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.StandardLocation;
import dorkbox.build.util.wildcard.Paths;
public class JavaMemFileManager extends ForwardingJavaFileManager<StandardJavaFileManager> {
static class ClassMemFileObject extends SimpleJavaFileObject {

View File

@ -20,8 +20,7 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import com.esotericsoftware.wildcard.Paths;
import dorkbox.build.util.wildcard.Paths;
import dorkbox.license.License;
public class JarOptions {

View File

@ -55,7 +55,6 @@ import java.util.zip.ZipOutputStream;
import org.bouncycastle.crypto.digests.SHA512Digest;
import com.esotericsoftware.wildcard.Paths;
import com.ice.tar.TarEntry;
import com.ice.tar.TarInputStream;
@ -63,6 +62,7 @@ import dorkbox.BuildOptions;
import dorkbox.Builder;
import dorkbox.build.Project;
import dorkbox.build.util.BuildLog;
import dorkbox.build.util.wildcard.Paths;
import dorkbox.license.License;
import dorkbox.util.Base64Fast;
import dorkbox.util.FileUtil;
@ -386,6 +386,40 @@ class JarUtil {
return new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
}
/**
* @param inputFile file to get the contents from (excluding manifest info and directory names)
*
* @return list of files, null if there are problems
*/
public static
List<String> getJarContents(File inputFile) {
// by default, this will not have access to the manifest! (not that we care...)
// we will ALSO lose entry comments!
try {
JarInputStream jarInputStream = new JarInputStream(new FileInputStream(inputFile), false);
ArrayList<String> contents = new ArrayList<>();
JarEntry entry;
while ((entry = jarInputStream.getNextJarEntry()) != null) {
if (!entry.isDirectory()) {
String name = entry.getName();
contents.add(name);
}
}
// finish the stream that we have been writing to
IO.close(jarInputStream);
return contents;
} catch (IOException ignored) {
}
return null;
}
/**
* This will also install the specified licenses, and for JARs, this will ALSO normalize (pack+unpack) the jar
* <p/>
@ -475,15 +509,15 @@ class JarUtil {
int totalEntries = 0;
if (options.inputPaths != null) {
totalEntries += options.inputPaths.count();
totalEntries += options.inputPaths.size();
}
if (options.extraPaths != null) {
totalEntries += options.extraPaths.count();
totalEntries += options.extraPaths.size();
}
if (options.sourcePaths != null) {
totalEntries += options.sourcePaths.count();
totalEntries += options.sourcePaths.size();
}
BuildLog.println();
@ -697,7 +731,7 @@ class JarUtil {
// These files will MATCH the path hierarchy in the jar
///////////////////////////////////////////////
if (options.extraPaths != null) {
final int count = options.extraPaths.count();
final int count = options.extraPaths.size();
List<SortedFiles> sortList = new ArrayList<SortedFiles>(count);
if (count > 0) {
@ -728,9 +762,9 @@ class JarUtil {
// include the source code if possible
///////////////////////////////////////////////
if (options.sourcePaths != null && !options.sourcePaths.isEmpty()) {
List<SortedFiles> sortList = new ArrayList<SortedFiles>(options.sourcePaths.count());
List<SortedFiles> sortList = new ArrayList<SortedFiles>(options.sourcePaths.size());
BuildLog.println("\tAdding sources (" + options.sourcePaths.count() + " entries)...");
BuildLog.println("\tAdding sources (" + options.sourcePaths.size() + " entries)...");
fullPaths = options.sourcePaths.getPaths();
relativePaths = options.sourcePaths.getRelativePaths();

View File

@ -0,0 +1,296 @@
/*
* Copyright (c) 2009, Nathan Sweet
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* * Neither the name of Esoteric Software nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Modified by dorkbox, llc
*/
package dorkbox.build.util.wildcard;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
class GlobScanner {
private final File rootDir;
private final List<String> matches = new ArrayList(128);
public GlobScanner (File rootDir, List<String> includes, List<String> excludes) {
if (rootDir == null) throw new IllegalArgumentException("rootDir cannot be null.");
if (!rootDir.exists()) throw new IllegalArgumentException("Directory does not exist: " + rootDir);
if (!rootDir.isDirectory()) throw new IllegalArgumentException("File must be a directory: " + rootDir);
try {
rootDir = rootDir.getCanonicalFile();
} catch (IOException ex) {
throw new RuntimeException("OS error determining canonical path: " + rootDir, ex);
}
this.rootDir = rootDir;
if (includes == null) throw new IllegalArgumentException("includes cannot be null.");
if (excludes == null) throw new IllegalArgumentException("excludes cannot be null.");
if (includes.isEmpty()) includes.add("**");
List<Pattern> includePatterns = new ArrayList(includes.size());
for (String include : includes)
includePatterns.add(new Pattern(include));
List<Pattern> allExcludePatterns = new ArrayList(excludes.size());
for (String exclude : excludes)
allExcludePatterns.add(new Pattern(exclude));
scanDir(rootDir, includePatterns);
if (!allExcludePatterns.isEmpty()) {
// For each file, see if any exclude patterns match.
outerLoop:
//
for (Iterator matchIter = matches.iterator(); matchIter.hasNext();) {
String filePath = (String)matchIter.next();
List<Pattern> excludePatterns = new ArrayList(allExcludePatterns);
try {
// Shortcut for excludes that are "**/XXX", just check file name.
for (Iterator excludeIter = excludePatterns.iterator(); excludeIter.hasNext();) {
Pattern exclude = (Pattern)excludeIter.next();
if (exclude.values.length == 2 && exclude.values[0].equals("**")) {
exclude.incr();
String fileName = filePath.substring(filePath.lastIndexOf(File.separatorChar) + 1);
if (exclude.matches(fileName)) {
matchIter.remove();
continue outerLoop;
}
excludeIter.remove();
}
}
// Get the file names after the root dir.
String[] fileNames = filePath.split("\\" + File.separator);
for (String fileName : fileNames) {
for (Iterator excludeIter = excludePatterns.iterator(); excludeIter.hasNext();) {
Pattern exclude = (Pattern)excludeIter.next();
if (!exclude.matches(fileName)) {
excludeIter.remove();
continue;
}
exclude.incr(fileName);
if (exclude.wasFinalMatch()) {
// Exclude pattern matched.
matchIter.remove();
continue outerLoop;
}
}
// Stop processing the file if none of the exclude patterns matched.
if (excludePatterns.isEmpty()) continue outerLoop;
}
} finally {
for (Pattern exclude : allExcludePatterns)
exclude.reset();
}
}
}
}
private void scanDir (File dir, List<Pattern> includes) {
if (!dir.canRead()) return;
// See if patterns are specific enough to avoid scanning every file in the directory.
boolean scanAll = false;
for (Pattern include : includes) {
if (include.value.indexOf('*') != -1 || include.value.indexOf('?') != -1) {
scanAll = true;
break;
}
}
if (!scanAll) {
// If not scanning all the files, we know exactly which ones to include.
List matchingIncludes = new ArrayList(1);
for (Pattern include : includes) {
if (matchingIncludes.isEmpty())
matchingIncludes.add(include);
else
matchingIncludes.set(0, include);
process(dir, include.value, matchingIncludes);
}
} else {
// Scan every file.
for (String fileName : dir.list()) {
// Get all include patterns that match.
List<Pattern> matchingIncludes = new ArrayList(includes.size());
for (Pattern include : includes)
if (include.matches(fileName)) matchingIncludes.add(include);
if (matchingIncludes.isEmpty()) continue;
process(dir, fileName, matchingIncludes);
}
}
}
private void process (File dir, String fileName, List<Pattern> matchingIncludes) {
// Increment patterns that need to move to the next token.
boolean isFinalMatch = false;
List<Pattern> incrementedPatterns = new ArrayList();
for (Iterator iter = matchingIncludes.iterator(); iter.hasNext();) {
Pattern include = (Pattern)iter.next();
if (include.incr(fileName)) {
incrementedPatterns.add(include);
if (include.isExhausted()) iter.remove();
}
if (include.wasFinalMatch()) isFinalMatch = true;
}
File file = new File(dir, fileName);
if (isFinalMatch) {
int length = rootDir.getPath().length();
if (!rootDir.getPath().endsWith(File.separator)) length++; // Lose starting slash.
matches.add(file.getPath().substring(length));
}
if (!matchingIncludes.isEmpty() && file.isDirectory()) scanDir(file, matchingIncludes);
// Decrement patterns.
for (Pattern include : incrementedPatterns)
include.decr();
}
public List<String> matches () {
return matches;
}
public File rootDir () {
return rootDir;
}
static class Pattern {
String value;
final String[] values;
private int index;
Pattern (String pattern) {
pattern = pattern.replace('\\', '/');
pattern = pattern.replaceAll("\\*\\*[^/]", "**/*");
pattern = pattern.replaceAll("[^/]\\*\\*", "*/**");
// pattern = pattern.toLowerCase();
values = pattern.split("/");
value = values[0];
}
boolean matches (String fileName) {
if (value.equals("**")) return true;
// fileName = fileName.toLowerCase();
// Shortcut if no wildcards.
if (value.indexOf('*') == -1 && value.indexOf('?') == -1) return fileName.equals(value);
int i = 0, j = 0;
while (i < fileName.length() && j < value.length() && value.charAt(j) != '*') {
if (value.charAt(j) != fileName.charAt(i) && value.charAt(j) != '?') return false;
i++;
j++;
}
// If reached end of pattern without finding a * wildcard, the match has to fail if not same length.
if (j == value.length()) return fileName.length() == value.length();
int cp = 0;
int mp = 0;
while (i < fileName.length()) {
if (j < value.length() && value.charAt(j) == '*') {
if (j++ >= value.length()) return true;
mp = j;
cp = i + 1;
} else if (j < value.length() && (value.charAt(j) == fileName.charAt(i) || value.charAt(j) == '?')) {
j++;
i++;
} else {
j = mp;
i = cp++;
}
}
// Handle trailing asterisks.
while (j < value.length() && value.charAt(j) == '*')
j++;
return j >= value.length();
}
String nextValue () {
if (index + 1 == values.length) return null;
return values[index + 1];
}
boolean incr (String fileName) {
if (value.equals("**")) {
if (index == values.length - 1) return false;
incr();
if (matches(fileName))
incr();
else {
decr();
return false;
}
} else
incr();
return true;
}
void incr () {
index++;
if (index >= values.length)
value = null;
else
value = values[index];
}
void decr () {
index--;
if (index > 0 && values[index - 1].equals("**")) index--;
value = values[index];
}
void reset () {
index = 0;
value = values[0];
}
boolean isExhausted () {
return index >= values.length;
}
boolean isLast () {
return index >= values.length - 1;
}
boolean wasFinalMatch () {
return isExhausted() || (isLast() && value.equals("**"));
}
}
public static void main (String[] args) {
// System.out.println(new Paths("C:\\Java\\ls", "**"));
List<String> includes = new ArrayList();
includes.add("website/in*");
// includes.add("**/lavaserver/**");
List<String> excludes = new ArrayList();
// excludes.add("**/*.php");
// excludes.add("website/**/doc**");
long start = System.nanoTime();
List<String> files = new GlobScanner(new File(".."), includes, excludes).matches();
long end = System.nanoTime();
System.out.println(files.toString().replaceAll(", ", "\n").replaceAll("[\\[\\]]", ""));
System.out.println((end - start) / 1000000f);
}
}

View File

@ -0,0 +1,84 @@
/*
* Copyright (c) 2009, Nathan Sweet
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* * Neither the name of Esoteric Software nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Modified by dorkbox, llc
*/
package dorkbox.build.util.wildcard;
import java.io.File;
public final
class Path {
public final String dir;
public final String name;
public
Path(String dir, String name) {
this.dir = dir;
this.name = name;
}
public
String absolute() {
return dir + name;
}
public
File file() {
return new File(dir, name);
}
public
int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((dir == null) ? 0 : dir.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
public
boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
Path other = (Path) obj;
if (dir == null) {
if (other.dir != null) {
return false;
}
}
else if (!dir.equals(other.dir)) {
return false;
}
if (name == null) {
if (other.name != null) {
return false;
}
}
else if (!name.equals(other.name)) {
return false;
}
return true;
}
}

View File

@ -0,0 +1,604 @@
/*
* Copyright (c) 2009, Nathan Sweet
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* * Neither the name of Esoteric Software nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Modified by dorkbox, llc
*/
package dorkbox.build.util.wildcard;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
/**
* Collects filesystem paths using wildcards, preserving the directory structure. Copies, deletes, and zips paths.
*/
@SuppressWarnings({"Duplicates", "WeakerAccess"})
public
class Paths implements Iterable<Path> {
static private final Comparator<Path> LONGEST_TO_SHORTEST = new Comparator<Path>() {
public
int compare(Path s1, Path s2) {
return s2.absolute()
.length() - s1.absolute()
.length();
}
};
static private List<String> defaultGlobExcludes;
/**
* Sets the exclude patterns that will be used in addition to the excludes specified for all glob searches.
*/
static public
void setDefaultGlobExcludes(String... defaultGlobExcludes) {
Paths.defaultGlobExcludes = Arrays.asList(defaultGlobExcludes);
}
/**
* Deletes a directory and all files and directories it contains.
*/
static private
boolean deleteDirectory(File file) {
if (file.exists()) {
File[] files = file.listFiles();
for (int i = 0, n = files.length; i < n; i++) {
if (files[i].isDirectory()) {
deleteDirectory(files[i]);
}
else {
files[i].delete();
}
}
}
return file.delete();
}
final HashSet<Path> paths = new HashSet<Path>(32);
/**
* Creates an empty Paths object.
*/
public
Paths() {
}
/**
* Creates a Paths object and calls {@link #glob(String, String[])} with the specified arguments.
*/
public
Paths(File dir, String... patterns) {
glob(dir.getAbsolutePath(), patterns);
}
/**
* Creates a Paths object and calls {@link #glob(String, String[])} with the specified arguments.
*/
public
Paths(String dir, String... patterns) {
glob(dir, patterns);
}
/**
* Collects all files and directories in the specified directory matching the wildcard patterns.
*
* @param dir The directory containing the paths to collect. If it does not exist, no paths are collected. If null, "." is
* assumed.
* @param patterns The wildcard patterns of the paths to collect or exclude. Patterns may optionally contain wildcards
* represented by asterisks and question marks. If empty or omitted then the dir parameter is split on the "|"
* character, the first element is used as the directory and remaining are used as the patterns. If null, ** is
* assumed (collects all paths).<br>
* <br>
* A single question mark (?) matches any single character. Eg, something? collects any path that is named
* "something" plus any character.<br>
* <br>
* A single asterisk (*) matches any characters up to the next slash (/). Eg, *\*\something* collects any path that
* has two directories of any name, then a file or directory that starts with the name "something".<br>
* <br>
* A double asterisk (**) matches any characters. Eg, **\something\** collects any path that contains a directory
* named "something".<br>
* <br>
* A pattern starting with an exclamation point (!) causes paths matched by the pattern to be excluded, even if other
* patterns would select the paths.
*/
public
Paths glob(File dir, String... patterns) {
return glob(dir.getAbsolutePath(), patterns);
}
/**
* Collects all files and directories in the specified directory matching the wildcard patterns.
*
* @param dir The directory containing the paths to collect. If it does not exist, no paths are collected. If null, "." is
* assumed.
* @param patterns The wildcard patterns of the paths to collect or exclude. Patterns may optionally contain wildcards
* represented by asterisks and question marks. If empty or omitted then the dir parameter is split on the "|"
* character, the first element is used as the directory and remaining are used as the patterns. If null, ** is
* assumed (collects all paths).<br>
* <br>
* A single question mark (?) matches any single character. Eg, something? collects any path that is named
* "something" plus any character.<br>
* <br>
* A single asterisk (*) matches any characters up to the next slash (/). Eg, *\*\something* collects any path that
* has two directories of any name, then a file or directory that starts with the name "something".<br>
* <br>
* A double asterisk (**) matches any characters. Eg, **\something\** collects any path that contains a directory
* named "something".<br>
* <br>
* A pattern starting with an exclamation point (!) causes paths matched by the pattern to be excluded, even if other
* patterns would select the paths.
*/
public
Paths glob(String dir, String... patterns) {
if (dir == null) {
dir = ".";
}
if (patterns != null && patterns.length == 0) {
String[] split = dir.split("\\|");
if (split.length > 1) {
dir = split[0];
patterns = new String[split.length - 1];
for (int i = 1, n = split.length; i < n; i++) {
patterns[i - 1] = split[i];
}
}
}
File dirFile = new File(dir);
if (!dirFile.exists()) {
return this;
}
List<String> includes = new ArrayList<String>();
List<String> excludes = new ArrayList<String>();
if (patterns != null) {
for (String pattern : patterns) {
if (pattern.charAt(0) == '!') {
excludes.add(pattern.substring(1));
}
else {
includes.add(pattern);
}
}
}
if (includes.isEmpty()) {
includes.add("**");
}
if (defaultGlobExcludes != null) {
excludes.addAll(defaultGlobExcludes);
}
GlobScanner scanner = new GlobScanner(dirFile, includes, excludes);
String rootDir = scanner.rootDir()
.getPath()
.replace('\\', '/');
if (!rootDir.endsWith("/")) {
rootDir += '/';
}
for (String filePath : scanner.matches()) {
paths.add(new Path(rootDir, filePath));
}
return this;
}
/**
* Creates a Paths object and calls {@link #glob(String, List)} with the specified arguments.
*/
public
Paths(String dir, List<String> patterns) {
glob(dir, patterns);
}
/**
* Calls {@link #glob(String, String...)}.
*/
public
Paths glob(String dir, List<String> patterns) {
if (patterns == null) {
throw new IllegalArgumentException("patterns cannot be null.");
}
glob(dir, patterns.toArray(new String[patterns.size()]));
return this;
}
/**
* Collects all files and directories in the specified directory matching the regular expression patterns. This method is much
* slower than {@link #glob(String, String...)} because every file and directory under the specified directory must be
* inspected.
*
* @param dir The directory containing the paths to collect. If it does not exist, no paths are collected.
* @param patterns The regular expression patterns of the paths to collect or exclude. If empty or omitted then the dir
* parameter is split on the "|" character, the first element is used as the directory and remaining are used as the
* patterns. If null, ** is assumed (collects all paths).<br>
* <br>
* A pattern starting with an exclamation point (!) causes paths matched by the pattern to be excluded, even if other
* patterns would select the paths.
*/
public
Paths regex(String dir, String... patterns) {
if (dir == null) {
dir = ".";
}
if (patterns != null && patterns.length == 0) {
String[] split = dir.split("\\|");
if (split.length > 1) {
dir = split[0];
patterns = new String[split.length - 1];
for (int i = 1, n = split.length; i < n; i++) {
patterns[i - 1] = split[i];
}
}
}
File dirFile = new File(dir);
if (!dirFile.exists()) {
return this;
}
List<String> includes = new ArrayList<String>();
List<String> excludes = new ArrayList<String>();
if (patterns != null) {
for (String pattern : patterns) {
if (pattern.charAt(0) == '!') {
excludes.add(pattern.substring(1));
}
else {
includes.add(pattern);
}
}
}
if (includes.isEmpty()) {
includes.add(".*");
}
RegexScanner scanner = new RegexScanner(dirFile, includes, excludes);
String rootDir = scanner.rootDir()
.getPath()
.replace('\\', '/');
if (!rootDir.endsWith("/")) {
rootDir += '/';
}
for (String filePath : scanner.matches()) {
paths.add(new Path(rootDir, filePath));
}
return this;
}
/**
* Copies the files and directories to the specified directory.
*
* @return A paths object containing the paths of the new files.
*/
public
Paths copyTo(String destDir) throws IOException {
Paths newPaths = new Paths();
for (Path path : paths) {
File destFile = new File(destDir, path.name);
File srcFile = path.file();
if (srcFile.isDirectory()) {
destFile.mkdirs();
}
else {
destFile.getParentFile()
.mkdirs();
copyFile(srcFile, destFile);
}
newPaths.paths.add(new Path(destDir, path.name));
}
return newPaths;
}
/**
* Copies one file to another.
*/
static private
void copyFile(File in, File out) throws IOException {
FileInputStream sourceStream = new FileInputStream(in);
FileOutputStream destinationStream = new FileOutputStream(out);
FileChannel sourceChannel = sourceStream.getChannel();
FileChannel destinationChannel = destinationStream.getChannel();
sourceChannel.transferTo(0, sourceChannel.size(), destinationChannel);
sourceChannel.close();
sourceStream.close();
destinationChannel.close();
destinationStream.close();
}
/**
* Deletes all the files, directories, and any files in the directories.
*
* @return False if any file could not be deleted.
*/
public
boolean delete() {
boolean success = true;
List<Path> pathsCopy = new ArrayList<Path>(paths);
Collections.sort(pathsCopy, LONGEST_TO_SHORTEST);
for (File file : getFiles(pathsCopy)) {
if (file.isDirectory()) {
if (!deleteDirectory(file)) {
success = false;
}
}
else {
if (!file.delete()) {
success = false;
}
}
}
return success;
}
/**
* Returns the absolute paths delimited by commas.
*/
public
String toString() {
return toString(", ");
}
/**
* Returns the absolute paths delimited by the specified character.
*/
public
String toString(String delimiter) {
StringBuilder buffer = new StringBuilder(256);
for (String path : getPaths()) {
if (buffer.length() > 0) {
buffer.append(delimiter);
}
buffer.append(path);
}
return buffer.toString();
}
/**
* Returns the full paths.
*/
public
List<String> getPaths() {
ArrayList<String> stringPaths = new ArrayList<String>(paths.size());
for (File file : getFiles()) {
stringPaths.add(file.getPath());
}
return stringPaths;
}
/**
* Returns the paths as File objects.
*/
public
List<File> getFiles() {
return getFiles(new ArrayList<Path>(paths));
}
private
ArrayList<File> getFiles(List<Path> paths) {
ArrayList<File> files = new ArrayList<File>(paths.size());
int i = 0;
for (Path path : paths) {
files.add(path.file());
}
return files;
}
/**
* Compresses the files and directories specified by the paths into a new zip file at the specified location. If there are no
* paths or all the paths are directories, no zip file will be created.
*/
public
void zip(String destFile) throws IOException {
Paths zipPaths = filesOnly();
if (zipPaths.paths.isEmpty()) {
return;
}
byte[] buf = new byte[1024];
ZipOutputStream out = new ZipOutputStream(new FileOutputStream(destFile));
try {
for (Path path : zipPaths.paths) {
File file = path.file();
out.putNextEntry(new ZipEntry(path.name.replace('\\', '/')));
FileInputStream in = new FileInputStream(file);
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
in.close();
out.closeEntry();
}
} finally {
out.close();
}
}
/**
* Returns a Paths object containing the paths that are files.
*/
public
Paths filesOnly() {
Paths newPaths = new Paths();
for (Path path : paths) {
if (path.file()
.isFile()) {
newPaths.paths.add(path);
}
}
return newPaths;
}
/**
* Returns the number of paths.
*
* @return the number of paths
*/
public
int size() {
return paths.size();
}
/**
* Returns <tt>true</tt> if there are no paths
*
* @return <tt>true</tt> if there are no paths
*/
public
boolean isEmpty() {
return paths.isEmpty();
}
/**
* Returns a Paths object containing the paths that are files, as if each file were selected from its parent directory.
*/
public
Paths flatten() {
Paths newPaths = new Paths();
for (Path path : paths) {
File file = path.file();
if (file.isFile()) {
newPaths.paths.add(new Path(file.getParent(), file.getName()));
}
}
return newPaths;
}
/**
* Returns a Paths object containing the paths that are directories.
*/
public
Paths dirsOnly() {
Paths newPaths = new Paths();
for (Path path : paths) {
if (path.file()
.isDirectory()) {
newPaths.paths.add(path);
}
}
return newPaths;
}
/**
* Returns the portion of the path after the root directory where the path was collected.
*/
public
List<String> getRelativePaths() {
ArrayList<String> stringPaths = new ArrayList<String>(paths.size());
for (Path path : paths) {
stringPaths.add(path.name);
}
return stringPaths;
}
/**
* Returns the paths' filenames.
*/
public
List<String> getNames() {
ArrayList<String> stringPaths = new ArrayList<String>(paths.size());
for (File file : getFiles()) {
stringPaths.add(file.getName());
}
return stringPaths;
}
/**
* Adds a single path to this Paths object.
*/
public
Paths addFile(String fullPath) {
File file = new File(fullPath);
paths.add(new Path(file.getParent(), file.getName()));
return this;
}
/**
* Adds a single path to this Paths object.
*/
public
Paths add(String dir, String name) {
paths.add(new Path(dir, name));
return this;
}
/**
* Adds all paths from the specified Paths object to this Paths object.
*/
public
void add(Paths paths) {
this.paths.addAll(paths.paths);
}
/**
* Iterates over the absolute paths. The iterator supports the remove method.
*/
public
Iterator<Path> iterator() {
return new Iterator<Path>() {
private Iterator<Path> iter = paths.iterator();
public
boolean hasNext() {
return iter.hasNext();
}
public
void remove() {
iter.remove();
}
public
Path next() {
return iter.next();
}
};
}
/**
* Iterates over the paths as File objects. The iterator supports the remove method.
*/
public
Iterator<File> fileIterator() {
return new Iterator<File>() {
private Iterator<Path> iter = paths.iterator();
public
void remove() {
iter.remove();
}
public
File next() {
return iter.next()
.file();
}
public
boolean hasNext() {
return iter.hasNext();
}
};
}
}

View File

@ -0,0 +1,131 @@
/*
* Copyright (c) 2009, Nathan Sweet
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* * Neither the name of Esoteric Software nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Modified by dorkbox, llc
*/
package dorkbox.build.util.wildcard;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Pattern;
class RegexScanner {
public static
void main(String[] args) {
// System.out.println(new Paths("C:\\Java\\ls", "**"));
List<String> includes = new ArrayList();
includes.add("core[^T]+php");
// includes.add(".*/lavaserver/.*");
List<String> excludes = new ArrayList();
// excludes.add("website/**/doc**");
long start = System.nanoTime();
List<String> files = new RegexScanner(new File("..\\website\\includes"), includes, excludes).matches();
long end = System.nanoTime();
System.out.println(files.toString()
.replaceAll(", ", "\n")
.replaceAll("[\\[\\]]", ""));
System.out.println((end - start) / 1000000f);
}
public
List<String> matches() {
return matches;
}
private final File rootDir;
private final List<Pattern> includePatterns;
private final List<String> matches = new ArrayList(128);
public
RegexScanner(File rootDir, List<String> includes, List<String> excludes) {
if (rootDir == null) {
throw new IllegalArgumentException("rootDir cannot be null.");
}
if (!rootDir.exists()) {
throw new IllegalArgumentException("Directory does not exist: " + rootDir);
}
if (!rootDir.isDirectory()) {
throw new IllegalArgumentException("File must be a directory: " + rootDir);
}
try {
rootDir = rootDir.getCanonicalFile();
} catch (IOException ex) {
throw new RuntimeException("OS error determining canonical path: " + rootDir, ex);
}
this.rootDir = rootDir;
if (includes == null) {
throw new IllegalArgumentException("includes cannot be null.");
}
if (excludes == null) {
throw new IllegalArgumentException("excludes cannot be null.");
}
includePatterns = new ArrayList();
for (String include : includes) {
includePatterns.add(Pattern.compile(include, Pattern.CASE_INSENSITIVE));
}
List<Pattern> excludePatterns = new ArrayList();
for (String exclude : excludes) {
excludePatterns.add(Pattern.compile(exclude, Pattern.CASE_INSENSITIVE));
}
scanDir(rootDir);
for (Iterator matchIter = matches.iterator(); matchIter.hasNext(); ) {
String filePath = (String) matchIter.next();
for (Pattern exclude : excludePatterns) {
if (exclude.matcher(filePath)
.matches()) {
matchIter.remove();
}
}
}
}
private
void scanDir(File dir) {
for (File file : dir.listFiles()) {
for (Pattern include : includePatterns) {
int length = rootDir.getPath()
.length();
if (!rootDir.getPath()
.endsWith(File.separator)) {
length++; // Lose starting slash.
}
String filePath = file.getPath()
.substring(length);
if (include.matcher(filePath)
.matches()) {
matches.add(filePath);
break;
}
}
if (file.isDirectory()) {
scanDir(file);
}
}
}
public
File rootDir() {
return rootDir;
}
}