io.qt.internal.ResourceUtility Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of qtjambi Show documentation
Show all versions of qtjambi Show documentation
QtJambi base module containing QtCore, QtGui and QtWidgets.
/****************************************************************************
**
** Copyright (C) 2009-2024 Dr. Peter Droste, Omix Visualization GmbH & Co. KG. All rights reserved.
**
** This file is part of Qt Jambi.
**
** $BEGIN_LICENSE$
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
** $END_LICENSE$
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
****************************************************************************/
package io.qt.internal;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.JarURLConnection;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLConnection;
import java.nio.file.Files;
import java.nio.file.attribute.FileTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.TreeSet;
import java.util.function.Function;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import io.qt.NativeAccess;
import io.qt.core.QDir;
import io.qt.core.QLocale;
/**
* @hidden
*/
final class ResourceUtility {
static {
QtJambi_LibraryUtilities.initialize();
}
private ResourceUtility() {
}
private interface JarResourceFactoryInterface{
JarResource create(File fileToJarFile) throws IOException;
JarResource create(URL urlToJarFile) throws IOException;
}
private static class JarResourceFactory implements JarResourceFactoryInterface{
@Override
public JarResource create(File fileToJarFile) throws IOException {
return new JarResource(fileToJarFile);
}
@Override
public JarResource create(URL urlToJarFile) throws IOException {
return new JarResource(urlToJarFile);
}
}
private static class RecognizableJarResourceFactory implements JarResourceFactoryInterface{
@Override
public JarResource create(File fileToJarFile) throws IOException {
return new RecognizableJarResource(fileToJarFile);
}
@Override
public JarResource create(URL urlToJarFile) throws IOException {
return new RecognizableJarResource(urlToJarFile);
}
}
private final static JarResourceFactoryInterface factory;
static {
if(Boolean.getBoolean("io.qt.acknowledge-resources")) {
factory = new RecognizableJarResourceFactory();
}else {
factory = new JarResourceFactory();
}
}
private static class UrlOrFile {
UrlOrFile(URL url, File file) {
super();
this.url = url;
this.file = file;
}
final URL url;
final File file;
}
private static final Function> newSetFactory = s -> new HashSet<>();
private final static JarCache cache = new JarCache();
static void addSearchPath(URL url) {
if (url != null) {
cache.addPath(url);
}
}
static void addSearchPath(String path) {
URL url = resolveUrlFromPath(path);
if (url != null) {
cache.addPath(url);
}
}
static void removeSearchPath(String path) {
URL url = resolveUrlFromPath(path);
if (url != null) {
cache.removePath(url);
}
}
@NativeAccess
private static void initialize() {
List cpUrls = new ArrayList<>();
try {
for(URI uri : RetroHelper.moduleLocations()) {
try{
URL url = uri.toURL();
if(!"jrt".equals(url.getProtocol()) && !cpUrls.contains(url)) {
cache.addPath(url);
cpUrls.add(url);
}
} catch (Exception e) {
java.util.logging.Logger.getLogger("io.qt.internal.fileengine").log(java.util.logging.Level.SEVERE, "", e);
}
}
for(ClassLoader loader : RetroHelper.classLoaders()) {
if(loader instanceof URLClassLoader) {
for(URL url : ((URLClassLoader) loader).getURLs()) {
if(!cpUrls.contains(url)) {
cache.addPath(url);
cpUrls.add(url);
}
}
}else {
Enumeration urls = loader.getResources("META-INF/MANIFEST.MF");
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
try {
String end;
String urlPath;
if("jar".equals(url.getProtocol())) {
urlPath = url.getPath();
end = "!/META-INF/MANIFEST.MF";
}else {
urlPath = url.toString();
end = "/META-INF/MANIFEST.MF";
if(urlPath.endsWith(end))
end = "META-INF/MANIFEST.MF";
}
if(urlPath.endsWith(end)) {
urlPath = urlPath.substring(0, urlPath.length() - end.length());
URL fileUrl = CoreUtility.createURL(urlPath);
if(!"jar".equals(url.getProtocol()) && fileUrl.getPath().isEmpty()) {
fileUrl = CoreUtility.createURL(urlPath+"/");
}
if(!cpUrls.contains(fileUrl)) {
cache.addPath(fileUrl);
cpUrls.add(fileUrl);
}
}
} catch (Throwable e) {
java.util.logging.Logger.getLogger("io.qt.internal.fileengine")
.log(java.util.logging.Level.SEVERE, "", e);
}
}
}
}
} catch (Exception e) {
java.util.logging.Logger.getLogger("io.qt.internal.fileengine").log(java.util.logging.Level.SEVERE, "",
e);
}
String javaClassPath = System.getProperty("java.class.path");
if (javaClassPath == null)
javaClassPath = ""; // gets ignored below
String javaModulePath = System.getProperty("jdk.module.path");
if (javaModulePath != null)
javaClassPath = javaModulePath + File.pathSeparator + javaClassPath; // gets ignored below
String paths[] = javaClassPath.split("\\" + File.pathSeparator);
// Only add the .jar files that are not already added...
int counter = 0;
for (String path : paths) {
path = path.trim();
if (!path.isEmpty()) {
counter++; // count all paths, invalid and valid
URL url = resolveUrlFromPath(path);
boolean match = false;
if(url!=null){
cache.addPath(url);
if(url.toString().endsWith("/"))
continue;
}else{
continue;
}
JarResource resource2 = null;
JarResource resource1 = null;
try {
resource2 = resolveUrlToJarResource(url);
if (resource2 != null) {
for (URL otherURL : cpUrls) {
if(otherURL.toString().endsWith("/"))
continue;
resource1 = resolveUrlToJarResource(otherURL);
if (resource1 != null) {
File file1 = new File(resource1.getName());
File file2 = new File(resource2.getName());
if (file1.getCanonicalPath().equals(file2.getCanonicalPath())) {
match = true;
break;
}
}
}
}
} catch (Exception e) { // This should probably just be IOException
java.util.logging.Logger.getLogger("io.qt.internal.fileengine")
.log(java.util.logging.Level.SEVERE, "", e); // this has been so useful in finding many
// bugs/issues
} finally {
if (resource2 != null) {
resource2.put();
resource2 = null;
}
if (resource1 != null) {
resource1.put();
resource1 = null;
}
}
if (!match)
cache.addPath(url);
}
}
if (counter == 0) {
try {
cache.addPath(new File(System.getProperty("user.dir")).toURI().toURL());
} catch (MalformedURLException e) {
java.util.logging.Logger.getLogger("io.qt.internal.fileengine")
.log(java.util.logging.Level.SEVERE, "", e);
}
}
}
@NativeAccess
private static URL resolveUrlFromPath(String path) {
URL result = null;
if (path != null) {
File file = new File(path);
if(file.exists()) {
try {
result = file.toURI().toURL();
} catch (Exception e) {
}
}
if(result==null) {
try {
result = CoreUtility.createURL(path);
} catch (Exception e) {
}
}
if(result==null) {
final int pathLength = path.length();
boolean skipTryAsis = false; // attempt to not use exceptions for common situations
if (pathLength > 0) {
char firstChar = path.charAt(0);
// Both a "/" and "\\" are illegal characters in the scheme/protocol.
if (firstChar == File.separatorChar) {
skipTryAsis = true;
} else if (pathLength == 1) {
if (firstChar == '.') {
String tmpPath = System.getProperty("user.dir");
if (tmpPath != null) {
path = tmpPath;
skipTryAsis = true;
}
}
// ELSE it is a relative path and will be picked up below
} else if (pathLength > 1) {
char secondChar = path.charAt(1);
if (firstChar == '.' && secondChar == File.separatorChar) {
// ./foo/bar case
String tmpPath = System.getProperty("user.dir");
if (tmpPath != null) {
path = tmpPath + File.separatorChar + path.substring(2);
skipTryAsis = true;
}
} else if (pathLength > 2) {
// Windows "C:\\..." for which "\\" is incorrect for URLs
char thirdChar = path.charAt(2);
if ((firstChar >= 'A' && firstChar <= 'Z') || (firstChar >= 'a' && firstChar <= 'z')) {
// We don't check for '/' since that might be a real URL "a://host:port/path?qs"
// and would be invalid for windows using java.io.File API anyway.
if (secondChar == ':' && thirdChar == '\\')
skipTryAsis = true;
if (secondChar == ':' && thirdChar == '/') // "C:/dir1/dir2/file.dat" is seen when processing
// paths from QFileInfo
skipTryAsis = true;
} else if (pathLength > 3) {
// Eclipse "/C:/..."
char fourthChar = path.charAt(3);
if (firstChar == '/' && (secondChar >= 'A' && secondChar <= 'Z')
|| (secondChar >= 'a' && secondChar <= 'z')) {
if (thirdChar == ':' && fourthChar == '/')
skipTryAsis = true; // we prefix it with file:// below
}
}
}
}
if (skipTryAsis == false) {
boolean prefix = !(path.startsWith("file:")
|| path.startsWith("jar:")
|| path.startsWith("http:")
|| path.startsWith("https:"));
int colon = path.indexOf(':');
if(colon>0) {
String protocol = path.substring(0, colon);
try {
CoreUtility.createURL(protocol+"://");
prefix = false;
} catch (MalformedURLException e) {
}
}
if (prefix) {
String tmpPath = System.getProperty("user.dir");
if (tmpPath != null) {
String _path = tmpPath + File.separatorChar + path;
if(new File(_path).exists()) {
path = _path;
skipTryAsis = true;
}
}
}
}
}
if (result == null) {
try {
String xPath = path.replace('\\', '/');
String xPrefix;
if (path.length() > 0 && xPath.charAt(0) != '/')
xPrefix = "file:///";
else
xPrefix = "file://";
String newTmpPath = xPrefix;
if (File.separatorChar == '\\')
newTmpPath += path.replace('\\', '/'); // windows
else
newTmpPath += path;
result = CoreUtility.createURL(newTmpPath);
} catch (Exception e) {
}
}
}
try {
UrlOrFile urlOrFile = checkURL(result);
if (urlOrFile.file != null) { // Due to workaround
if (!urlOrFile.file.exists())
result = null;
} else if(!urlOrFile.url.toString().endsWith("/") && !urlOrFile.url.getPath().isEmpty()){
URLConnection urlConn = urlOrFile.url.openConnection();
try(InputStream inStream = urlConn.getInputStream()){}
}
} catch(java.io.FileNotFoundException e) {
result = null;
} catch (Throwable e) {
if(!result.getProtocol().equals("jrt") && !result.getProtocol().equals("file"))
java.util.logging.Logger.getLogger("io.qt.internal.fileengine").log(java.util.logging.Level.SEVERE, ""+result, e);
result = null;
}
}
return result;
}
@NativeAccess
private static Collection pathToJarFiles(String entry) {
return cache.pathToJarFiles(entry);
}
@NativeAccess
private static boolean isDirectory(JarResource myJarFile, String fileName) {
boolean isDirectory = false;
JarEntry fileInJar = myJarFile.getJarEntry(fileName);
// If the entry exists in the given file, look it up and
// check if its a dir or not
if (fileInJar != null) {
isDirectory = fileInJar.isDirectory();
if (!isDirectory) {
boolean tmpIsDirectory = checkIsDirectory(myJarFile, fileInJar);
isDirectory = tmpIsDirectory;
} else {
}
}
if (!isDirectory) {
// Otherwise, look if the directory exists in the
// cache...
Collection pathToJarFiles = pathToJarFiles(fileName);
String jarFileName = myJarFile.getName();
if (pathToJarFiles != null) {
for (String thisPathToJar : pathToJarFiles) {
if (thisPathToJar.equals(jarFileName)) {
isDirectory = true;
break;
}
}
}
// Nasty fallback... Iterate through the .jar file and try to check if
// fileName is the prefix (hence directory) of any of the entries...
if (!isDirectory) {
String fileNameWithSlash = fileName + "/";
Enumeration entries = myJarFile.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
String entryName = entry.getName();
if (entryName.startsWith(fileNameWithSlash)) {
isDirectory = true;
break;
}
}
}
}
return isDirectory;
}
/**
* The JarEntry.isDirectory() method in Java returns false
* even for directories, so we need this extra check
* which tries to read a byte from the entry in order
* to trigger an exception when the entry is a directory.
*/
static boolean checkIsDirectory(JarResource myJarFile, JarEntry fileInJar) {
InputStream inStream = null;
try {
// CHECKME is this hack/trick/kludge maybe somewhat problematic
// for connection handler based JarFile handles ?
inStream = myJarFile.getInputStream(fileInJar);
if(inStream == null)
return true; // avoid NPE
inStream.read();
} catch(IOException e) {
return true;
} finally {
if(inStream != null) {
try {
inStream.close();
} catch(IOException eat) {
}
inStream = null;
}
}
return false;
}
@NativeAccess
private static JarResource resolveUrlToJarResource(URL url) throws IOException, ZipException {
JarResource myJarFile = null;
UrlOrFile urlOrFile = checkURL(url);
try {
if(urlOrFile.file != null) { // Due to workaround
if(urlOrFile.file.isFile()) // skip dirs
myJarFile = factory.create(urlOrFile.file);
} else if(!"jrt".equals(urlOrFile.url.getProtocol())){
myJarFile = factory.create(urlOrFile.url);
}
} catch(ZipException e) {
// This often fails with "java.util.zip.ZipException: error in opening zip file" but never discloses the filename
throw new ZipException(e.getMessage() + ": " + url);
}
return myJarFile;
}
private static UrlOrFile checkURL(URL url) {
File file = null;
if("jar".equals(url.getProtocol())) {
String path = url.getPath();
if(path.endsWith("!/")) {
try {
url = CoreUtility.createURL(path.substring(0, path.length() - 2));
return checkURL(url);
} catch(MalformedURLException eat) {
url = null;
}
}
} else if("file".equals(url.getProtocol())) {
try {
file = new File(url.toURI());
} catch (URISyntaxException e) {
}
}
return new UrlOrFile(url, file);
}
private static class JarResource{
private URLConnection urlConnection; // do we need to keep this around ?
private File fileToJarFile; // we store this object type to differentiate between URLs and direct File IO.
private URL urlToJarFile; // we save this to allow for close/reopen based on just this handle
private JarFile jarFile;
private int refCount;
JarResource(File fileToJarFile) throws IOException {
this.fileToJarFile = fileToJarFile;
openInternal();
}
JarResource(URL urlToJarFile) throws IOException {
this.urlToJarFile = urlToJarFile;
openInternal();
}
private void openInternal() throws IOException {
if(fileToJarFile != null) { // Direct File I/O Jar file
jarFile = new JarFile(fileToJarFile);
} else {
urlConnection = urlToJarFile.openConnection();
if(urlConnection instanceof JarURLConnection) {
JarURLConnection jarUrlConnection = (JarURLConnection) urlConnection;
jarFile = jarUrlConnection.getJarFile();
}else {
IOException thr = new IOException("not a JarURLConnection: " + urlConnection.getClass().getName()+" for URL: "+urlToJarFile);
urlConnection = null; // we only keep handle when we have active Jar open
throw thr;
}
}
refCount = 1;
}
// This method may never throw an exception
@NativeAccess
final void get() {
synchronized(this) {
refCount++;
}
}
// This method must cause a double increment on the reopen() case
// Returns the previous refCount, so 0 means we just reopened, non-zero means we did get()
@NativeAccess
final int getOrReopen() throws IOException {
int oldRefCount;
synchronized (this) {
oldRefCount = refCount;
if(refCount <= 0)
reopen();
get();
}
return oldRefCount;
}
// This method may never throw an exception
@NativeAccess
final void put() {
JarFile closeJarFile = null;
synchronized(this) {
refCount--;
if(refCount == 0) {
closeJarFile = jarFile;
jarFile = null;
}
}
if(closeJarFile != null) {
try {
closeJarFile.close();
} catch(IOException eat) {
}
if(urlConnection != null) {
urlConnection = null;
}
}
}
final void reopen() throws IOException {
if(jarFile != null)
throw new IOException("jarFile already open");
openInternal();
}
@NativeAccess
final String getName() {
return jarFile.getName();
}
Enumeration entries() {
return jarFile.entries();
}
@NativeAccess
JarEntry getJarEntry(String name) {
JarEntry entry = jarFile.getJarEntry(name);
if(entry==null) {
JarEntry aliasEntry = jarFile.getJarEntry(name+".alias");
if(aliasEntry!=null) {
try(InputStream stream = getInputStream(aliasEntry)){
Properties aliasProperties = new Properties();
aliasProperties.load(stream);
QLocale locale = new QLocale();
String localeName = locale.name();
String aliasName = aliasProperties.getProperty(localeName);
if(aliasName==null && !locale.bcp47Name().equals(localeName)) {
aliasName = aliasProperties.getProperty(locale.bcp47Name());
}
if(aliasName==null) {
String[] split = localeName.split("_");
if(split.length>1)
aliasName = aliasProperties.getProperty(split[0]);
}
if(aliasName==null) {
aliasName = aliasProperties.getProperty("default");
}
if(aliasName==null) {
aliasName = aliasProperties.getProperty("");
}
if(aliasName!=null) {
int idx = name.lastIndexOf('/');
if(idx>0) {
String path = name.substring(0, idx+1);
entry = jarFile.getJarEntry(path + aliasName);
}else{
entry = jarFile.getJarEntry(aliasName);
}
}
} catch (Throwable ign) {
}
}
}
return entry;
}
@NativeAccess
InputStream getInputStream(ZipEntry ze) throws IOException {
return jarFile.getInputStream(ze);
}
@NativeAccess
private void entryList(List result, int _filters, Collection filterNames, String mentryName){
if (!mentryName.endsWith("/") && mentryName.length() > 0)
mentryName = mentryName + "/";
Enumeration entries = entries();
HashSet used = new HashSet();
QDir.Filters filters = new QDir.Filters(_filters);
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
String entryName = entry.getName();
// Must be inside this directory
if (entryName.length() <= mentryName.length() || !mentryName.equals(entryName.substring(0, mentryName.length())) || mentryName.equals(entryName))
continue;
// Only one level
boolean isDir;
int pos = entryName.indexOf("/", mentryName.length());
if (pos > 0) {
entryName = entryName.substring(0, pos);
isDir = true;
} else {
isDir = entry.isDirectory();
if (!isDir)
isDir = ResourceUtility.checkIsDirectory(this, entry);
}
if (!filters.testFlag(QDir.Filter.Readable))
continue ;
if (!filters.testFlag(QDir.Filter.Dirs) && isDir)
continue ;
if (!filters.testFlag(QDir.Filter.Files) && !isDir)
continue ;
if (filterNames.size() > 0) {
if ((!isDir || !filters.testFlag(QDir.Filter.AllDirs))
&& (!QDir.match(filterNames, entryName.substring(mentryName.length())))) {
continue;
}
}
if (entryName.endsWith("/") && entryName.length() > 1)
entryName = entryName.substring(0, entryName.length() - 1);
entryName = entryName.substring(mentryName.length());
if (!used.contains(entryName)) {
used.add(entryName);
result.add(entryName);
}
}
}
@NativeAccess
private long fileTime(ZipEntry ze, boolean creationTime, boolean lastAccessTime, boolean lastModified) throws IOException {
FileTime fileTime = null;
if(ze!=null) {
if(creationTime)
fileTime = ze.getCreationTime();
else if(lastAccessTime)
fileTime = ze.getLastAccessTime();
else if(lastModified)
fileTime = ze.getLastModifiedTime();
else
fileTime = ze.getCreationTime();
}else if(fileToJarFile!=null){
fileTime = Files.getLastModifiedTime(fileToJarFile.toPath());
}
if(fileTime!=null) {
return fileTime.toMillis();
}else {
long tm = -1;
if(ze!=null){
tm = ze.getTime();
}else if(fileToJarFile!=null){
tm = fileToJarFile.lastModified();
}
return tm;
}
}
}
private static final class RecognizableJarResource extends JarResource{
RecognizableJarResource(URL urlToJarFile) throws IOException {
super(urlToJarFile);
}
RecognizableJarResource(File fileToJarFile) throws IOException {
super(fileToJarFile);
}
@Override
JarEntry getJarEntry(String name) {
JarEntry entry = super.getJarEntry(name);
if(entry!=null) {
try {
ResourceUtility.class.getClassLoader().getResource(entry.getName());
} catch (Exception e) {
}
}
return entry;
}
@Override
InputStream getInputStream(ZipEntry entry) throws IOException {
if(entry!=null) {
try {
ResourceUtility.class.getClassLoader().getResource(entry.getName());
} catch (Exception e) {
}
}
return super.getInputStream(entry);
}
@Override
Enumeration entries() {
Enumeration entries = super.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
if(entry!=null) {
try {
ResourceUtility.class.getClassLoader().getResource(entry.getName());
} catch (Exception e) {
}
}
}
return super.entries();
}
}
@NativeAccess
private static Collection classPathDirs() {
return cache.classPathDirs();
}
@NativeAccess
private static void clear() {
synchronized(cache.cache) {
cache.cache.clear();
}
synchronized(cache.classPathDirs) {
cache.classPathDirs.clear();
}
}
private final static class JarCache {
Collection pathToJarFiles(String entry) {
synchronized(cache) {
Set result = cache.get(entry);
return result==null? Collections.emptyList() : new HashSet<>(result);
}
}
Collection classPathDirs() {
synchronized(classPathDirs) {
return new HashSet<>(classPathDirs);
}
}
private final Map> cache = new HashMap<>();
private final Set classPathDirs = new HashSet<>();
void addPath(URL url) {
JarResource resource = null;
try {
//
if(url.getProtocol().equals("file")) {
File fileDir = new File(url.toURI());
if(fileDir.isDirectory()) {
synchronized(classPathDirs) {
classPathDirs.add(fileDir.getAbsolutePath());
}
return;
} else if(fileDir.isFile()) {
try {
resource = factory.create(fileDir);
} catch(IOException eat) {
}
}else {
return;
}
}else if(url.toString().endsWith("/") || url.getPath().isEmpty()) {
synchronized(classPathDirs) {
classPathDirs.add(url.toString());
}
return;
}
if(resource == null) {
try {
resource = factory.create(url);
} catch(ZipException e) {
throw new ZipException(e.getMessage() + ": " + url);
}
}
String resourceName = resource.getName();
Set seenSet = new TreeSet<>();
Enumeration entries = resource.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
String dirName = null;
boolean isToplevelFile = false;
String entryName = entry.getName();
// Remove potentially initial '/'
while (entryName.startsWith("/"))
entryName = entryName.substring(1);
if (entry.isDirectory()) {
if (entryName.endsWith("/"))
dirName = entryName.substring(0, entryName.length() - 1); // canonicalize
else
dirName = entryName;
} else {
int slashPos = entryName.lastIndexOf("/");
if (slashPos > 0)
dirName = entryName.substring(0, slashPos); // isolate directory part
else
isToplevelFile = true; // dirName will be null; there is no directory part
}
// Add all parent directories "foo/bar/dir1/dir2", "foo/bar/dir1", "foo/bar", "foo"
while (dirName != null) {
// optimization: if we saw the long nested path (then we already processed its parents as well)
if (seenSet.contains(dirName))
break;
seenSet.add(dirName);
synchronized(cache) {
cache.computeIfAbsent(dirName, newSetFactory).add(resourceName);
}
int slashPos = dirName.lastIndexOf("/");
if (slashPos > 0)
dirName = dirName.substring(0, slashPos);
else
dirName = null;
}
if (isToplevelFile) {
if (!seenSet.contains("")) {
seenSet.add("");
synchronized(cache) {
cache.computeIfAbsent("", newSetFactory).add(resourceName);
}
}
}
}
// Add root dir for all jar files (even empty ones)
if (!seenSet.contains("")) {
seenSet.add("");
// Add root dir for all jar files (even empty ones)
synchronized(cache) {
cache.computeIfAbsent("", newSetFactory).add(resourceName);
}
}
} catch (Exception e) {
} finally {
if (resource != null) {
resource.put();
resource = null;
}
}
}
void removePath(URL url) {
JarResource resource = null;
try {
//
if(url.getProtocol().equals("file")) {
File fileDir = new File(url.toURI());
if(fileDir.isDirectory()) {
removeImpl(fileDir.getAbsolutePath());
return;
} else if(fileDir.isFile()) {
try {
resource = factory.create(fileDir);
} catch(IOException eat) {
}
}else {
return;
}
}else if(url.toString().endsWith("/") || url.getPath().isEmpty()) {
removeImpl(url.toString());
return;
}
if(resource == null) {
try {
resource = factory.create(url);
} catch(ZipException e) {
}
}
if(resource != null) {
removeImpl(resource.getName());
}else {
removeImpl(url.toString());
}
} catch (Exception e) {
removeImpl(url.toString());
} finally {
if (resource != null) {
resource.put();
resource = null;
}
}
}
private void removeImpl(String jarFileName) {
synchronized(cache) {
for(String key : new ArrayList<>(cache.keySet())) {
Set entries = cache.get(key);
if(entries!=null) {
entries.remove(jarFileName);
if(entries.isEmpty()) {
cache.remove(key);
}
}else {
cache.remove(key);
}
}
}
}
}
}