ProblemYou need to store keys and values that are both strings, possibly with persistence across runs of a program for example, program customization. SolutionUse a java.util.Prefs.Preferences object (JDK 1.4 and above) or a java.util.Properties object. DiscussionHere are three approaches to customization based on the user's environment. Java offers Preferences and Properties for cross-platform customizations; for Windows only deployments, a Java-based commercial product can do the trick. PreferencesThe Preferences class java.util.prefs.Preferences (added in SDK 1.4) is intended to provide an easier-to-use mechanism for storing user customizations in a system-dependent way (which might mean dot files on Unix, a preferences file on the Mac, or the registry on Windows systems). This new class provides a hierarchical set of nodes representing a user's preferences. Data is stored in the system-dependent storage format but can also be exported to or imported from an XML format. Here is a simple demonstration of Preferences: // PrefsDemo.java // Set up the Preferences for this application, by class. Preferences prefs = Preferences.userNodeForPackage(PrefsDemo.class); // Retrieve some preferences previously stored, with defaults in case // this is the first run. String text = prefs.get("textFontName", "lucida-bright"); String display = prefs.get("displayFontName", "lucida-blackletter"); System.out.println(text); System.out.println(display); // Assume the user chose new preference values: Store them back. prefs.put("textFontName", "times-roman"); prefs.put("displayFontName", "helvetica"); When you run the PrefsDemo program the first time, of course, it doesn't find any settings, so the calls to preferences.get( ) return the default values: > javac PrefsDemo.java > java PrefsDemo lucida-bright lucida-blackletter On subsequent runs, it finds and returns the "user provided" settings: > java PrefsDemo times-roman helvetica > PropertiesThe Properties class is similar to a HashMap or Hashtable (it extends the latter), but with methods defined specifically for string storage and retrieval and for loading/saving. Properties objects are used throughout Java, for everything from setting the platform font names to customizing user applications into different Locale settings as part of internationalization and localization. When stored on disk, a Properties object looks just like a series of name=value assignments, with optional comments. Comments are added when you edit a Properties file by hand, ignored when the Properties object reads itself, and lost when you ask the Properties object to save itself to disk. Here is an example of a Properties file that could be used to internationalize the menus in a GUI-based program: # Default properties for MenuIntl program.title=Demonstrate I18N (MenuIntl) program.message=Welcome to an English-localized Java Program # # The File Menu # file.label=File Menu file.new.label=New File file.new.key=N file.open.label=Open... file.open.key=O file.save.label=Save file.save.key=S file.exit.label=Exit file.exit.key=Q Here is another example, showing some personalization properties: name=Ian Darwin favorite_popsicle=cherry favorite_rock group=Fleetwood Mac favorite_programming_language=Java pencil color=green A Properties object can be loaded from a file. The rules are flexible: either = , :, or spaces can be used after a key name and its values. Spaces after a non-space character are ignored in the key. Backslash can be used to continue lines or to escape other characters. Comment lines may begin with either # or !. Thus, a Properties file containing the previous items, if prepared by hand, could look like this: # Here is a list of properties ! first, my name name Ian Darwin favorite_popsicle = cherry favorite_rock\ group \ Fleetwood Mac favorite_programming_language=Java pencil\ color green Fortunately, when a Properties object writes itself to a file, it uses the simple format: key=value Here is an example of a program that creates a Properties object and adds into it the list of companies and their locations from Recipe 7.6. It then loads additional properties from disk. To simplify the I/O processing, the program assumes that the Properties file to be loaded is contained in the standard input, as would be done using a command-line redirection on either Unix or DOS: import java.util.*; public class PropsCompanies { public static void main(String argv[]) throws java.io.IOException { Properties props = new Properties( ); // Get my data. props.setProperty("Adobe", "Mountain View, CA"); props.setProperty("IBM", "White Plains, NY"); props.setProperty("Learning Tree", "Los Angeles, CA"); props.setProperty("O'Reilly & Associates", "Sebastopol, CA"); props.setProperty("Netscape", "Mountain View, CA"); props.setProperty("Sun", "Mountain View, CA"); // Now load additional properties props.load(System.in); // Now list the merged Properties, using System.out props.list(System.out); } } JDK 1.2 added setProperty( ); prior to that, the put( ) method of parent class Hashtable was used. Running it as: java PropsCompanies < PropsDemo.dat produces the following output: -- listing properties -- Sony=Japan Sun=Mountain View, CA IBM=White Plains, NY Netscape=Mountain View, CA Nippon_Kogaku=Japan Acorn=United Kingdom Adobe=Mountain View, CA Ericsson=Sweden O'Reilly & Associates=Sebastopol, CA Learning Tree=Los Angeles, CA In case you didn't notice in either the HashMap or the Properties examples, the order that the outputs appear in these examples is neither sorted nor in the same order we put them in. The hashing classes and the Properties subclass make no claim about the order in which objects are retrieved. If you need them sorted, see Recipe Recipe 7.8. As a convenient shortcut, my FileProperties class includes a constructor that takes a filename, as in: import com.darwinsys.util.FileProperties; ... Properties p = new FileProperties("PropsDemo.dat"); Note that constructing a FileProperties object causes it to be loaded, and therefore the constructor may throw a checked exception of class IOException. Commercial solution for Windows registry accessThough it is platform-specific, Cogent Logic produces a JNDI (Java Naming and Directory Interface) service provider for accessing the Windows registry, which can also be used for preferences. JNDI is a general naming and directory lookup that, like java.util.prefs.Preference, is better suited than Properties for dealing with hierarchical data. Cogent Logic's product gives you both local and (subject to security arrangements) remote access to preferences on a Windows system. See http://cogentlogic.com/jndi/. |