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 Disqus

Project 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.