*.savant
file and
@@ -40,7 +41,10 @@ import org.thdl.util.ThdlDebug;
public class SavantFileView extends FileView {
/** When opening a file, this is the only extension Savant cares
about. This is case-insensitive. */
- public final static String dotSavant = ".savant";
+ public final static String getDotSavant() {
+ return ThdlOptions.getStringOption("thdl.savant.file.extension",
+ ".savant");
+ }
/** This loads *.savant
files as properties files and
returns an associated TITLE attribute. For any other type of
@@ -56,8 +60,8 @@ public class SavantFileView extends FileView {
unresponsive. In addition, you'll cause the floppy drive
to spin up every time you refresh or cd in the file
chooser. */
- if (!Boolean.getBoolean("THDL_TREAT_ALL_FILES_AS_DOT_SAVANT_FILES_REGARDLESS_OF_EXTENSION")) { /* FIXME */
- if (!f.getName().toLowerCase().endsWith(dotSavant))
+ if (!ThdlOptions.getBooleanOption("thdl.treat.all.files.as.dot.savant.files.regardless.of.extension")) { /* FIXME */
+ if (!f.getName().toLowerCase().endsWith(getDotSavant()))
return null;
}
Properties p = new Properties();
diff --git a/source/org/thdl/savant/SavantShell.java b/source/org/thdl/savant/SavantShell.java
index 6742078..9da80b4 100644
--- a/source/org/thdl/savant/SavantShell.java
+++ b/source/org/thdl/savant/SavantShell.java
@@ -97,7 +97,7 @@ public class SavantShell extends JFrame
if (f.isDirectory()) {
return true;
}
- return f.getName().toLowerCase().endsWith(SavantFileView.dotSavant);
+ return f.getName().toLowerCase().endsWith(SavantFileView.getDotSavant());
}
//the description of this filter
diff --git a/source/org/thdl/tib/input/DuffPane.java b/source/org/thdl/tib/input/DuffPane.java
index caf9cce..1174371 100644
--- a/source/org/thdl/tib/input/DuffPane.java
+++ b/source/org/thdl/tib/input/DuffPane.java
@@ -33,6 +33,7 @@ import java.io.*;
import javax.swing.text.rtf.*;
import org.thdl.util.ThdlDebug;
+import org.thdl.util.ThdlOptions;
import org.thdl.util.StatusBar;
/**
@@ -284,6 +285,20 @@ public RTFEditorKit rtfEd = null;
}
*/
+ private static int defaultTibFontSize() {
+ // FIXME: at program exit, or when the user selects "Save
+ // preferences", or somehow, save the value the users chooses:
+ return ThdlOptions.getIntegerOption("thdl.default.tibetan.font.size",
+ 36);
+ }
+
+ private static int defaultRomanFontSize() {
+ // FIXME: at program exit, or when the user selects "Save
+ // preferences", or somehow, save the value the users chooses:
+ return ThdlOptions.getIntegerOption("thdl.default.roman.font.size",
+ 14);
+ }
+
/**
* This method sets up the editor, assigns fonts and point sizes,
* sets the document, the caret, and adds key and focus listeners.
@@ -298,15 +313,16 @@ public RTFEditorKit rtfEd = null;
styleContext = new StyleContext();
doc = new TibetanDocument(styleContext);
- doc.setTibetanFontSize(36);
+ doc.setTibetanFontSize(defaultTibFontSize());
setDocument(doc);
Style defaultStyle = styleContext.getStyle(StyleContext.DEFAULT_STYLE);
StyleConstants.setFontFamily(defaultStyle, "TibetanMachineWeb");
- StyleConstants.setFontSize(defaultStyle, 36);
+ StyleConstants.setFontSize(defaultStyle, defaultTibFontSize()); // FIXME make pref
- romanFontFamily = "Serif";
- romanFontSize = 14;
+ romanFontFamily = ThdlOptions.getStringOption("thdl.default.roman.font.face",
+ "Serif"); // FIXME write out this preference.
+ romanFontSize = defaultRomanFontSize(); // FIXME make pref
setRomanAttributeSet(romanFontFamily, romanFontSize);
// newDocument();
@@ -403,12 +419,12 @@ public RTFEditorKit rtfEd = null;
styleContext = new StyleContext();
doc = new TibetanDocument(styleContext);
- doc.setTibetanFontSize(36);
+ doc.setTibetanFontSize(defaultTibFontSize());
setDocument(doc);
Style defaultStyle = styleContext.getStyle(StyleContext.DEFAULT_STYLE);
StyleConstants.setFontFamily(defaultStyle, "TibetanMachineWeb");
- StyleConstants.setFontSize(defaultStyle, 36);
+ StyleConstants.setFontSize(defaultStyle, defaultTibFontSize());
}
/**
@@ -1006,9 +1022,9 @@ public void paste(int offset) {
e.consume();
initKeyboard();
if (isTibetan)
- doc.appendDuff(caret.getDot(),"\n",TibetanMachineWeb.getAttributeSet(1));
+ doc.appendDuff(caret.getDot(),"\n",TibetanMachineWeb.getAttributeSet(1)); // FIXME does this work on all platforms?
else
- append("\n", romanAttributeSet);
+ append("\n", romanAttributeSet); // FIXME does this work on all platforms?
break;
}
}
@@ -1455,10 +1471,10 @@ public void paste(int offset) {
*/
public void toTibetanMachineWeb(String wylie, int offset) {
try {
- StringTokenizer sTok = new StringTokenizer(wylie, "\n\t", true);
+ StringTokenizer sTok = new StringTokenizer(wylie, "\n\t", true); // FIXME does this work on all platforms?
while (sTok.hasMoreTokens()) {
String next = sTok.nextToken();
- if (next.equals("\n") || next.equals("\t")) {
+ if (next.equals("\n") || next.equals("\t")) { // FIXME does this work on all platforms?
try {
doc.insertString(offset, next, null);
offset++;
diff --git a/source/org/thdl/tib/input/Jskad.java b/source/org/thdl/tib/input/Jskad.java
index cf88bf1..1e27e4a 100644
--- a/source/org/thdl/tib/input/Jskad.java
+++ b/source/org/thdl/tib/input/Jskad.java
@@ -33,6 +33,7 @@ import javax.swing.text.rtf.*;
import org.thdl.tib.text.*;
import org.thdl.util.ThdlDebug;
+import org.thdl.util.ThdlOptions;
import org.thdl.util.StatusBar;
import org.thdl.util.ThdlActionListener;
import org.thdl.util.RTFPane;
@@ -80,7 +81,6 @@ public class Jskad extends JPanel implements DocumentListener {
private final String keybd4HelpFile = "Sambhota_keymap_one.rtf";
- private String fontName = "";
private JComboBox fontFamilies, fontSizes;
private JFileChooser fileChooser;
private javax.swing.filechooser.FileFilter rtfFilter;
@@ -90,7 +90,6 @@ public class Jskad extends JPanel implements DocumentListener {
private static int numberOfTibsRTFOpen = 0;
private static int x_size;
private static int y_size;
- private TibetanKeyboard sambhota1Keyboard = null, duff1Keyboard = null, duff2Keyboard = null;
/**
* The text editing window which this Jskad object embeds.
@@ -115,10 +114,12 @@ public class Jskad extends JPanel implements DocumentListener {
* is a JApplet then the File menu is omitted from the menu bar.
*/
public Jskad(final Object parent) {
- if (Boolean.getBoolean("thdl.Jskad.disable.status.bar")) {
+ if (ThdlOptions.getBooleanOption("thdl.Jskad.disable.status.bar")) {
statusBar = null;
} else {
- statusBar = new StatusBar("Welcome to Jskad!");
+ statusBar
+ = new StatusBar(ThdlOptions.getStringOption("thdl.Jskad.initial.status.message",
+ "Welcome to Jskad!"));
}
parentObject = parent;
numberOfTibsRTFOpen++;
@@ -378,49 +379,35 @@ public class Jskad extends JPanel implements DocumentListener {
toolBar.add(new JLabel("Keyboard:"));
toolBar.addSeparator();
+ /* Initialize dp before calling installKeyboard. */
+ if (ThdlOptions.getBooleanOption(Jskad.enableKeypressStatusProp)) {
+ dp = new DuffPane(statusBar);
+ } else {
+ dp = new DuffPane();
+ }
+
+
String[] keyboard_options = {keybd1Description,
keybd2Description,
keybd3Description,
keybd4Description};
final JComboBox keyboards = new JComboBox(keyboard_options);
+ int initialKeyboard
+ = ThdlOptions.getIntegerOption("thdl.default.tibetan.keyboard", 0);
+ try {
+ keyboards.setSelectedIndex(initialKeyboard);
+ } catch (IllegalArgumentException e) {
+ keyboards.setSelectedIndex(0); // good ol' Wylie
+ }
+ installKeyboard(initialKeyboard);
keyboards.addActionListener(new ThdlActionListener() {
public void theRealActionPerformed(ActionEvent e) {
- switch (keyboards.getSelectedIndex()) {
- case 0: //Extended Wylie
- installKeyboard("wylie");
- break;
-
- case 1: //TCC 1
- if (duff1Keyboard == null)
- duff1Keyboard = installKeyboard("tcc1");
- else
- dp.registerKeyboard(duff1Keyboard);
- break;
-
- case 2: //TCC 2
- if (duff2Keyboard == null)
- duff2Keyboard = installKeyboard("tcc2");
- else
- dp.registerKeyboard(duff2Keyboard);
- break;
-
- case 3: //Sambhota
- if (sambhota1Keyboard == null)
- sambhota1Keyboard = installKeyboard("sambhota1");
- else
- dp.registerKeyboard(sambhota1Keyboard);
- break;
- }
+ installKeyboard(keyboards.getSelectedIndex());
}
});
toolBar.add(keyboards);
toolBar.add(Box.createHorizontalGlue());
- if (Boolean.getBoolean(Jskad.enableKeypressStatusProp)) {
- dp = new DuffPane(statusBar);
- } else {
- dp = new DuffPane();
- }
JScrollPane sp = new JScrollPane(dp, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
dp.getDocument().addDocumentListener(this);
@@ -786,7 +773,7 @@ public class Jskad extends JPanel implements DocumentListener {
try {
BufferedReader in = new BufferedReader(new FileReader(txt_fileChosen));
DuffPane dp2;
- if (Boolean.getBoolean(Jskad.enableKeypressStatusProp)) {
+ if (ThdlOptions.getBooleanOption(Jskad.enableKeypressStatusProp)) {
dp2 = new DuffPane(statusBar);
} else {
dp2 = new DuffPane();
@@ -812,7 +799,41 @@ public class Jskad extends JPanel implements DocumentListener {
}
}
- private TibetanKeyboard installKeyboard(String name) {
+ private TibetanKeyboard sambhota1Keyboard = null, duff1Keyboard = null, duff2Keyboard = null;
+ private void installKeyboard(int keyboardIndex) {
+ switch (keyboardIndex) {
+ case 1: //TCC 1
+ if (duff1Keyboard == null)
+ duff1Keyboard = installKeyboard("tcc1");
+ else
+ dp.registerKeyboard(duff1Keyboard);
+ break;
+
+ case 2: //TCC 2
+ if (duff2Keyboard == null)
+ duff2Keyboard = installKeyboard("tcc2");
+ else
+ dp.registerKeyboard(duff2Keyboard);
+ break;
+
+ case 3: //Sambhota
+ if (sambhota1Keyboard == null)
+ sambhota1Keyboard = installKeyboard("sambhota1");
+ else
+ dp.registerKeyboard(sambhota1Keyboard);
+ break;
+ default: //Extended Wylie
+ if (0 != keyboardIndex
+ && ThdlOptions.getBooleanOption("thdl.debug")) {
+ throw new Error("You set thdl.default.tibetan.keyboard to an illegal value: "
+ + keyboardIndex);
+ }
+ installKeyboard("wylie");
+ break;
+ }
+ }
+
+ private TibetanKeyboard installKeyboard(String name) {
TibetanKeyboard tk = null;
if (!name.equalsIgnoreCase("wylie")) {
@@ -833,6 +854,7 @@ public class Jskad extends JPanel implements DocumentListener {
}
catch (TibetanKeyboard.InvalidKeyboardException ike) {
System.out.println("invalid keyboard file or file not found");
+ ThdlDebug.noteIffyCode();
return null;
}
}
@@ -840,6 +862,7 @@ public class Jskad extends JPanel implements DocumentListener {
return null;
}
+ ThdlDebug.verify("dp != null", dp != null);
dp.registerKeyboard(tk);
return tk;
}
@@ -1074,22 +1097,27 @@ public class Jskad extends JPanel implements DocumentListener {
* you discover a bug, please send us an email, making sure to include
* the jskad.log file as an attachment. */
public static void main(String[] args) {
- ThdlDebug.attemptToSetUpLogFile("jskad", ".log");
+ try {
+ ThdlDebug.attemptToSetUpLogFile("jskad", ".log");
- try {
- UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
- }
- catch (Exception e) {
- }
+ try {
+ UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+ }
+ catch (Exception e) {
+ }
- JFrame f = new JFrame("Jskad");
- Dimension d = f.getToolkit().getScreenSize();
- x_size = d.width/4*3;
- y_size = d.height/4*3;
- f.setSize(x_size, y_size);
- f.setLocation(d.width/8, d.height/8);
- f.getContentPane().add(new Jskad(f));
- f.setVisible(true);
+ JFrame f = new JFrame("Jskad");
+ Dimension d = f.getToolkit().getScreenSize();
+ x_size = d.width/4*3;
+ y_size = d.height/4*3;
+ f.setSize(x_size, y_size);
+ f.setLocation(d.width/8, d.height/8);
+ f.getContentPane().add(new Jskad(f));
+ f.setVisible(true);
+ } catch (ThdlLazyException e) {
+ System.err.println("Jskad has a BUG:");
+ e.getRealException().printStackTrace(System.err);
+ }
}
}
diff --git a/source/org/thdl/util/ThdlDebug.java b/source/org/thdl/util/ThdlDebug.java
index e35c304..177dcbf 100644
--- a/source/org/thdl/util/ThdlDebug.java
+++ b/source/org/thdl/util/ThdlDebug.java
@@ -23,6 +23,7 @@ import java.io.FileOutputStream;
import java.io.File;
import org.thdl.util.TeeStream;
+import org.thdl.util.ThdlOptions;
/**
* This uninstantiable class provides assertions and the like in a
@@ -64,7 +65,7 @@ public class ThdlDebug {
throw new ThdlLazyException(new Error(((msg == null)
? "THDL Tools sanity check: "
: msg)
- + "An assertion failed. This means that there is a bug in this software."
+ + "\nAn assertion failed. This means that there is a bug in this software."
+ contactMsg));
}
}
@@ -73,15 +74,15 @@ public class ThdlDebug {
* out. For example, if you have to catch an IOException, but
* you're fairly certain that it'll never be thrown, call this
* function if it is indeed thrown. Developers can set the
- * THDL_DIE_EAGERLY property to true (using 'java
- * -DTHDL_DIE_ON_IFFY_CODE=true'
) in order to test the
- * code's robustness.
+ * thdl.debug property to true (using 'java
+ * -Dthdl.debug=true'
or the properties files) in order to
+ * test the code's robustness.
*
* Throws a THDL-specific exception so that you can catch these
* specially in case you want to ignore them.
*
- * @throws ThdlLazyException if the THDL_DIE_ON_IFFY_CODE system
- * property is set to "true" */
+ * @throws ThdlLazyException if the thdl.debug option is set to
+ * "true" */
public static void noteIffyCode()
throws ThdlLazyException
{
@@ -89,8 +90,8 @@ public class ThdlDebug {
/* FIXME: find all calls to this function and rethink or shore
up the calling code. */
- if (Boolean.getBoolean("THDL_DIE_ON_IFFY_CODE"))
- throw new ThdlLazyException(new Error("You've reached some iffy code, some code that's not well thought-out. Because you invoked the Java runtime environment with the property THDL_DIE_ON_IFFY_CODE set to true (developers: use 'ant -Dthdl.die.on.iffy=false' to prevent this), the program is now aborting."));
+ if (ThdlOptions.getBooleanOption("thdl.debug"))
+ throw new ThdlLazyException(new Error("You've reached some iffy code, some code that's not well thought-out. Because you invoked the Java runtime environment with the property thdl.debug set to true (developers: use 'ant -Dthdl.debug=false' to prevent this), or because you set the thdl.debug preference to true in one of the preferences files, the program is now aborting."));
}
/** Exits the program with a message that the CLASSPATH is not set
@@ -101,7 +102,7 @@ public class ThdlDebug {
/* FIXME */
System.err.println("Note that Savant and QuillDriver CANNOT be invoked via the");
- System.err.println("'java -jar Savant-xyz.jar' option, because that silently ignores");
+ System.err.println("'java -jar Savant-xyz.jar' option, because that silently ignores"); // FIXME yes you can when Java Web Start is up and running
System.err.println("the CLASSPATH. This means that double-clicking them won't work");
System.err.println("either, because we don't set the JARs' manifest files to contain");
System.err.println("Class-path attributes. See installation instructions."); /* FIXME: we don't HAVE installation instructions, do we? */
@@ -109,7 +110,7 @@ public class ThdlDebug {
System.err.println("Details: Missing class: "
+ ((error == null)
? "unknown!" : error.getMessage()));
- if (Boolean.getBoolean("THDL_DEBUG")) {
+ if (ThdlOptions.getBooleanOption("thdl.debug")) {
System.err.println("Details: Stack trace: "
+ ((error == null)
? "unknown!" : error.getMessage()));
@@ -124,18 +125,22 @@ public class ThdlDebug {
* path, because we may put this file into an arbitrary
* directory. */
public static void attemptToSetUpLogFile(String prefix, String suffix) {
+ if (ThdlOptions.getBooleanOption("thdl.disable.log.file"))
+ return;
+ // Else:
final String tempDirProp = "thdl.use.temp.file.directory.for.log";
final String logDirProp = "thdl.log.directory";
File logFile = null;
- if (Boolean.getBoolean(tempDirProp)) {
+ if (ThdlOptions.getBooleanOption(tempDirProp)) {
/* The log file won't be named 'jskad.log', it'll be named
'jskad-SAKFJDS3134.log', and they'll all just pile up,
because we don't deleteOnExit. */
/* First, ensure that the user hasn't set conflicting options. */
try {
- if (null != System.getProperty("thdl.log.directory"))
+ if (!("".equals(ThdlOptions.getStringOption("thdl.log.directory",
+ ""))))
throw new Error("You cannot set the property "
+ tempDirProp + " and the property "
+ logDirProp
@@ -159,13 +164,14 @@ public class ThdlDebug {
thdl.log.directory, respect their choice. */
String logDir = null;
try {
- logDir = System.getProperty(logDirProp);
+ logDir = ThdlOptions.getStringOption(logDirProp, "");
} catch (Exception e) {
/* SecurityExceptions, e.g., will trigger this. We
leave logDir null. */
noteIffyCode();
}
- if (null != logDir) {
+ if (!("".equals(ThdlOptions.getStringOption("thdl.log.directory",
+ "")))) {
logFile = new File(logDir, prefix + suffix);
} else {
/* Create the log file in the current directory. For
diff --git a/source/org/thdl/util/ThdlOptions.java b/source/org/thdl/util/ThdlOptions.java
new file mode 100644
index 0000000..cf961a5
--- /dev/null
+++ b/source/org/thdl/util/ThdlOptions.java
@@ -0,0 +1,353 @@
+/*
+The contents of this file are subject to the THDL Open Community License
+Version 1.0 (the "License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License on the THDL web site
+(http://www.thdl.org/).
+
+Software distributed under the License is distributed on an "AS IS" basis,
+WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+License for the specific terms governing rights and limitations under the
+License.
+
+The Initial Developer of this software is the Tibetan and Himalayan Digital
+Library (THDL). Portions created by the THDL are Copyright 2001 THDL.
+All Rights Reserved.
+
+Contributor(s): ______________________________________.
+*/
+
+package org.thdl.util;
+
+import java.io.InputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.FileNotFoundException;
+import java.util.Properties;
+
+import org.thdl.util.ThdlLazyException;
+
+/**
+ * @author David Chandler
+ *
+ * Provides a clean interface to the multi-tiered system of user
+ * preferences (also known as options).
+ *
+ * The {@link java.util.Properties java.util.Properties} class
+ * makes it easy for us to provide a hierarchical preferences (that
+ * is, options) structure. One option a user might wish to set is the
+ * font size for Tibetan, for example. The highest precedence goes to
+ * a pref that the user set on the Java command line using something
+ * like java -Dpref=value -jar jskad_or_whatever.jar
. If
+ * the user went to the trouble of doing this, we definitely want to
+ * respect his or her wish. The next highest precedence goes to an
+ * individual user's preferences file (a file readable and writable by
+ * {@link java.util.Properties java.util.Properties}, but also
+ * hand-editable), if one exists. Next is the system-wide preferences
+ * file, if one exists. Finally, we fall back on the preferences file
+ * shipped with the application inside the JAR.
ThdlOptions is not instantiable. It contains only static + * methods for answering questions about the user's preferences.
+ * + *There are three kinds of preferences: boolean-valued preferences + * ("true" or "false"), integer-valued preferences, and string-valued + * preferences. Boolean-valued preferences should, by convention, be + * false by default. If you want to enable feature foo by default, + * but give a preference for disabling it, then call the preference + * "DisableFeatureFoo". If you want to disable feature foo by + * default, call it "EnableFeatureFoo". This makes the users' lives + * easier.
+ * + *ThdlOptions is a final class so that compilers can make this + * code run efficiently.
*/ +public final class ThdlOptions { + /** + * So that you're not tempted to instantiate this class, the + * constructor is private: */ + private ThdlOptions() { + // don't instantiate this class. + } + + /** + * Returns the value of a boolean-valued option, or false if that + * option is set nowhere in the hierarchy of properties files. + * + * @param optionName the option whose value you wish to retrieve + * (note the naming conventions detailed in the class comment) + */ + public static boolean getBooleanOption(String optionName) + { + init(); + + // Look to the System first. + String answer = getSystemValue(optionName); + if (answer == null) { + answer = userProperties.getProperty(optionName, "false"); + } + return answer.equalsIgnoreCase("true"); + } + + // FIXMEDOC + private static String getSystemValue(String optionName) { + // Look to the System first. + String answer = null; + try { + answer = System.getProperty(optionName); + } catch (SecurityException e) { + if (!suppressErrs()) + throw e; + } + return answer; + } + + /** + * Returns the value of a string-valued option, or null if that + * option is set nowhere in the hierarchy of properties files. + * + * @param optionName the option whose value you wish to retrieve + * (note the naming conventions detailed in the class comment) */ + public static String getStringOption(String optionName) + { + init(); + // Look to the System first. + String answer = getSystemValue(optionName); + if (answer == null) { + answer = userProperties.getProperty(optionName, null); + } + return answer; + } + + /** + * Returns the value of a string-valued option, or defaultValue if that + * option is set nowhere in the hierarchy of properties files. + * + * @param optionName the option whose value you wish to retrieve + * (note the naming conventions detailed in the class comment) + * @param defaultValue the default value */ + public static String getStringOption(String optionName, + String defaultValue) + { + String x = getStringOption(optionName); + if (null == x) + return defaultValue; + else + return x; + } + + /** + * Returns the value of an integer-valued option, or defaultValue + * if that option is set nowhere in the hierarchy of properties + * files or is set to something that cannot be decoded to an + * integer. + * + * @param optionName the option whose value you wish to retrieve + * (note the naming conventions detailed in the class comment) + * @param defaultValue the default value */ + public static int getIntegerOption(String optionName, int defaultValue) + { + Integer x = getIntegerOption(optionName); + if (null == x) + return defaultValue; + else + return x.intValue(); + } + + /** + * Returns the value of an integer-valued option, or null if that + * option is set nowhere in the hierarchy of properties files or + * is set to something that cannot be decoded to an integer. + * + * @param optionName the option whose value you wish to retrieve + * (note the naming conventions detailed in the class comment) */ + public static Integer getIntegerOption(String optionName) + { + init(); + + // Look to the System first. + String answer = getSystemValue(optionName); + if (answer == null) { + answer = userProperties.getProperty(optionName, null); + } + if (null == answer) { + return null; + } else { + try { + return Integer.decode(answer); + } catch (NumberFormatException e) { + if (getBooleanOption("thdl.debug")) + throw new ThdlLazyException(e); + else + return null; + } + } + } + + private static boolean suppressErrs() { + return false; /* FIXME--make THIS configurable. */ + } + private static void init() { + try { + initialize(ThdlOptions.class, "/options.txt", + suppressErrs()); + } catch (FileNotFoundException e) { + if (!suppressErrs()) + throw new ThdlLazyException(e); + } + } + + /** the properties object that is chained to the system-wide + properties which is chained to the built-in properties which + is chained to the Java System properties */ + private static Properties userProperties = null; + + /** to avoid initializing twice */ + private static boolean isInitialized = false; + + /** Sets userProperties so that it represents the entire, chained + preferences hierarchy. + + @param resourceHolder the class associated with the builtin + defaults properties file resource + @param resourceName the name of the builtin defaults + properties file resource + @param suppressErrors true if the show must go on, false if + you want unchecked exceptions thrown when bad things happen + + @throws FileNotFoundException if !suppressErrors and if the + user gave the location of the system-wide or user preferences + files, but gave it incorrectly. */ + private static void initialize(Class resourceHolder, + String resourceName, + boolean suppressErrors) + throws FileNotFoundException + { + if (isInitialized) + return; + isInitialized = true; + try { + + // Get the application's built-in, default properties. + Properties defaultProperties + = getPropertiesFromResource(resourceHolder, + resourceName, + suppressErrors, + new Properties() /* empty */); + + // Get the system's properties, if the system administrator + // has created any: + Properties systemWideProperties + = tryToGetPropsFromFile("thdl.system.wide.options.file", + // FIXME this default is + // system-dependent: + "C:\\thdl_opt.txt", + defaultProperties, + suppressErrors); + + // Get the user's properties, if they've set any: + userProperties + = tryToGetPropsFromFile("thdl.user.options.file", + // FIXME this default is + // system-dependent: + "C:\\thdl_uopt.txt", + systemWideProperties, + suppressErrors); + } catch (SecurityException e) { + if (suppressErrors) { + if (userProperties == null) { + userProperties = new Properties(); // empty + } // else leave it as is. + } else { + throw new ThdlLazyException(e); + } + } + } + + /** Returns a new, nonnull Properties object if (1) a preferences + file is found at the location specified by the value of the + System property pName, if it is set, or at defaultLoc, if + pName is not set, and (2) if that file is successfully read + in. Otherwise, this returns defaultProps. + + @param pName the name of the System property that overrides + this application's default idea of where to look for the file + @param defaultLoc the default preferences file name + @param suppressErrors true iff you want to proceed without + throwing exceptions whenever possible + + @throws FileNotFoundException if !suppressErrors and if the + user gave the location of the system-wide or user preferences + files, but gave it incorrectly. @throws SecurityException if + playing with files or system properties is not OK */ + private static Properties tryToGetPropsFromFile(String pName, + String defaultLoc, + Properties defaultProps, + boolean suppressErrors) + throws FileNotFoundException, SecurityException + { + Properties props = defaultProps; + String systemPropFileName = System.getProperty(pName, defaultLoc); + FileInputStream fis = null; + try { + fis = new FileInputStream(systemPropFileName); + } catch (FileNotFoundException e) { + if (null != System.getProperty(pName)) { + // the user explicitly set this, but not + // correctly. + if (!suppressErrors) + throw e; + } else { + // definitely suppress this. On a Mac or + // Unix/Linux box, this'll happen every time + // at present. (FIXME) + } + } + + if (fis != null) { + props = getPropertiesFromStream(fis, + suppressErrors, + defaultProps); + } + return props; + } + + + // FIXMEDOC + private static Properties getPropertiesFromResource(Class resourceHolder, + String resourceName, + boolean suppressErrors, + Properties defaults) + { + InputStream in = resourceHolder.getResourceAsStream(resourceName); + return getPropertiesFromStream(in, suppressErrors, defaults); + } + + // FIXMEDOC + private static Properties getPropertiesFromStream(InputStream in, + boolean suppressErrors, + Properties defaults) + throws NullPointerException + { + Properties options; + if (defaults == null) + options = new Properties(); // empty properties list + else + options = new Properties(defaults); + try { + // Load props from the resource: + options.load(in); + return options; + } catch (Exception e) { + // e is an IOException or a SecurityException or, if the + // resource was not found, a NullPointerException. + if (suppressErrors) { + return options; + } else { + throw new ThdlLazyException(e); + } + } + } +} + + + +