/*
* This file is part of the Illarion project.
*
* Copyright © 2015 - Illarion e.V.
*
* Illarion is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Illarion is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
package illarion.common.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
/**
* This class is used to manage the global directory manager that takes care for the directories the applications need
* to use.
*
* @author Martin Karing <nitram@illarion.org>
*/
public final class DirectoryManager {
@SuppressWarnings("LoggerInitializedWithForeignClass")
private static final class LazyHolder {
@Nonnull
static final Logger log = LoggerFactory.getLogger(DirectoryManager.class);
}
/**
* Get the logger.
* <p />
* This function is around to avoid the initialization of the log in this function as it may happen to early in
* the runtime of the applications.
*
* @return the logging instance
*/
@Nonnull
private static Logger getLog() {
return LazyHolder.log;
}
/**
* The enumeration of directories that are managed by this manager.
*/
public enum Directory {
/**
* The user directory that stores the user related data like log files, character data and settings.
*/
User,
/**
* The data directory that stores the application binary data required to launch the applications.
*/
Data
}
/**
* The singleton instance of this class.
*/
@Nonnull
private static final DirectoryManager INSTANCE = new DirectoryManager();
/**
* The detected working directory.
*/
@Nonnull
private final Path workingDirectory;
/**
* The binary directory that got selected.
*/
@Nullable
private Path binaryDirectory;
/**
* Private constructor to ensure that only the singleton instance exists.
*/
private DirectoryManager() {
String installationDir = System.getProperty("org.illarion.install.dir");
workingDirectory = Paths.get((installationDir == null) ? "." : installationDir);
binaryDirectory = null;
Path userDir = getDirectory(Directory.User);
if (Files.isRegularFile(userDir)) {
try {
Files.delete(userDir);
} catch (IOException e) {
getLog().error("Failed to delete old .illarion file.", e);
}
}
if (!Files.isDirectory(userDir)) {
try {
Files.createDirectories(userDir);
} catch (IOException e) {
getLog().error("Failed to create the .illarion directory.", e);
}
}
}
/**
* Get the singleton instance of this class.
*
* @return the singleton instance
*/
@Nonnull
public static DirectoryManager getInstance() {
return INSTANCE;
}
/**
* Get the location of the specified directory in the local file system.
*
* @param dir the directory
* @return the location of the directory in the local file system or {@code null} in case the directory is not set
*/
@Nonnull
public Path getDirectory(@Nonnull Directory dir) {
switch (dir) {
case User:
if (System.getProperty("os.name").contains("Mac OS X")) {
return Paths.get(System.getProperty("user.home"), "Library", "org.illarion");
}
return Paths.get(System.getProperty("user.home"), ".illarion");
case Data:
return getBinaryDirectory();
}
throw new IllegalArgumentException("Parameter 'dir' was set to an illegal value: " + dir);
}
@Nonnull
private Path getBinaryDirectory() {
if (binaryDirectory == null) {
Path firstChoice = workingDirectory.resolve("bin");
if (!Files.exists(firstChoice)) {
try {
return Files.createDirectories(firstChoice);
} catch (IOException ignored) {
// not accessible
}
}
if (Files.isDirectory(firstChoice)) {
try {
Path temporaryTestFile = firstChoice.resolve("writing.test");
if (Files.isRegularFile(temporaryTestFile)) {
Files.delete(temporaryTestFile);
}
Path newCreatedFile = Files.createFile(temporaryTestFile);
if (Files.isRegularFile(temporaryTestFile)) {
binaryDirectory = firstChoice;
}
Files.delete(newCreatedFile);
} catch (IOException e) {
getLog().info("Accessing the directory failed: {}", e.getMessage());
}
}
if (binaryDirectory == null) {
Path userDir = getDirectory(Directory.User);
binaryDirectory = userDir.resolve("bin");
assert binaryDirectory != null;
if (!Files.exists(binaryDirectory)) {
try {
return Files.createDirectories(binaryDirectory);
} catch (IOException e) {
getLog().error("Critical error! No possible binary directory.");
}
}
}
}
return binaryDirectory;
}
@Nonnull
public Path resolveFile(@Nonnull Directory dir, @Nonnull String... segments) {
Path result = getDirectory(dir);
for (String segment : segments) {
result = result.resolve(segment);
}
return result;
}
/**
* In case the directory manager supports relative directories, this is the working directory the client needs to
* be launched in.
*
* @return the working directory or {@code null} in case none is supported
*/
@Nonnull
public Path getWorkingDirectory() {
return workingDirectory;
}
}