package com.gmail.dpierron.calibre.configuration;
/**
* Class that handles Configuration Management
*
* NOTE: As there should only ever be a single instance of this
* class all vriables and methods are declared static
*/
import com.gmail.dpierron.calibre.datamodel.EBookFormat;
import com.gmail.dpierron.calibre.opds.Constants;
import com.gmail.dpierron.calibre.opds.JDOMManager;
import com.gmail.dpierron.tools.i18n.Localization;
import com.gmail.dpierron.tools.Helper;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.*;
import java.util.*;
public class ConfigurationManager {
public static final String PROFILES_SUFFIX = ".profile.xml";
private final static String PROFILE_FILENAME = "profile.xml";
private final static String DEFAULT_PROFILE = "default";
public static final String LOGGING_PREFIX = "log4j2.";
public static final String LOGGING_SUFFIX = ".xml";
public final static String LOGGING_FILENAME = "log4j2.xml";
private final static String PROPERTY_NAME_CURRENTCONFIGURATION = "CurrentConfiguration";
private final static Logger logger = LogManager.getLogger(ConfigurationManager.class);
private static File configurationDirectory;
// private static File configurationFolder = null;
private static ConfigurationHolder currentProfile;
private static PropertiesBasedConfiguration defaultConfiguration;
private static Locale configLocale = null;
// Listof formats that are used in the current profile
private static List<EBookFormat> profileFormats = null;
/**
* Load up the default set of Configuration settings.
*
* Typically we then load the stored settings for a particular profile.
* However loading default first ensures any new ones get default values.
* @return
*/
public static PropertiesBasedConfiguration getDefaultConfiguration() {
if (defaultConfiguration == null) {
logger.trace("defaultConfiguration is not set");
File file = new File(getConfigurationDirectory(), PROFILE_FILENAME);
logger.trace("file=" + file);
defaultConfiguration = new PropertiesBasedConfiguration(file);
defaultConfiguration.setPropertiesFile(file);
if (file.exists()) {
try {
defaultConfiguration.load();
} catch (IOException e) {
logger.warn(Localization.Main.getText("error.loadingProperties") + ": " + file.getName());
}
} else {
// Create the standard default file
defaultConfiguration.setProperty(PROPERTY_NAME_CURRENTCONFIGURATION, "Default");
defaultConfiguration.save();
}
}
return defaultConfiguration;
}
/**
* Load up the current profile
* If the 'currentProfile' class variable is set then simply return the cached copy.
* If it is null then it needs to be loaded from disk.
*
* The name for the 'currentProfile' should be set prior to calling this method
*
* @return
*/
public static ConfigurationHolder getCurrentProfile() {
String currentProfileNme = getCurrentProfileName();
assert currentProfileNme != null;
if (currentProfile == null) {
logger.trace("getCurrentProfile - currentProfile not set");
currentProfile = new ConfigurationHolder(new File(getConfigurationDirectory(), currentProfileNme + PROFILES_SUFFIX));
Configuration.setConfiguration(currentProfile);
}
return currentProfile;
}
/**
* Get the name of the current profile
* If there is not one already loaded we return the default name for a calibre2opds profile
* @return
*/
public static String getCurrentProfileName() {
String s = getDefaultConfiguration().getProperty(PROPERTY_NAME_CURRENTCONFIGURATION);
if (Helper.isNotNullOrEmpty(s))
return s;
else
return DEFAULT_PROFILE;
}
/**
* Set a new name for the current profile after it has beeb renamed.
* As a consistency check the old name is provided.
*
* @param newName
*/
public static void setCurrentProfileName(String newName) {
getDefaultConfiguration().setProperty(PROPERTY_NAME_CURRENTCONFIGURATION, newName);
}
/**
* Change the current loaded profile.
*
* There is an option as to whether it should become the new currentProfile
*
* @param profileName
* @param setDefault
*/
public static ConfigurationHolder changeProfile(String profileName, boolean setDefault) {
logger.trace("changeProfile to " + profileName);
String currentProfileName = getCurrentProfileName();
getDefaultConfiguration().setProperty(PROPERTY_NAME_CURRENTCONFIGURATION, profileName);
currentProfile = null; // clear cached profile to force a new one to load
getCurrentProfile(); // Load up the new profile
if (setDefault) getDefaultConfiguration().setProperty(PROPERTY_NAME_CURRENTCONFIGURATION, profileName);
return currentProfile;
}
/**
* Copy the current profile to a new one with a given name
*
* @param newProfileName
*/
public static void copyCurrentProfile(String newProfileName) {
getCurrentProfile().setPropertiesFile(new File(getConfigurationDirectory(), newProfileName + PROFILES_SUFFIX));
getCurrentProfile().save();
getDefaultConfiguration().setProperty(PROPERTY_NAME_CURRENTCONFIGURATION, newProfileName);
currentProfile = null;
getCurrentProfile();
}
/**
* See if the configuration already exists
*
* We return the name if matched so that the
* name case is maintained.
*
* @param filename
* @return Null if not found
* Existing name if found
*/
public static String isExistingConfiguration(String filename) {
for (String existingConfigName : getExistingConfigurations()) {
if (existingConfigName.equalsIgnoreCase(filename))
return existingConfigName;
}
return null;
}
/**
*
* @return
*/
public static List<String> getExistingConfigurations() {
File configurationFolder = getConfigurationDirectory();
String[] files = configurationFolder.list(new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.toUpperCase().endsWith(PROFILES_SUFFIX.toUpperCase());
}
});
List<String> result = new LinkedList<String>();
for (String file : files) {
result.add(file.substring(0, file.toUpperCase().indexOf(PROFILES_SUFFIX.toUpperCase())));
}
return result;
}
/**
* Get the Configuration folder
*
* @return
*/
public static File getConfigurationDirectory() {
assert configurationDirectory != null;
return configurationDirectory;
}
/**
* Set the configuration folder
* @param f
*/
public static void setConfigurationDirectory (File f) {
configurationDirectory = f;
}
/**
* Special variant of this that checks several locations for the file before
* resorting to using the built-in resource file. The purpose is to allow
* the user to over-ride the built-in resource files if so required.
* @param filename
* @return
*/
public static InputStream getResourceAsStream(String filename) {
InputStream ins = null;
try {
// Try user configuration folder
ins = new FileInputStream(getConfigurationDirectory() + "/" + filename);
logger.info("Resource '" + filename + "' loaded from Configuration folder");
} catch (FileNotFoundException e) {
try {
// If that fails, try install folder
ins = new FileInputStream (Helper.getInstallDirectory() + "/" + filename);
logger.info("Resource '" + filename + "' loaded from Install folder");
} catch (FileNotFoundException f) {
// If still not found then use built-in resource
ins = JDOMManager.class.getResourceAsStream(filename);
}
}
return ins;
}
public static boolean isHacksEnabled() {
return Helper.isNotNullOrEmpty(System.getenv("CALIBRE2OPDS_HACKSENABLED"));
}
/**
* Set the lcoal that we are using for this generation
* If the one requested is not one we support we set it to English
* @param lc
*/
public static void setLocale (Locale lc){
if (lc == null) {
lc = Locale.getDefault();
logger.debug("setLocale: lc==null. Trying to set to Default Locale: " + lc.getISO3Language());
}
Vector<Locale> avail = Localization.Main.getAvailableLocalizationsAsLocales();
if (avail.contains(lc)) {
configLocale = lc;
} else {
configLocale = Locale.ENGLISH;
logger.trace("setLocale: Requested locale " + lc.getISO3Language() + " is not supported");
logger.trace("setLocale: set to fallback of English (EN)");
}
logger.trace("setLocale: Locale set to " + configLocale.getLanguage() + "(" + configLocale.getDisplayLanguage() + ")");
}
/**
* Get the locale that is to be used for this configuration
* @return
*/
public static Locale getLocale () {
if (configLocale == null) {
logger.trace("getLocale: Not set, so try to set to default");
setLocale(Locale.getDefault());
}
return configLocale;
}
/**
* get the list of supported ebook formats.
*
* We use the function that can read from a user
* configuration file (if present), and if that
* is not present the default resource file
*
* @return
*/
public static void initialiseListOfSupportedEbookFormats () {
if (EBookFormat.getSupportedFormats() != null) {
return;
}
List<EBookFormat> supportedFormats = new LinkedList<EBookFormat>();
InputStream is = ConfigurationManager.getResourceAsStream(Constants.MIMETYPES_FILENAME);
assert is != null;
Scanner scanner = new Scanner(is);
String line;
try {
while (scanner.hasNextLine()) {
line = scanner.nextLine();
// Ignore blank lines and those starting with #
if (line.length() == 0 || line.charAt(0) == '#') {
continue;
}
// Split any line into format identifier and mime type
Scanner lineScanner = new Scanner (line);
String formatType = null;
if (lineScanner.hasNext()) formatType = lineScanner.next();
String mimeType = null;
if (lineScanner.hasNext()) mimeType = lineScanner.next();
if (Helper.isNullOrEmpty(formatType) || Helper.isNullOrEmpty(mimeType)) {
logger.error("Invalid line in Mimetypes file '" + line + "'");
continue;
}
supportedFormats.add(new EBookFormat(formatType,mimeType));
}
scanner.close();
is.close();
} catch (Exception e) {
// Error reading the file
}
EBookFormat.setSupportedFormats(supportedFormats);
}
}