package livework;
/* (c) Alex McLean 2006
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*/
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.nio.charset.*;
import java.util.*;
import java.util.regex.*;
import livework.Util;
/**
* Singleton class for managing user settings for the application.
* User settings strings can be set and retreived. On instantiantion the
* settings are checked for required fields, which are set with default values
* as necessary.
Settings are saved to a file "preferences.txt" under a
* directory called '.livework' under the user's home directory. This is
* conventional under UNIX based systems, but may annoy users of other
* operating systems.
* @author Alex McLean - ma503@gold.ac.uk
*/
public class Settings {
/** Singleton object */
static private Settings singleton = new Settings();
/** Holds the settings in memory */
private Hashtable table = new Hashtable();
/** File in which the settings are stored */
private File settingsFile;
/** Directory in which settings and other user data are saved. */
private File dataFolder;
/**
* Constructor for this class
* Cannot be called externally, call getInstance() instead.
*/
protected Settings() {
initSettings();
}
/**
* Retreives the singleton object for this class.
* @return The settings object.
*/
public static Settings getInstance() {
return(singleton);
}
/**
* Makes the data folder and settings file if they're missing
*/
private void initSettings() {
File home = new File(System.getProperty("user.home"));
dataFolder = new File(home, ".livework");
if (!dataFolder.exists()) {
if (! dataFolder.mkdirs()) {
Util.error("Startup error", "Couldn't make settings directory '" + dataFolder.getAbsolutePath() + "'", null);
}
}
settingsFile = new File(dataFolder, "preferences.txt");
if (!settingsFile.exists()) {
// save defaults
initRequired();
save();
}
else {
try {
load();
}
catch (IOException e) {
Util.error(null, "Couldn't load settings", e);
}
}
}
/**
* Saves settings to the settings file.
*/
public void save () {
try {
FileOutputStream output = new FileOutputStream(settingsFile);
PrintWriter writer = new PrintWriter(new OutputStreamWriter(output));
Enumeration keys = table.keys();
while (keys.hasMoreElements()) {
String key = (String) keys.nextElement();
writer.println(key + ": " + ((String) table.get(key)));
}
writer.flush();
writer.close();
} catch (IOException ex) {
Util.error("File error", "Couldn't write to settings file " + settingsFile.getAbsolutePath(), ex);
}
}
/**
* Loads the settings file. Would
* rarely need to be called externally, as it's
* called automatically when this object is first instantiated.
* @throws IOException thrown where there is a problem loading the settings file
*/
public void load() throws IOException {
// load into a buffer
FileChannel channel = new FileInputStream(settingsFile).getChannel();
ByteBuffer buffer =
channel.map(FileChannel.MapMode.READ_ONLY, 0, (int) channel.size());
// decode as unicode, in case this software is ever used by greeks
CharBuffer charBuffer = Charset.forName("UTF-8").newDecoder().decode(buffer);
// Run some matches
Pattern regex = Pattern.compile("([a-zA-Z]+):\\s*(.*)");
Matcher m = regex.matcher(charBuffer);
while (m.find()) {
table.put(m.group(1).trim(), m.group(2).trim());
}
checkSettings();
initRequired();
}
/**
* Get a setting
* @param key The setting to be retrieved
* @return The value of the setting
*/
public String get(String key) {
return((String) table.get(key));
}
/**
* Set a setting
* @param key The setting to be set
* @param value The new value for the setting
*/
public void put(String key, String value) {
table.put(key, value);
}
/**
* Static version of the set method
*/
public static String sget(String key) {
return(getInstance().get(key));
}
/**
* Static version of the put method
*/
public static void sput(String key, String value) {
getInstance().put(key, value);
}
/**
* Checks that the settings are valid, wiping any
* invalid values.
*/
private void checkSettings() {
String path = get("liveClasspath");
if (path != null) {
File file = new File(path);
if (! (file.exists() && file.isDirectory()
&& file.canWrite() && file.canRead()
)
) {
// can't use that path, wipe it so that one is
// calculated later
put("liveClasspath", null);
}
}
}
/**
* Initialises required values and save()s if necessary.
*/
private void initRequired() {
boolean saveNeeded = false;
if (get("liveClasspath") == null || get("liveClasspath") == "") {
initLiveClasspath();
saveNeeded = true;
}
if (saveNeeded) {
save();
}
}
/**
* Initialises the "liveclasses" setting, where livecoded classes are saved and compiled.
* The directory is created if necessary.
*/
private void initLiveClasspath() {
File liveClassFolder = new File(dataFolder, "liveclasses");
if (!liveClassFolder.exists()) {
if (! liveClassFolder.mkdirs()) {
Util.error("Startup error", "Couldn't make liveclasses folder '" + liveClassFolder.getAbsolutePath() + "'", null);
}
}
put("liveClasspath", liveClassFolder.getAbsolutePath());
}
}