Penlets.com provides resources for users and developers of the Pulse smart pen from LiveScribe.
Subscribe: RSS Feed - Developers Group
Tutorial
Using Properties Files
Tutorial by Robert Hanson
For us "regular" Java developers it can be a source of frustration
trying to work with J2ME. One such case I recently encountered was
the lack of a
Properties
class. In this article I will provide you with a home-made edition of
Properties
, as well as useful string utilities such as
split
and
chomp
.
In my mind reading a properties file should be simple. Slurp in the
file, split the file on the newlines, then split again on the equals
(=), and stick the key-value pairs into a
Map
. Unfortunately J2ME's
String
doesn't have a
split
method, so lets start there.
More than one way to skin a cat Using split is not the most efficient way of parsing a properties file, but it does the job just fine. If you prefer another approach check out the source code from Neto Marin's article on J2ME Internationalization.
import java.util.Vector;
public class StringUtils
{
public static String[] split (String str, char sep, int maxNum)
{
if (str == null || str.length() == 0) { /* [1] */
return new String[0];
}
/* [2] */
Vector results = maxNum == 0 ? new Vector() : new Vector(maxNum);
StringBuffer buf = new StringBuffer();
for (int i = 0; i < str.length(); i++) { /* [3] */
char c = str.charAt(i);
if (c == sep) {
if (maxNum != 0) { /* [4] */
if ((results.size() + 1) == maxNum) {
for (; i < str.length(); i++) {
buf.append(str.charAt(i));
}
break;
}
}
results.addElement(buf.toString());
buf.setLength(0);
}
else {
buf.append(c);
}
}
if (buf.length() > 0) {
results.addElement(buf.toString());
}
return toStringArray(results); /* [5] */
}
public static String[] toStringArray (Vector strings)
{
String[] result = new String[strings.size()];
for (int i = 0; i < strings.size(); i++) {
result[i] = strings.elementAt(i).toString();
}
return result;
}
}
The
split
method presented here takes a string value, a single character
separator, and a maximum number of values to return. This is similar
to the split method in the JRE, so it should look pretty familiar.
The algorithm is essentially to iterate over the characters in the in the input string [3], and when a separator character is encountered add the characters prior to the separator to the resulting array. Once the end of the input string is reached add the characters following the last separator match to the result.
In the method presented we add some additional features. At the
beginning of the method we check for a
null
or empty input string
[1] and quickly return. We also use a
Vector
to capture the resulting values
[2], using the
maxNum
to set the
Vector
capacity if known.
Allowing the caller to specify a maximum number of splits required us to be slurp all remaining characters when the limit is reached [4].
When we return we need to convert the
Vector
that we have been using to store the results of the function to an
array
[5]. This introduces a new method
toStringArray
, which has potential use outside of the split function.
With the
split
method in hand I thought I was done, but I ran into one small
problem. When I split the properties file (with the yet to be seen
Properties
class), I found that the lines ended not in "\n", but with "\r\n".
I could have solved this in another way perhaps, but I decided to
take the easy way out and added an additional
chomp
method to
StringUtils
.
import java.util.Vector;
public class StringUtils
{
/* ... hiding the split and toStringAray methods ... */
public static String chomp (String inStr)
{
if (inStr == null || "".equals(inStr)) {
return inStr;
}
char lastChar = inStr.charAt(inStr.length() - 1);
if (lastChar == 13) {
return inStr.substring(0, inStr.length() - 1);
}
else if (lastChar == 10) {
String tmp = inStr.substring(0, inStr.length() - 1);
if ("".equals(tmp)) {
return tmp;
}
lastChar = tmp.charAt(tmp.length() - 1);
if (lastChar == 13) {
return tmp.substring(0, tmp.length() - 1);
}
else {
return tmp;
}
}
else {
return inStr;
}
}
}
Without getting into the details, the
chomp
method examines the input string and removes any training newline
("\n") or return-newline ("\r\n"). We again do an initial check for a
null
or empty input string and short-circuit if true.
This brings us to the
Properties
class. It is presented here in its entirety to aid is copying and
asting the code into your own project.
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Enumeration;
import java.util.Hashtable;
public class Properties
{
private Hashtable propTable = new Hashtable();
/* [1] */
public static Properties loadProperties (String filePath) throws IOException
{
Properties result = new Properties();
InputStream stream = result.getClass().getResourceAsStream(filePath);
InputStreamReader reader = new InputStreamReader(stream);
StringBuffer sBuf = new StringBuffer();
char[] buff = new char[1024];
/* [2] */
int pos = reader.read(buff, 0, 1024);
while (pos != -1) {
sBuf.append(buff, 0, pos);
pos = reader.read(buff, 0, 1024);
}
/* [3] */
String[] lines = StringUtils.split(sBuf.toString(), '\n', 0);
for (int i = 0; i < lines.length; i++) {
String[] kv = StringUtils.split(StringUtils.chomp(lines[i]), '=', 2);
if (kv.length == 1) {
result.setProperty(kv[0], "");
}
if (kv.length == 2) {
result.setProperty(kv[0], kv[1]);
}
}
return result;
}
public void setProperty (String key, String val)
{
this.propTable.put(key, val);
}
public String getProperty (String key)
{
return (String) this.propTable.get(key);
}
public int getPropertyCount ()
{
return this.propTable.size();
}
public Enumeration getEnumeratedNames ()
{
return this.propTable.keys();
}
public String[] getPropertyNames ()
{
String[] result = new String[this.propTable.size()];
int c = 0;
for (Enumeration e = this.propTable.keys(); e.hasMoreElements();) {
result[c] = (String) e.nextElement();
c++;
}
return result;
}
}
Although we can create a new instance of
Properties
, the more interesting use would be to call the static method
Properties.loadProperties(file)
[1]. Before we get into the details on using it,
let's briefly explore the class.
The
loadProperties
method has no error checking and simple rethrows any
IOException
encountered, so be sure to account for this if you need to. The
method itself does exactly what we explained, it slurps in the
properties file
[2], then splits the lines and key-value pairs
[3].
Once the properties are loaded you can get/set properties, and get an
array of property names or an
Enumeration
. This is a simplistic implementation of the
Properties
class from the JRE, but it is usually all you would need.
Now when you use this you need to be aware of how your penlet
resources are stored in the JAR file, and how to address their
location. To illustrate, take the following project structure with a
an
assets.properties
file.
/src/java ... your code ... /res assets.properties
When you compile this with the LiveScript SDK it will copy everything
in the
/res
directory to the base of the resulting JAR file. Because
assets.properties
has moved to the root of the JAR, we would load it like this.
Properties props = Properties.loadProperties("/assets.properties");
String prop1 = props.getProperty("prop1");
Note the leading slash to indicate the root of the JAR file.
When you use this for real remember that it isn't exactly optimized for memory usage, but unless you are loading a huge properties file, this won't be an issue. You may also want to consider loading the properties when penlet is initialized instead of each time it is activated. This is more efficient in most cases.
Until next time, happy coding!
Comments (View) blog comments powered by DisqusProject Information
Tested for use with: PreRelease-SDK
Download the source code for this tutorial.
Download Source Code (zip)
New Tutorials
Using a Shared Library
Learn how to create a library of utils that you can
share between projects.
Capturing Drawn Shapes
Learn how to capture shapes drawn by the pen and determine relationships.
Penlets 101
Never written a penlet before? Then start here with Penlets 101!
Creating a Custom Vocabulary
Learn how to create a custom vocabulary for your ICR applications.
Using Properties Files
J2ME lacks a Properties class. In this tutorial we roll our own,
along with split and chomp functions.