package org.jboss.windup.util;
import java.io.IOException;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.logging.Logger;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.jboss.windup.util.exception.WindupException;
/**
* Provides useful methods for manipulating filenames (eg, removing illegal chars from files).
*
* @author <a href="mailto:jesse.sightler@gmail.com">Jesse Sightler</a>
* @author <a href="mailto:lincolnbaxter@gmail.com">Lincoln Baxter, III</a>
*/
public class PathUtil
{
private static final Logger LOG = Logger.getLogger(PathUtil.class.getName());
public static final String WINDUP_HOME = "windup.home";
public static final String WINDUP_RULESETS_DIR_SYSPROP = "windup.rulesets.dir";
public static final String RULES_DIRECTORY_NAME = "rules";
public static final String IGNORE_DIRECTORY_NAME = "ignore";
public static final String CACHE_DIRECTORY_NAME = "cache";
public static final String ADDONS_DIRECTORY_NAME = "addons";
public static String LIBRARY_DIRECTORY_NAME = "lib";
public static String BINARY_DIRECTORY_NAME = "bin";
/**
* The path $USER_HOME/.rhamt
*/
public static Path getWindupUserDir()
{
String userHome = System.getProperty("user.home");
if (userHome == null)
{
Path path = new File("").toPath();
LOG.warning("$USER_HOME not set, using [" + path.toAbsolutePath().toString() + "] instead.");
return path;
}
return Paths.get(userHome).resolve(".rhamt");
}
/**
* The path $WINDUP_HOME (where Windup is installed.)
*/
public static Path getWindupHome()
{
String windupHome = System.getProperty(WINDUP_HOME);
if (windupHome == null)
{
Path path = new File("").toPath();
LOG.warning("$WINDUP_HOME not set, using [" + path.toAbsolutePath().toString() + "] instead.");
return path;
}
return Paths.get(windupHome);
}
public static void setWindupHome(Path windupHome)
{
System.setProperty(WINDUP_HOME, windupHome.toAbsolutePath().toString());
}
/**
* The path $USER_HOME/cache
*/
public static Path getUserCacheDir()
{
return getUserSubdirectory(CACHE_DIRECTORY_NAME);
}
/**
* The path $WINDUP_HOME/cache
*/
public static Path getWindupCacheDir()
{
return getWindupSubdirectory(CACHE_DIRECTORY_NAME);
}
/**
* The path $USER_HOME/ignore
*/
public static Path getUserIgnoreDir()
{
return getUserSubdirectory(IGNORE_DIRECTORY_NAME);
}
/**
* The path $WINDUP_HOME/ignore
*/
public static Path getWindupIgnoreDir()
{
return getWindupSubdirectory(IGNORE_DIRECTORY_NAME);
}
/**
* The path $WINDUP_HOME/addons
*/
public static Path getWindupAddonsDir()
{
return getWindupSubdirectory(ADDONS_DIRECTORY_NAME);
}
/**
* The path $USER_HOME/rules
*/
public static Path getUserRulesDir()
{
return getUserSubdirectory(RULES_DIRECTORY_NAME);
}
/**
* The path $WINDUP_HOME/rules
*/
public static Path getWindupRulesDir()
{
String rulesDir = System.getProperty(WINDUP_RULESETS_DIR_SYSPROP);
if (rulesDir != null)
{
Path path = Paths.get(rulesDir);
if (!path.toFile().exists())
LOG.warning(WINDUP_RULESETS_DIR_SYSPROP + " points to a non-existent directory!" + path.toAbsolutePath().toString());
return path;
}
else
return getWindupSubdirectory(RULES_DIRECTORY_NAME);
}
/**
* Conservative approach to insuring that a given filename only contains characters that are legal for use in
* filenames on the disk. Other characters are replaced with underscore _ .
* Note that this should only be used with the filename itself, not the entire path, because it removes the '/' characters as well.
*/
public static String cleanFileName(String badFileName)
{
if (badFileName == null)
return null;
StringBuilder cleanName = new StringBuilder();
for (int i = 0; i < badFileName.length(); i++)
{
int c = (int) badFileName.charAt(i);
if (Character.isJavaIdentifierPart(c))
cleanName.append((char) c);
else
cleanName.append('_');
}
return cleanName.toString();
}
/**
* Converts a path to a class file (like "foo/bar/My.class" or "foo\\bar\\My.class") to a fully qualified class name
* (like "foo.bar.My").
*/
public static String classFilePathToClassname(String relativePath)
{
if (relativePath == null)
return null;
final int pos = relativePath.lastIndexOf(".class");
if (pos < 0 && relativePath.lastIndexOf(".java") < 0)
throw new IllegalArgumentException("Not a .class/.java file path: " + relativePath);
relativePath = FilenameUtils.separatorsToUnix(relativePath);
if (relativePath.startsWith("/"))
{
relativePath = relativePath.substring(1);
}
if (relativePath.startsWith("src/main/java/"))
{
relativePath = relativePath.substring("src/main/java/".length());
}
if (relativePath.startsWith("WEB-INF/classes/"))
{
relativePath = relativePath.substring("WEB-INF/classes/".length());
}
if (relativePath.startsWith("WEB-INF/classes.jdk15/"))
{
relativePath = relativePath.substring("WEB-INF/classes.jdk15/".length());
}
if (relativePath.endsWith(".class"))
{
relativePath = relativePath.substring(0, relativePath.length() - ".class".length());
}
else if (relativePath.endsWith(".java"))
{
relativePath = relativePath.substring(0, relativePath.length() - ".java".length());
}
String qualifiedName = relativePath.replace("/", ".");
return qualifiedName;
}
/**
* Returns the root path for this source file, based upon the package name.
*
* For example, if path is "/project/src/main/java/org/example/Foo.java" and the package is "org.example", then this
* should return "/project/src/main/java".
*
* Returns null if the folder structure does not match the package name.
*/
public static Path getRootFolderForSource(Path sourceFilePath, String packageName)
{
if (packageName == null || packageName.trim().isEmpty())
{
return sourceFilePath.getParent();
}
String[] packageNameComponents = packageName.split("\\.");
Path currentPath = sourceFilePath.getParent();
for (int i = packageNameComponents.length; i > 0; i--)
{
String packageComponent = packageNameComponents[i - 1];
if (!StringUtils.equals(packageComponent, currentPath.getFileName().toString()))
{
return null;
}
currentPath = currentPath.getParent();
}
return currentPath;
}
/**
* Returns true if "file" is a subfile or subdirectory of "dir".
*
* For example with the directory /path/to/a, the following return values would occur:
*
* /path/to/a/foo.txt - true /path/to/a/bar/zoo/boo/team.txt - true /path/to/b/foo.txt - false
*
*/
public static boolean isInSubDirectory(File dir, File file)
{
if (file == null)
return false;
if (file.equals(dir))
return true;
return isInSubDirectory(dir, file.getParentFile());
}
/**
* Attempts to convert a path name (possibly a path within an archive) to a package name.
*/
public static String pathToPackageName(String relativePath)
{
String qualifiedName = classFilePathToClassname(relativePath);
return ClassNameUtil.getPackageName(qualifiedName);
}
/**
* Creates the given directory. Fails if it already exists.
*/
public static void createDirectory(Path dir, String dirDesc)
{
try
{
Files.createDirectories(dir);
}
catch (IOException ex)
{
throw new WindupException("Error creating " + dirDesc + " folder: " + dir.toString() + " due to: " + ex.getMessage(), ex);
}
}
/*
* Helpers
*/
private static Path getUserSubdirectory(String subdirectory)
{
Path windupUserDir = getWindupUserDir();
if (windupUserDir == null)
return null;
return windupUserDir.resolve(subdirectory);
}
private static Path getWindupSubdirectory(String subdirectory)
{
Path windupHome = getWindupHome();
if (windupHome == null)
return null;
return windupHome.resolve(subdirectory);
}
}