Added a flexible mechanism for persistent boolean-, integer-, and
string-valued preferences built atop java.util.Properties. How it works: the jvm is asked first, and then the user's prefs file, if it exists, then the system-wide prefs file, and then the built-in preferences. Finally, for robustness, a default may be optionally hard-coded in the source. I made several things configurable, too: the default Tibetan keyboard the default font sizes and faces whether you want developer-only features enabled Savant's file extension (.savant) etc. The only known problems are the following: The default location for the user's preferences file is windows-specific, arbitrary, and not in the user documentation. Likewise for the location of the system-wide preferences file. You can change them using 'java -D', though. There is no "Save preferences" option yet, and closing the program does not save preferences either.
This commit is contained in:
parent
b914309dba
commit
08e4e2fc57
8 changed files with 591 additions and 101 deletions
46
build.xml
46
build.xml
|
@ -57,13 +57,12 @@ info on where to find these.
|
||||||
<property name="privatejavadocs" location="${docs}/private-javadocs"/>
|
<property name="privatejavadocs" location="${docs}/private-javadocs"/>
|
||||||
<property name="j2ee.sdk.home" location="F:\Program Files\j2sdkee1.3.1\lib"/>
|
<property name="j2ee.sdk.home" location="F:\Program Files\j2sdkee1.3.1\lib"/>
|
||||||
|
|
||||||
<!-- Set to false for less output when running the tools: -->
|
<!-- Set to true for more output when running the tools and if you
|
||||||
|
want the code to abort when it reaches poorly thought-out
|
||||||
|
control flow paths (i.e., iffy code).
|
||||||
|
-->
|
||||||
<property name="thdl.debug" value="true"/>
|
<property name="thdl.debug" value="true"/>
|
||||||
|
|
||||||
<!-- Set to true if you want the code to abort when it reaches
|
|
||||||
poorly thought-out control flow paths (i.e., iffy code). -->
|
|
||||||
<property name="thdl.die.on.iffy" value="true"/>
|
|
||||||
|
|
||||||
<!-- Set to the name of the dictionary used by the Translation Tool: -->
|
<!-- Set to the name of the dictionary used by the Translation Tool: -->
|
||||||
<property name="arch.dict" value="architecture_dictionary"/>
|
<property name="arch.dict" value="architecture_dictionary"/>
|
||||||
|
|
||||||
|
@ -160,7 +159,7 @@ info on where to find these.
|
||||||
<antcall target="copy-ini-files-to-bin-dir-for-jarring">
|
<antcall target="copy-ini-files-to-bin-dir-for-jarring">
|
||||||
<param name="mybin" value="${jskadbin}"/>
|
<param name="mybin" value="${jskadbin}"/>
|
||||||
</antcall>
|
</antcall>
|
||||||
<antcall target="copy-license-to-bin-dir-for-jarring">
|
<antcall target="copy-license-etc-to-bin-dir-for-jarring">
|
||||||
<param name="mybin" value="${jskadbin}"/>
|
<param name="mybin" value="${jskadbin}"/>
|
||||||
</antcall>
|
</antcall>
|
||||||
<copy todir="${jskadbin}/org/thdl/tib/input">
|
<copy todir="${jskadbin}/org/thdl/tib/input">
|
||||||
|
@ -191,7 +190,7 @@ info on where to find these.
|
||||||
<antcall target="copy-ini-files-to-bin-dir-for-jarring">
|
<antcall target="copy-ini-files-to-bin-dir-for-jarring">
|
||||||
<param name="mybin" value="${savantbin}"/>
|
<param name="mybin" value="${savantbin}"/>
|
||||||
</antcall>
|
</antcall>
|
||||||
<antcall target="copy-license-to-bin-dir-for-jarring">
|
<antcall target="copy-license-etc-to-bin-dir-for-jarring">
|
||||||
<param name="mybin" value="${savantbin}"/>
|
<param name="mybin" value="${savantbin}"/>
|
||||||
</antcall>
|
</antcall>
|
||||||
</target>
|
</target>
|
||||||
|
@ -213,7 +212,7 @@ info on where to find these.
|
||||||
<antcall target="copy-ini-files-to-bin-dir-for-jarring">
|
<antcall target="copy-ini-files-to-bin-dir-for-jarring">
|
||||||
<param name="mybin" value="${ttstandalonebin}"/>
|
<param name="mybin" value="${ttstandalonebin}"/>
|
||||||
</antcall>
|
</antcall>
|
||||||
<antcall target="copy-license-to-bin-dir-for-jarring">
|
<antcall target="copy-license-etc-to-bin-dir-for-jarring">
|
||||||
<param name="mybin" value="${ttstandalonebin}"/>
|
<param name="mybin" value="${ttstandalonebin}"/>
|
||||||
</antcall>
|
</antcall>
|
||||||
</target>
|
</target>
|
||||||
|
@ -230,7 +229,7 @@ info on where to find these.
|
||||||
<param name="my.included.source.file"
|
<param name="my.included.source.file"
|
||||||
value="org/thdl/tib/scanner/WindowScannerFilter.java"/>
|
value="org/thdl/tib/scanner/WindowScannerFilter.java"/>
|
||||||
</antcall>
|
</antcall>
|
||||||
<antcall target="copy-license-to-bin-dir-for-jarring">
|
<antcall target="copy-license-etc-to-bin-dir-for-jarring">
|
||||||
<param name="mybin" value="${tthandheldbin}"/>
|
<param name="mybin" value="${tthandheldbin}"/>
|
||||||
</antcall>
|
</antcall>
|
||||||
</target>
|
</target>
|
||||||
|
@ -252,7 +251,7 @@ info on where to find these.
|
||||||
<antcall target="copy-ini-files-to-bin-dir-for-jarring">
|
<antcall target="copy-ini-files-to-bin-dir-for-jarring">
|
||||||
<param name="mybin" value="${ttappletjwsbin}"/>
|
<param name="mybin" value="${ttappletjwsbin}"/>
|
||||||
</antcall>
|
</antcall>
|
||||||
<antcall target="copy-license-to-bin-dir-for-jarring">
|
<antcall target="copy-license-etc-to-bin-dir-for-jarring">
|
||||||
<param name="mybin" value="${ttappletjwsbin}"/>
|
<param name="mybin" value="${ttappletjwsbin}"/>
|
||||||
</antcall>
|
</antcall>
|
||||||
</target>
|
</target>
|
||||||
|
@ -265,7 +264,7 @@ info on where to find these.
|
||||||
<param name="my.included.source.file"
|
<param name="my.included.source.file"
|
||||||
value="org/thdl/tib/scanner/ConsoleScannerFilter.java"/>
|
value="org/thdl/tib/scanner/ConsoleScannerFilter.java"/>
|
||||||
</antcall>
|
</antcall>
|
||||||
<antcall target="copy-license-to-bin-dir-for-jarring">
|
<antcall target="copy-license-etc-to-bin-dir-for-jarring">
|
||||||
<param name="mybin" value="${ttbin}"/>
|
<param name="mybin" value="${ttbin}"/>
|
||||||
</antcall>
|
</antcall>
|
||||||
</target>
|
</target>
|
||||||
|
@ -292,7 +291,7 @@ info on where to find these.
|
||||||
<antcall target="copy-ini-files-to-bin-dir-for-jarring">
|
<antcall target="copy-ini-files-to-bin-dir-for-jarring">
|
||||||
<param name="mybin" value="${qdbin}"/>
|
<param name="mybin" value="${qdbin}"/>
|
||||||
</antcall>
|
</antcall>
|
||||||
<antcall target="copy-license-to-bin-dir-for-jarring">
|
<antcall target="copy-license-etc-to-bin-dir-for-jarring">
|
||||||
<param name="mybin" value="${qdbin}"/>
|
<param name="mybin" value="${qdbin}"/>
|
||||||
</antcall>
|
</antcall>
|
||||||
</target>
|
</target>
|
||||||
|
@ -538,8 +537,7 @@ info on where to find these.
|
||||||
<pathelement location="${dist}/lib/QuillDriver-${my.jar.suffix}.jar"/>
|
<pathelement location="${dist}/lib/QuillDriver-${my.jar.suffix}.jar"/>
|
||||||
<path refid="entire.class.path"/>
|
<path refid="entire.class.path"/>
|
||||||
</classpath>
|
</classpath>
|
||||||
<jvmarg value="-DTHDL_DEBUG=${thdl.debug}"/>
|
<jvmarg value="-Dthdl.debug=${thdl.debug}"/>
|
||||||
<jvmarg value="-DTHDL_DIE_ON_IFFY_CODE=${thdl.die.on.iffy}"/>
|
|
||||||
</java>
|
</java>
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
|
@ -549,8 +547,7 @@ info on where to find these.
|
||||||
<pathelement location="${dist}/lib/Savant-${my.jar.suffix}.jar"/>
|
<pathelement location="${dist}/lib/Savant-${my.jar.suffix}.jar"/>
|
||||||
<path refid="entire.class.path"/>
|
<path refid="entire.class.path"/>
|
||||||
</classpath>
|
</classpath>
|
||||||
<jvmarg value="-DTHDL_DEBUG=${thdl.debug}"/>
|
<jvmarg value="-Dthdl.debug=${thdl.debug}"/>
|
||||||
<jvmarg value="-DTHDL_DIE_ON_IFFY_CODE=${thdl.die.on.iffy}"/>
|
|
||||||
</java>
|
</java>
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
|
@ -560,8 +557,7 @@ info on where to find these.
|
||||||
<pathelement location="${dist}/lib/Jskad-${my.jar.suffix}.jar"/>
|
<pathelement location="${dist}/lib/Jskad-${my.jar.suffix}.jar"/>
|
||||||
<path refid="entire.class.path"/>
|
<path refid="entire.class.path"/>
|
||||||
</classpath>
|
</classpath>
|
||||||
<jvmarg value="-DTHDL_DEBUG=${thdl.debug}"/>
|
<jvmarg value="-Dthdl.debug=${thdl.debug}"/>
|
||||||
<jvmarg value="-DTHDL_DIE_ON_IFFY_CODE=${thdl.die.on.iffy}"/>
|
|
||||||
</java>
|
</java>
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
|
@ -572,8 +568,7 @@ info on where to find these.
|
||||||
location="${dist}/lib/DictionarySearchStandalone-${my.jar.suffix}.jar"/>
|
location="${dist}/lib/DictionarySearchStandalone-${my.jar.suffix}.jar"/>
|
||||||
<path refid="entire.class.path"/>
|
<path refid="entire.class.path"/>
|
||||||
</classpath>
|
</classpath>
|
||||||
<jvmarg value="-DTHDL_DEBUG=${thdl.debug}"/>
|
<jvmarg value="-Dthdl.debug=${thdl.debug}"/>
|
||||||
<jvmarg value="-DTHDL_DIE_ON_IFFY_CODE=${thdl.die.on.iffy}"/>
|
|
||||||
<!-- For non-swing version: <arg value="-simple"/> -->
|
<!-- For non-swing version: <arg value="-simple"/> -->
|
||||||
<arg value="${arch.dict}"/>
|
<arg value="${arch.dict}"/>
|
||||||
</java>
|
</java>
|
||||||
|
@ -710,12 +705,19 @@ info on where to find these.
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
|
|
||||||
<target name="copy-license-to-bin-dir-for-jarring"
|
<target name="copy-license-etc-to-bin-dir-for-jarring"
|
||||||
depends="init"
|
depends="init"
|
||||||
description="INTERNAL TASK: Copies the license documents to the bin directory. Usually not called directly.">
|
description="INTERNAL TASK: Copies the license documents and the default properties file (hence the et cetera) to the bin directory. Usually not called directly.">
|
||||||
<copy todir="${mybin}">
|
<copy todir="${mybin}">
|
||||||
<fileset dir="${license}"/>
|
<fileset dir="${license}"/>
|
||||||
</copy>
|
</copy>
|
||||||
|
|
||||||
|
<!-- The default properties file, shared by all our tools: -->
|
||||||
|
<copy todir="${mybin}">
|
||||||
|
<fileset dir="${source}">
|
||||||
|
<include name="options.txt"/>
|
||||||
|
</fileset>
|
||||||
|
</copy>
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
|
|
||||||
|
|
81
source/options.txt
Normal file
81
source/options.txt
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
# This is the built-in preferences file for Jskad, Savant,
|
||||||
|
# QuillDriver, and the Translation Tool.
|
||||||
|
#
|
||||||
|
# Please do not edit this file. Instead, create a system-wide or
|
||||||
|
# user-specific properties file, defining just the options that you
|
||||||
|
# want to differ from the defaults given here. See the documentation
|
||||||
|
# for how to do that. Or, if you are a developer or if we haven't
|
||||||
|
# written any documentation yet *smile*, look at
|
||||||
|
# 'http://thdltools.sourceforge.net/api/org/thdl/util/ThdlOptions.html'.
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# This file must remain readable by the
|
||||||
|
# java.util.Properties.load(InputStream) mechanism, so don't just edit
|
||||||
|
# it willy-nilly -- instead, read the comments for each item.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
############################################################################
|
||||||
|
######################### User Preferences ###########################
|
||||||
|
############################################################################
|
||||||
|
|
||||||
|
# Sets the Tibetan keyboard that Jskad uses on startup.
|
||||||
|
# Here are the acceptable values:
|
||||||
|
#
|
||||||
|
# 0: Extended Wylie Keyboard
|
||||||
|
# 1: TCC Keyboard #1
|
||||||
|
# 2: TCC Keyboard #2
|
||||||
|
# 3: Sambhota Keymap One
|
||||||
|
thdl.default.tibetan.keyboard = 0
|
||||||
|
|
||||||
|
# Set this to the default font face for Roman input. No error is
|
||||||
|
# given if this font is not found.
|
||||||
|
thdl.default.roman.font.face = Serif
|
||||||
|
|
||||||
|
# Set this to the default font size for Roman input:
|
||||||
|
thdl.default.roman.font.size = 14
|
||||||
|
|
||||||
|
# Set this to the default font size for Tibetan input:
|
||||||
|
thdl.default.tibetan.font.size = 36
|
||||||
|
|
||||||
|
# The extension that Savant's input files, the titles, must have:
|
||||||
|
thdl.savant.file.extension = .savant
|
||||||
|
|
||||||
|
# Set this to true only if you do not want Jskad to have a status bar.
|
||||||
|
thdl.Jskad.disable.status.bar = false
|
||||||
|
|
||||||
|
# The message displayed in Jskad's status bar when you start it up
|
||||||
|
# with thdl.Jskad.disable.status.bar set to false:
|
||||||
|
thdl.Jskad.initial.status.message = Welcome to Jskad!
|
||||||
|
|
||||||
|
|
||||||
|
############################################################################
|
||||||
|
####################### Developer Preferences ########################
|
||||||
|
############################################################################
|
||||||
|
|
||||||
|
# Set this to true to disable the creation of log files.
|
||||||
|
thdl.disable.log.file = false
|
||||||
|
|
||||||
|
# Set this to true if you want the debugging output, the .log files,
|
||||||
|
# to be placed in your system's temporary directory. You cannot set
|
||||||
|
# this to true AND set thdl.log.directory.
|
||||||
|
thdl.use.temp.file.directory.for.log = false
|
||||||
|
|
||||||
|
# Set this to the directory in which you want the debugging output,
|
||||||
|
# the .log files, placed. You cannot set this to true AND set
|
||||||
|
# thdl.use.temp.file.directory.for.log to true.
|
||||||
|
thdl.log.directory =
|
||||||
|
|
||||||
|
# Set this to true only if you are a developer and want to see what
|
||||||
|
# Jskad is "thinking" as you enter tibetan text. If
|
||||||
|
# thdl.Jskad.disable.status.bar is true, then this will have no
|
||||||
|
# effect.
|
||||||
|
thdl.Jskad.enable.tibetan.mode.status = false
|
||||||
|
|
||||||
|
# Set this to true only if you are a developer and want exceptional
|
||||||
|
# behavior to rear its ugly head as soon as possible:
|
||||||
|
thdl.debug = false
|
||||||
|
|
||||||
|
# Set this to true if you want Savant to treat all files as valid
|
||||||
|
# input. Otherwise, only .savant files are acceptable.
|
||||||
|
thdl.treat.all.files.as.dot.savant.files.regardless.of.extension = false
|
|
@ -24,6 +24,7 @@ import javax.swing.*;
|
||||||
import javax.swing.filechooser.*;
|
import javax.swing.filechooser.*;
|
||||||
|
|
||||||
import org.thdl.util.ThdlDebug;
|
import org.thdl.util.ThdlDebug;
|
||||||
|
import org.thdl.util.ThdlOptions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The SavantFileView "sees through" a <code>*.savant</code> file and
|
* The SavantFileView "sees through" a <code>*.savant</code> file and
|
||||||
|
@ -40,7 +41,10 @@ import org.thdl.util.ThdlDebug;
|
||||||
public class SavantFileView extends FileView {
|
public class SavantFileView extends FileView {
|
||||||
/** When opening a file, this is the only extension Savant cares
|
/** When opening a file, this is the only extension Savant cares
|
||||||
about. This is case-insensitive. */
|
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 <code>*.savant</code> files as properties files and
|
/** This loads <code>*.savant</code> files as properties files and
|
||||||
returns an associated TITLE attribute. For any other type of
|
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
|
unresponsive. In addition, you'll cause the floppy drive
|
||||||
to spin up every time you refresh or cd in the file
|
to spin up every time you refresh or cd in the file
|
||||||
chooser. */
|
chooser. */
|
||||||
if (!Boolean.getBoolean("THDL_TREAT_ALL_FILES_AS_DOT_SAVANT_FILES_REGARDLESS_OF_EXTENSION")) { /* FIXME */
|
if (!ThdlOptions.getBooleanOption("thdl.treat.all.files.as.dot.savant.files.regardless.of.extension")) { /* FIXME */
|
||||||
if (!f.getName().toLowerCase().endsWith(dotSavant))
|
if (!f.getName().toLowerCase().endsWith(getDotSavant()))
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
Properties p = new Properties();
|
Properties p = new Properties();
|
||||||
|
|
|
@ -97,7 +97,7 @@ public class SavantShell extends JFrame
|
||||||
if (f.isDirectory()) {
|
if (f.isDirectory()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return f.getName().toLowerCase().endsWith(SavantFileView.dotSavant);
|
return f.getName().toLowerCase().endsWith(SavantFileView.getDotSavant());
|
||||||
}
|
}
|
||||||
|
|
||||||
//the description of this filter
|
//the description of this filter
|
||||||
|
|
|
@ -33,6 +33,7 @@ import java.io.*;
|
||||||
import javax.swing.text.rtf.*;
|
import javax.swing.text.rtf.*;
|
||||||
|
|
||||||
import org.thdl.util.ThdlDebug;
|
import org.thdl.util.ThdlDebug;
|
||||||
|
import org.thdl.util.ThdlOptions;
|
||||||
import org.thdl.util.StatusBar;
|
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,
|
* This method sets up the editor, assigns fonts and point sizes,
|
||||||
* sets the document, the caret, and adds key and focus listeners.
|
* sets the document, the caret, and adds key and focus listeners.
|
||||||
|
@ -298,15 +313,16 @@ public RTFEditorKit rtfEd = null;
|
||||||
styleContext = new StyleContext();
|
styleContext = new StyleContext();
|
||||||
|
|
||||||
doc = new TibetanDocument(styleContext);
|
doc = new TibetanDocument(styleContext);
|
||||||
doc.setTibetanFontSize(36);
|
doc.setTibetanFontSize(defaultTibFontSize());
|
||||||
setDocument(doc);
|
setDocument(doc);
|
||||||
|
|
||||||
Style defaultStyle = styleContext.getStyle(StyleContext.DEFAULT_STYLE);
|
Style defaultStyle = styleContext.getStyle(StyleContext.DEFAULT_STYLE);
|
||||||
StyleConstants.setFontFamily(defaultStyle, "TibetanMachineWeb");
|
StyleConstants.setFontFamily(defaultStyle, "TibetanMachineWeb");
|
||||||
StyleConstants.setFontSize(defaultStyle, 36);
|
StyleConstants.setFontSize(defaultStyle, defaultTibFontSize()); // FIXME make pref
|
||||||
|
|
||||||
romanFontFamily = "Serif";
|
romanFontFamily = ThdlOptions.getStringOption("thdl.default.roman.font.face",
|
||||||
romanFontSize = 14;
|
"Serif"); // FIXME write out this preference.
|
||||||
|
romanFontSize = defaultRomanFontSize(); // FIXME make pref
|
||||||
setRomanAttributeSet(romanFontFamily, romanFontSize);
|
setRomanAttributeSet(romanFontFamily, romanFontSize);
|
||||||
|
|
||||||
// newDocument();
|
// newDocument();
|
||||||
|
@ -403,12 +419,12 @@ public RTFEditorKit rtfEd = null;
|
||||||
styleContext = new StyleContext();
|
styleContext = new StyleContext();
|
||||||
|
|
||||||
doc = new TibetanDocument(styleContext);
|
doc = new TibetanDocument(styleContext);
|
||||||
doc.setTibetanFontSize(36);
|
doc.setTibetanFontSize(defaultTibFontSize());
|
||||||
setDocument(doc);
|
setDocument(doc);
|
||||||
|
|
||||||
Style defaultStyle = styleContext.getStyle(StyleContext.DEFAULT_STYLE);
|
Style defaultStyle = styleContext.getStyle(StyleContext.DEFAULT_STYLE);
|
||||||
StyleConstants.setFontFamily(defaultStyle, "TibetanMachineWeb");
|
StyleConstants.setFontFamily(defaultStyle, "TibetanMachineWeb");
|
||||||
StyleConstants.setFontSize(defaultStyle, 36);
|
StyleConstants.setFontSize(defaultStyle, defaultTibFontSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1006,9 +1022,9 @@ public void paste(int offset) {
|
||||||
e.consume();
|
e.consume();
|
||||||
initKeyboard();
|
initKeyboard();
|
||||||
if (isTibetan)
|
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
|
else
|
||||||
append("\n", romanAttributeSet);
|
append("\n", romanAttributeSet); // FIXME does this work on all platforms?
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1455,10 +1471,10 @@ public void paste(int offset) {
|
||||||
*/
|
*/
|
||||||
public void toTibetanMachineWeb(String wylie, int offset) {
|
public void toTibetanMachineWeb(String wylie, int offset) {
|
||||||
try {
|
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()) {
|
while (sTok.hasMoreTokens()) {
|
||||||
String next = sTok.nextToken();
|
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 {
|
try {
|
||||||
doc.insertString(offset, next, null);
|
doc.insertString(offset, next, null);
|
||||||
offset++;
|
offset++;
|
||||||
|
|
|
@ -33,6 +33,7 @@ import javax.swing.text.rtf.*;
|
||||||
|
|
||||||
import org.thdl.tib.text.*;
|
import org.thdl.tib.text.*;
|
||||||
import org.thdl.util.ThdlDebug;
|
import org.thdl.util.ThdlDebug;
|
||||||
|
import org.thdl.util.ThdlOptions;
|
||||||
import org.thdl.util.StatusBar;
|
import org.thdl.util.StatusBar;
|
||||||
import org.thdl.util.ThdlActionListener;
|
import org.thdl.util.ThdlActionListener;
|
||||||
import org.thdl.util.RTFPane;
|
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 final String keybd4HelpFile = "Sambhota_keymap_one.rtf";
|
||||||
|
|
||||||
|
|
||||||
private String fontName = "";
|
|
||||||
private JComboBox fontFamilies, fontSizes;
|
private JComboBox fontFamilies, fontSizes;
|
||||||
private JFileChooser fileChooser;
|
private JFileChooser fileChooser;
|
||||||
private javax.swing.filechooser.FileFilter rtfFilter;
|
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 numberOfTibsRTFOpen = 0;
|
||||||
private static int x_size;
|
private static int x_size;
|
||||||
private static int y_size;
|
private static int y_size;
|
||||||
private TibetanKeyboard sambhota1Keyboard = null, duff1Keyboard = null, duff2Keyboard = null;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The text editing window which this Jskad object embeds.
|
* 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.
|
* is a JApplet then the File menu is omitted from the menu bar.
|
||||||
*/
|
*/
|
||||||
public Jskad(final Object parent) {
|
public Jskad(final Object parent) {
|
||||||
if (Boolean.getBoolean("thdl.Jskad.disable.status.bar")) {
|
if (ThdlOptions.getBooleanOption("thdl.Jskad.disable.status.bar")) {
|
||||||
statusBar = null;
|
statusBar = null;
|
||||||
} else {
|
} else {
|
||||||
statusBar = new StatusBar("Welcome to Jskad!");
|
statusBar
|
||||||
|
= new StatusBar(ThdlOptions.getStringOption("thdl.Jskad.initial.status.message",
|
||||||
|
"Welcome to Jskad!"));
|
||||||
}
|
}
|
||||||
parentObject = parent;
|
parentObject = parent;
|
||||||
numberOfTibsRTFOpen++;
|
numberOfTibsRTFOpen++;
|
||||||
|
@ -378,49 +379,35 @@ public class Jskad extends JPanel implements DocumentListener {
|
||||||
toolBar.add(new JLabel("Keyboard:"));
|
toolBar.add(new JLabel("Keyboard:"));
|
||||||
toolBar.addSeparator();
|
toolBar.addSeparator();
|
||||||
|
|
||||||
|
/* Initialize dp before calling installKeyboard. */
|
||||||
|
if (ThdlOptions.getBooleanOption(Jskad.enableKeypressStatusProp)) {
|
||||||
|
dp = new DuffPane(statusBar);
|
||||||
|
} else {
|
||||||
|
dp = new DuffPane();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
String[] keyboard_options = {keybd1Description,
|
String[] keyboard_options = {keybd1Description,
|
||||||
keybd2Description,
|
keybd2Description,
|
||||||
keybd3Description,
|
keybd3Description,
|
||||||
keybd4Description};
|
keybd4Description};
|
||||||
final JComboBox keyboards = new JComboBox(keyboard_options);
|
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() {
|
keyboards.addActionListener(new ThdlActionListener() {
|
||||||
public void theRealActionPerformed(ActionEvent e) {
|
public void theRealActionPerformed(ActionEvent e) {
|
||||||
switch (keyboards.getSelectedIndex()) {
|
installKeyboard(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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
toolBar.add(keyboards);
|
toolBar.add(keyboards);
|
||||||
toolBar.add(Box.createHorizontalGlue());
|
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);
|
JScrollPane sp = new JScrollPane(dp, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
|
||||||
dp.getDocument().addDocumentListener(this);
|
dp.getDocument().addDocumentListener(this);
|
||||||
|
|
||||||
|
@ -786,7 +773,7 @@ public class Jskad extends JPanel implements DocumentListener {
|
||||||
try {
|
try {
|
||||||
BufferedReader in = new BufferedReader(new FileReader(txt_fileChosen));
|
BufferedReader in = new BufferedReader(new FileReader(txt_fileChosen));
|
||||||
DuffPane dp2;
|
DuffPane dp2;
|
||||||
if (Boolean.getBoolean(Jskad.enableKeypressStatusProp)) {
|
if (ThdlOptions.getBooleanOption(Jskad.enableKeypressStatusProp)) {
|
||||||
dp2 = new DuffPane(statusBar);
|
dp2 = new DuffPane(statusBar);
|
||||||
} else {
|
} else {
|
||||||
dp2 = new DuffPane();
|
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;
|
TibetanKeyboard tk = null;
|
||||||
|
|
||||||
if (!name.equalsIgnoreCase("wylie")) {
|
if (!name.equalsIgnoreCase("wylie")) {
|
||||||
|
@ -833,6 +854,7 @@ public class Jskad extends JPanel implements DocumentListener {
|
||||||
}
|
}
|
||||||
catch (TibetanKeyboard.InvalidKeyboardException ike) {
|
catch (TibetanKeyboard.InvalidKeyboardException ike) {
|
||||||
System.out.println("invalid keyboard file or file not found");
|
System.out.println("invalid keyboard file or file not found");
|
||||||
|
ThdlDebug.noteIffyCode();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -840,6 +862,7 @@ public class Jskad extends JPanel implements DocumentListener {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ThdlDebug.verify("dp != null", dp != null);
|
||||||
dp.registerKeyboard(tk);
|
dp.registerKeyboard(tk);
|
||||||
return 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
|
* you discover a bug, please send us an email, making sure to include
|
||||||
* the jskad.log file as an attachment. */
|
* the jskad.log file as an attachment. */
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
ThdlDebug.attemptToSetUpLogFile("jskad", ".log");
|
try {
|
||||||
|
ThdlDebug.attemptToSetUpLogFile("jskad", ".log");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
JFrame f = new JFrame("Jskad");
|
JFrame f = new JFrame("Jskad");
|
||||||
Dimension d = f.getToolkit().getScreenSize();
|
Dimension d = f.getToolkit().getScreenSize();
|
||||||
x_size = d.width/4*3;
|
x_size = d.width/4*3;
|
||||||
y_size = d.height/4*3;
|
y_size = d.height/4*3;
|
||||||
f.setSize(x_size, y_size);
|
f.setSize(x_size, y_size);
|
||||||
f.setLocation(d.width/8, d.height/8);
|
f.setLocation(d.width/8, d.height/8);
|
||||||
f.getContentPane().add(new Jskad(f));
|
f.getContentPane().add(new Jskad(f));
|
||||||
f.setVisible(true);
|
f.setVisible(true);
|
||||||
|
} catch (ThdlLazyException e) {
|
||||||
|
System.err.println("Jskad has a BUG:");
|
||||||
|
e.getRealException().printStackTrace(System.err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@ import java.io.FileOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
import org.thdl.util.TeeStream;
|
import org.thdl.util.TeeStream;
|
||||||
|
import org.thdl.util.ThdlOptions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This uninstantiable class provides assertions and the like in a
|
* This uninstantiable class provides assertions and the like in a
|
||||||
|
@ -64,7 +65,7 @@ public class ThdlDebug {
|
||||||
throw new ThdlLazyException(new Error(((msg == null)
|
throw new ThdlLazyException(new Error(((msg == null)
|
||||||
? "THDL Tools sanity check: "
|
? "THDL Tools sanity check: "
|
||||||
: msg)
|
: 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));
|
+ contactMsg));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -73,15 +74,15 @@ public class ThdlDebug {
|
||||||
* out. For example, if you have to catch an IOException, but
|
* out. For example, if you have to catch an IOException, but
|
||||||
* you're fairly certain that it'll never be thrown, call this
|
* you're fairly certain that it'll never be thrown, call this
|
||||||
* function if it is indeed thrown. Developers can set the
|
* function if it is indeed thrown. Developers can set the
|
||||||
* THDL_DIE_EAGERLY property to true (using <code>'java
|
* thdl.debug property to true (using <code>'java
|
||||||
* -DTHDL_DIE_ON_IFFY_CODE=true'</code>) in order to test the
|
* -Dthdl.debug=true'</code> or the properties files) in order to
|
||||||
* code's robustness.
|
* test the code's robustness.
|
||||||
*
|
*
|
||||||
* Throws a THDL-specific exception so that you can catch these
|
* Throws a THDL-specific exception so that you can catch these
|
||||||
* specially in case you want to ignore them.
|
* specially in case you want to ignore them.
|
||||||
*
|
*
|
||||||
* @throws ThdlLazyException if the THDL_DIE_ON_IFFY_CODE system
|
* @throws ThdlLazyException if the thdl.debug option is set to
|
||||||
* property is set to "true" */
|
* "true" */
|
||||||
public static void noteIffyCode()
|
public static void noteIffyCode()
|
||||||
throws ThdlLazyException
|
throws ThdlLazyException
|
||||||
{
|
{
|
||||||
|
@ -89,8 +90,8 @@ public class ThdlDebug {
|
||||||
/* FIXME: find all calls to this function and rethink or shore
|
/* FIXME: find all calls to this function and rethink or shore
|
||||||
up the calling code. */
|
up the calling code. */
|
||||||
|
|
||||||
if (Boolean.getBoolean("THDL_DIE_ON_IFFY_CODE"))
|
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_DIE_ON_IFFY_CODE set to true (developers: use 'ant -Dthdl.die.on.iffy=false' to prevent this), the program is now aborting."));
|
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
|
/** Exits the program with a message that the CLASSPATH is not set
|
||||||
|
@ -101,7 +102,7 @@ public class ThdlDebug {
|
||||||
|
|
||||||
/* FIXME */
|
/* FIXME */
|
||||||
System.err.println("Note that Savant and QuillDriver CANNOT be invoked via the");
|
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("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("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? */
|
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: "
|
System.err.println("Details: Missing class: "
|
||||||
+ ((error == null)
|
+ ((error == null)
|
||||||
? "unknown!" : error.getMessage()));
|
? "unknown!" : error.getMessage()));
|
||||||
if (Boolean.getBoolean("THDL_DEBUG")) {
|
if (ThdlOptions.getBooleanOption("thdl.debug")) {
|
||||||
System.err.println("Details: Stack trace: "
|
System.err.println("Details: Stack trace: "
|
||||||
+ ((error == null)
|
+ ((error == null)
|
||||||
? "unknown!" : error.getMessage()));
|
? "unknown!" : error.getMessage()));
|
||||||
|
@ -124,18 +125,22 @@ public class ThdlDebug {
|
||||||
* path, because we may put this file into an arbitrary
|
* path, because we may put this file into an arbitrary
|
||||||
* directory. */
|
* directory. */
|
||||||
public static void attemptToSetUpLogFile(String prefix, String suffix) {
|
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 tempDirProp = "thdl.use.temp.file.directory.for.log";
|
||||||
final String logDirProp = "thdl.log.directory";
|
final String logDirProp = "thdl.log.directory";
|
||||||
File logFile = null;
|
File logFile = null;
|
||||||
|
|
||||||
if (Boolean.getBoolean(tempDirProp)) {
|
if (ThdlOptions.getBooleanOption(tempDirProp)) {
|
||||||
/* The log file won't be named 'jskad.log', it'll be named
|
/* The log file won't be named 'jskad.log', it'll be named
|
||||||
'jskad-SAKFJDS3134.log', and they'll all just pile up,
|
'jskad-SAKFJDS3134.log', and they'll all just pile up,
|
||||||
because we don't deleteOnExit. */
|
because we don't deleteOnExit. */
|
||||||
|
|
||||||
/* First, ensure that the user hasn't set conflicting options. */
|
/* First, ensure that the user hasn't set conflicting options. */
|
||||||
try {
|
try {
|
||||||
if (null != System.getProperty("thdl.log.directory"))
|
if (!("".equals(ThdlOptions.getStringOption("thdl.log.directory",
|
||||||
|
""))))
|
||||||
throw new Error("You cannot set the property "
|
throw new Error("You cannot set the property "
|
||||||
+ tempDirProp + " and the property "
|
+ tempDirProp + " and the property "
|
||||||
+ logDirProp
|
+ logDirProp
|
||||||
|
@ -159,13 +164,14 @@ public class ThdlDebug {
|
||||||
thdl.log.directory, respect their choice. */
|
thdl.log.directory, respect their choice. */
|
||||||
String logDir = null;
|
String logDir = null;
|
||||||
try {
|
try {
|
||||||
logDir = System.getProperty(logDirProp);
|
logDir = ThdlOptions.getStringOption(logDirProp, "");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
/* SecurityExceptions, e.g., will trigger this. We
|
/* SecurityExceptions, e.g., will trigger this. We
|
||||||
leave logDir null. */
|
leave logDir null. */
|
||||||
noteIffyCode();
|
noteIffyCode();
|
||||||
}
|
}
|
||||||
if (null != logDir) {
|
if (!("".equals(ThdlOptions.getStringOption("thdl.log.directory",
|
||||||
|
"")))) {
|
||||||
logFile = new File(logDir, prefix + suffix);
|
logFile = new File(logDir, prefix + suffix);
|
||||||
} else {
|
} else {
|
||||||
/* Create the log file in the current directory. For
|
/* Create the log file in the current directory. For
|
||||||
|
|
353
source/org/thdl/util/ThdlOptions.java
Normal file
353
source/org/thdl/util/ThdlOptions.java
Normal file
|
@ -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).
|
||||||
|
*
|
||||||
|
* <p>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 <code>java -Dpref=value -jar jskad_or_whatever.jar</code>. 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.</p>
|
||||||
|
*
|
||||||
|
* <p>ThdlOptions is not instantiable. It contains only static
|
||||||
|
* methods for answering questions about the user's preferences.</p>
|
||||||
|
*
|
||||||
|
* <p>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.</p>
|
||||||
|
*
|
||||||
|
* <p>ThdlOptions is a final class so that compilers can make this
|
||||||
|
* code run efficiently.</p> */
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue