org.eclipse.jetty.util.FileID Maven / Gradle / Ivy
Show all versions of jetty-util Show documentation
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.util;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Locale;
import java.util.regex.Pattern;
/**
* Simple, yet surprisingly common utility methods for identifying various file types commonly seen and worked with in a
* deployment scenario.
*/
public class FileID
{
private static final Pattern NUMBER = Pattern.compile("[0-9]+");
/**
* Retrieve the basename of a path. This is the name of the
* last segment of the path, with any dot suffix (e.g. ".war") removed
*
* @param path The string path
* @return The last segment of the path without any dot suffix
*/
public static String getBasename(Path path)
{
Path filename = path.getFileName();
if (filename == null)
return "";
String basename = filename.toString();
int dot = basename.lastIndexOf('.');
if (dot >= 0)
basename = basename.substring(0, dot);
return basename;
}
/**
* Get the last segment of the URI returning it as the filename
*
* @param uri the URI to look for the filename
* @return The last segment of the uri
*/
public static String getFileName(URI uri)
{
if (uri == null)
return "";
if (uri.isOpaque())
{
return getFileName(uri.getSchemeSpecificPart());
}
return getFileName(uri.getPath());
}
/**
* Get the last segment of a String path returning it as the filename
*
* @param path the string path to look for the filename
* @return The last segment of the path
*/
public static String getFileName(String path)
{
if (path == null || "/".equals(path))
return "";
int idx = path.lastIndexOf('/');
if (idx >= 0)
{
if (idx == path.length() - 1)
{
// we found the trailing slash
// eg: file:/path/to/dir/
// we want to return the "dir" segment here
int previousSlash = path.lastIndexOf('/', idx - 1);
if (previousSlash >= 0)
{
// we have a previous slash
// so return the segment without the trailing slash
return path.substring(previousSlash + 1, idx);
}
else
{
// we have no previous slash
// this input string is something like "foo/"
// so return the segment without trailing slash
return path.substring(0, idx);
}
}
return path.substring(idx + 1);
}
return path;
}
/**
*
* Retrieve the extension of a URI path.
*
*
*
* This is the name of the last segment of the URI path with a substring
* for the extension (if any), excluding the dot, lower-cased.
*
*
* {@code
* "foo.tar.gz" "gz"
* "foo.bar" "bar"
* "foo." ""
* "foo" null
* ".bar" null
* null null
* }
*
* @param uri The URI to search
* @return The last segment extension. Null if input uri is null, or scheme is null, or URI is not a `jar:file:` or `file:` based URI
*/
public static String getExtension(URI uri)
{
if (uri == null)
return null;
if (uri.getScheme() == null)
return null;
String path = null;
if (uri.getScheme().equalsIgnoreCase("jar"))
{
URI sspUri = URI.create(uri.getRawSchemeSpecificPart());
if (!sspUri.getScheme().equalsIgnoreCase("file"))
{
return null; // not a `jar:file:` based URI
}
path = sspUri.getPath();
}
else
{
path = uri.getPath();
}
// look for `!/` split
int jarEnd = path.indexOf("!/");
if (jarEnd >= 0)
{
return getExtension(path.substring(0, jarEnd));
}
return getExtension(path);
}
/**
*
* Retrieve the extension of a file path (not a directory).
*
*
*
* This is the name of the last segment of the file path with a substring
* for the extension (if any), excluding the dot, lower-cased.
*
*
* {@code
* "foo.tar.gz" "gz"
* "foo.bar" "bar"
* "foo." ""
* "foo" null
* ".bar" null
* null null
* }
*
* @param path The string path
* @return The last segment extension, or null if not a file, or null if there is no extension present
*/
public static String getExtension(Path path)
{
if (path == null)
return null; // no path
if (!Files.isRegularFile(path))
return null; // not a file
return getExtension(path.getFileName().toString());
}
/**
* Retrieve the extension of a file path (not a directory).
* This is the extension of filename of the last segment of the file path with a substring
* for the extension (if any), including the dot, lower-cased.
*
* @param filename The string path
* @return The last segment extension excluding the leading dot;
* or null if not a file;
* or null if there is no extension present
*/
public static String getExtension(String filename)
{
if (filename == null)
return null; // no filename
if (filename.endsWith("/") || filename.endsWith("\\"))
return null; // not a filename
int lastSlash = filename.lastIndexOf(File.separator);
if (lastSlash >= 0)
filename = filename.substring(lastSlash + 1);
int lastDot = filename.lastIndexOf('.');
if (lastDot <= 0)
return null; // no extension, or only filename that is only an extension (".foo")
return filename.substring(lastDot + 1).toLowerCase(Locale.ENGLISH);
}
/**
* Test if Path matches any of the indicated extensions.
*
* @param path the Path to test
* @param extensions the list of extensions (all lowercase, without preceding {@code .} dot)
* @return true if Path is a file, and has an extension, and it matches any of the indicated extensions
*/
public static boolean isExtension(Path path, String... extensions)
{
return matchesExtension(getExtension(path), extensions);
}
/**
* Test if URI matches any of the indicated extensions.
*
* @param uri the URI to test
* @param extensions the list of extensions (all lowercase, without preceding {@code .} dot)
* @return true if URI has an extension, and it matches any of the indicated extensions
*/
public static boolean isExtension(URI uri, String... extensions)
{
return matchesExtension(getExtension(uri), extensions);
}
/**
* Test if filename matches any of the indicated extensions.
*
* @param filename the filename to test
* @param extensions the list of extensions (all lowercase, without preceding {@code .} dot)
* @return true if filename has an extension, and it matches any of the indicated extensions
*/
public static boolean isExtension(String filename, String... extensions)
{
return matchesExtension(getExtension(filename), extensions);
}
private static boolean matchesExtension(String ext, String... extensions)
{
if (ext == null)
return false;
for (String extension : extensions)
{
if (ext.equals(extension))
return true;
}
return false;
}
/**
* Does the provided path have a directory segment with the given name.
*
* @param path the path to search
* @param segmentName the segment name (of the given path) to look for (case-insensitive lookup),
* only capable of searching 1 segment name at a time, does not support "foo/bar" multi-segment
* names.
* @return true if the directory name exists in path, false if otherwise
*/
public static boolean hasNamedPathSegment(Path path, String segmentName)
{
if (path == null)
return false;
int segmentCount = path.getNameCount();
for (int i = segmentCount - 1; i >= 0; i--)
{
Path segment = path.getName(i);
if (segment.getFileName().toString().equalsIgnoreCase(segmentName))
return true;
}
return false;
}
/**
* Test if Path is any supported Java Archive type (ends in {@code jar}, {@code war}, or {@code zip}).
*
* @param path the path to test
* @return true if path is a file, and an extension of {@code jar}, {@code war}, or {@code zip}
* @see #getExtension(Path)
*/
public static boolean isArchive(Path path)
{
return isExtension(path, "jar", "war", "zip");
}
/**
* Test if filename is any supported Java Archive type (ends in {@code jar}, {@code war}, or {@code zip}).
*
* @param filename the filename to test
* @return true if path is a file and name ends with {@code jar}, {@code war}, or {@code zip}
* @see #getExtension(String)
*/
public static boolean isArchive(String filename)
{
return isExtension(filename, "jar", "war", "zip");
}
/**
* Test if URI is any supported Java Archive type.
*
* @param uri the URI to test
* @return true if the URI has a path that seems to point to a ({@code jar}, {@code war}, or {@code zip}).
* @see #isArchive(String)
*/
public static boolean isArchive(URI uri)
{
return isExtension(uri, "jar", "war", "zip");
}
/**
* Test if Path is any supported Java Library Archive type (suitable to use as a library in a classpath/classloader)
*
* @param path the path to test
* @return true if path is a file, and an extension of {@code jar}, or {@code zip}
* @see #getExtension(Path)
*/
public static boolean isLibArchive(Path path)
{
return isExtension(path, "jar", "zip");
}
/**
* Test if filename is any supported Java Library Archive type (suitable to use as a library in a classpath/classloader)
*
* @param filename the filename to test
* @return true if path is a file and name ends with {@code jar}, or {@code zip}
* @see #getExtension(String)
*/
public static boolean isLibArchive(String filename)
{
return isExtension(filename, "jar", "zip");
}
/**
* Test if URI is any supported Java Library Archive type (suitable to use as a library in a classpath/classloader)
*
* @param uri the URI to test
* @return true if the URI has a path that seems to point to a ({@code jar}, or {@code zip}).
* @see #getExtension(URI)
*/
public static boolean isLibArchive(URI uri)
{
return isExtension(uri, "jar", "zip");
}
/**
* Predicate to test for class files
*
* @param path the path to test
* @return true if the filename ends with {@code .class}
*/
public static boolean isClassFile(Path path)
{
Path fileNamePath = path.getFileName();
if (fileNamePath == null)
return false;
String filename = fileNamePath.toString();
// has to end in ".class"
if (!StringUtil.asciiEndsWithIgnoreCase(filename, ".class"))
return false;
// is it a valid class filename?
int start = 0;
int end = filename.length() - 6; // drop ".class"
if (end <= start) // if the filename is only ".class"
return false;
// Test first character
if (!Character.isJavaIdentifierStart(filename.charAt(0)))
return false;
// Test rest
for (int i = start + 1; i < end; i++)
{
if (!Character.isJavaIdentifierPart(filename.codePointAt(i)))
{
// not a java identifier
return false;
}
}
return true;
}
/**
* Predicate useful for {@code Stream} to exclude hidden paths following
* filesystem rules for hidden directories and files.
*
* @param path the possibly relative path to evaluate
* @return true if hidden by FileSystem rules, false if not
* @see Files#isHidden(Path)
*/
public static boolean isHidden(Path path)
{
int count = path.getNameCount();
for (int i = 0; i < count; i++)
{
Path segment = path.getName(i);
String segmentName = segment.toString();
if (segmentName.isBlank())
continue; // skip blank entries
// default behavior that Jetty enforces
if (segmentName.charAt(0) == '.')
return true;
// FileSystem behavior, that the FileSystem enforces
try
{
if (Files.isHidden(segment))
return true;
}
catch (IOException ignore)
{
// ignore, if filesystem gives us an error, we cannot make the call on hidden status
}
}
return false;
}
/**
* Predicate useful for {@code Stream} to exclude hidden paths following
* filesystem rules for hidden directories and files.
*
* @param base the base path to search from (anything above this path is not evaluated)
* @param path the path to evaluate
* @return true if hidden by FileSystem rules, false if not
* @see Files#isHidden(Path)
*/
public static boolean isHidden(Path base, Path path)
{
// Work with the path in relative form, from the base onwards to the path
return isHidden(base.relativize(path));
}
/**
* Is the URI pointing to a Java Archive (JAR) File (not directory)
*
* @param uri the uri to test.
* @return True if a jar file.
*/
public static boolean isJavaArchive(URI uri)
{
return isExtension(uri, "jar");
}
/**
* Is the path a Java Archive (JAR) File (not directory)
*
* @param path the path to test.
* @return True if a jar file.
*/
public static boolean isJavaArchive(Path path)
{
return isExtension(path, "jar");
}
/**
* Is the filename a JAR file.
*
* @param filename the filename to test.
* @return True if a jar file.
*/
public static boolean isJavaArchive(String filename)
{
return isExtension(filename, "jar");
}
/**
* Predicate to filter on {@code META-INF/versions/*} tree in walk/stream results.
*
*
* This only works with a zipfs based FileSystem
*
*
* @param path the path to test
* @return true if path is in {@code META-INF/versions/*} tree
*/
public static boolean isMetaInfVersions(Path path)
{
if (path.getNameCount() < 3)
return false;
if (!StringUtil.asciiEqualsIgnoreCase("META-INF", path.getName(0).toString()))
return false;
if (!StringUtil.asciiEqualsIgnoreCase("versions", path.getName(1).toString()))
return false;
return NUMBER.matcher(path.getName(2).getFileName().toString()).matches();
}
/**
* Predicate to skip {@code META-INF/versions/*} tree from walk/stream results.
*
*
* This only works with a zipfs based FileSystem
*
*
* @param path the path to test
* @return true if not in {@code META-INF/versions/*} tree
*/
public static boolean isNotMetaInfVersions(Path path)
{
return !isMetaInfVersions(path);
}
/**
* Predicate to skip {@code module-info.class} files.
*
*
* This is a simple test against the last path segment using {@link Path#getFileName()}
*
*
* @param path the path to test
* @return true if not a {@code module-info.class} file
*/
public static boolean isModuleInfoClass(Path path)
{
Path filenameSegment = path.getFileName();
if (filenameSegment == null)
return false;
return filenameSegment.toString().equalsIgnoreCase("module-info.class");
}
/**
* Predicate to skip {@code module-info.class} files.
*
*
* This is a simple test against the last path segment using {@link Path#getFileName()}
*
*
* @param path the path to test
* @return true if not a {@code module-info.class} file
*/
public static boolean isNotModuleInfoClass(Path path)
{
return !isModuleInfoClass(path);
}
/**
* Is the path a TLD File
*
* @param path the path to test.
* @return True if a .tld file.
*/
public static boolean isTld(Path path)
{
if (path == null)
return false;
if (!hasNamedPathSegment(path, "META-INF"))
return false;
return isExtension(path, "tld");
}
/**
* Is the path a Web Archive File (not directory)
*
* @param path the path to test.
* @return True if a war file.
*/
public static boolean isWebArchive(Path path)
{
return isExtension(path, "war");
}
/**
* Is the path a Web Archive File (not directory)
*
* @param uri the uri to test.
* @return True if a war file.
*/
public static boolean isWebArchive(URI uri)
{
return isExtension(uri, "war");
}
/**
* Is the filename a WAR file.
*
* @param filename the filename to test.
* @return True if a war file.
*/
public static boolean isWebArchive(String filename)
{
return isExtension(filename, "war");
}
/**
* Is the Path a file that ends in XML?
*
* @param path the path to test
* @return true if a xml, false otherwise
*/
public static boolean isXml(Path path)
{
return isExtension(path, "xml");
}
/**
* Is the Path a file that ends in XML?
*
* @param filename the filename to test
* @return true if a xml, false otherwise
*/
public static boolean isXml(String filename)
{
return isExtension(filename, "xml");
}
}