Utils.java
/*
* <license>
* Copyright (c) 2003-2004, Sun Microsystems, Inc.
* Copyright (c) 2022-2026, Web-Legacy
* 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 Sun Microsystems, Inc. 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 OWNER 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.
* </license>
*/
package io.github.weblegacy.tlddoc;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.DirectoryStream;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Locale;
import java.util.function.Consumer;
import java.util.function.Predicate;
/**
* Various utils used by TldDoc.
*
* @author ste-gr
*/
public final class Utils {
/**
* Private constructor as this is a utility class.
*/
private Utils() {
}
/**
* The name separator as a string from the default-file-system.
*/
public static final String DEFAULT_SEPARATOR = FileSystems.getDefault().getSeparator();
/**
* Tests if the path is the WEB-INF directory.
*
* @param p the path
*
* @return {@code true} when it is the WEB-INF directory
*/
public static boolean isWebInf(final Path p) {
final Path fn = p.getFileName();
return fn != null && Files.isDirectory(p) && "WEB-INF".equalsIgnoreCase(fn.toString());
}
/**
* Tests if the path contains the directory-part.
*
* @param p path to test
* @param s the directory-part
*
* @return {@code true} when the path contains the directory-part
*/
public static boolean pathContains(final Path p, final String s) {
return pathString(p).contains(s);
}
/**
* Retuns the neutral string of the path (path-name-separator is always '/').
*
* @param p path to test
*
* @return the neutral string of the path
*/
public static String pathString(final Path p) {
final Path absolutePath = p.toAbsolutePath();
final String separator = absolutePath.getFileSystem().getSeparator();
String path = absolutePath.toString();
if (!"/".equals(separator)) {
path = path.replace(separator, "/");
}
return path;
}
/**
* Returns from the file the file-name-part in lower-case.
*
* @param file the file
*
* @return file-name-part in lower-case
*/
public static String getLowerFileName(final Path file) {
final Path fn = file.getFileName();
return fn == null ? "" : fn.toString().toLowerCase(Locale.ROOT);
}
/**
* Checks if the file is a tag-file (ends with {@code .tag} or {@code .tagx}).
*
* @param file to check
*
* @return {@code true} if tag-file
*/
public static boolean isTag(final Path file) {
if (Files.isDirectory(file)) {
return false;
}
final String lowerFileName = getLowerFileName(file);
return lowerFileName.endsWith(".tag") || lowerFileName.endsWith(".tagx");
}
/**
* Checks if the file is a tag-file (ends with {@code .tag} or {@code .tagx}).
*
* @param file to check
*
* @return {@code true} if tag-file
*/
public static boolean isTag(final String file) {
final String lowerFileName = file.toLowerCase(Locale.ROOT);
return lowerFileName.endsWith(".tag") || lowerFileName.endsWith(".tagx");
}
/**
* Checks if the file is a tld-file (ends with {@code .tld}).
*
* @param file to check
*
* @return {@code true} if tld-file
*/
public static boolean isTld(final Path file) {
return getLowerFileName(file).endsWith(".tld");
}
/**
* Checks if the file is a tld-file (ends with {@code .tld}).
*
* @param file to check
*
* @return {@code true} if tld-file
*/
public static boolean isTld(final String file) {
return file.toLowerCase(Locale.ROOT).endsWith(".tld");
}
/**
* Checks if the file is a jar-file (ends with {@code .jar}).
*
* @param file to check
*
* @return {@code true} if jar-file
*/
public static boolean isJar(final Path file) {
return getLowerFileName(file).endsWith(".jar");
}
/**
* Checks if the file is a jar-file (ends with {@code .jar}).
*
* @param file to check
*
* @return {@code true} if jar-file
*/
public static boolean isJar(final String file) {
return file.toLowerCase(Locale.ROOT).endsWith(".jar");
}
/**
* Process all filtered files under the given directory, recursively.
*
* @param path The path to search (recursively).
* @param filter call process when filter is {@code true}
* @param process process filtered files
*
* @throws IOException if an I/O error has occurred
*/
public static void processFiles(final Path path, final Predicate<Path> filter,
final Consumer<Path> process) throws IOException {
if (Files.isDirectory(path)) {
processFiles0(path, filter, process);
}
}
/**
* Process all filtered files under the given directory, recursively.
*
* @param path The path to search (recursively).
* @param filter call process when filter is {@code true}
* @param process process filtered files
*
* @throws IOException if an I/O error has occurred
*/
private static void processFiles0(final Path path, final Predicate<Path> filter,
final Consumer<Path> process) throws IOException {
try (DirectoryStream<Path> files = Files.newDirectoryStream(path)) {
for (Path file : files) {
if (Files.isDirectory(file)) {
processFiles0(file, filter, process);
} else if (filter.test(file)) {
process.accept(file);
}
}
}
}
/**
* Process all directories under the given directory, recursively.
*
* @param path The path to search (recursively).
* @param process process directory
*
* @throws IOException if an I/O error has occurred
*/
public static void processDirs(final Path path, final Consumer<Path> process)
throws IOException {
if (Files.isDirectory(path)) {
processDirs0(path, process);
}
}
/**
* Process all directories under the given directory, recursively.
*
* @param path The path to search (recursively).
* @param process process directory
*
* @throws IOException if an I/O error has occurred
*/
private static void processDirs0(final Path path, final Consumer<Path> process)
throws IOException {
process.accept(path);
try (DirectoryStream<Path> files = Files.newDirectoryStream(path, Files::isDirectory)) {
for (Path file : files) {
processDirs0(file, process);
}
}
}
/**
* Start from the dir and backtrack, using the path as a relative path.
*
* <p>For example:</p>
* <ul>
* <li>dir: /home/mroth/test/sample/WEB-INF/tags/mytags</li>
* <li>path: /WEB-INF/tags/mytags/tag1.tag</li>
* <li>returns: /home/mroth/test/sample/WEB-INF/tags/mytags/tag1.tag</li>
* </ul>
*
* @param dir the start-directory
* @param path the path to backtrack
*
* @return resolved path as {@link InputStream} or {@code null} when not found
*
* @throws IOException if an I/O error has occurred
*/
public static InputStream backtrackPath(Path dir, String path) throws IOException {
if (path.startsWith("/")) {
path = path.substring(1);
}
Path look = null;
while (dir != null && !Files.exists(look = dir.resolve(path))) {
dir = dir.getParent();
}
return look != null && Files.exists(look) ? Files.newInputStream(look) : null;
}
}