MultiProperties

R

Roedy Green

Ordinary java.util.Properties have two drawbacks:

1. spaces must be awkwardly encoded as "\ "
2. there is no provision for a value to have multiple values.

Here is a simple replacement for the Properties class that gets around
that.

It is a little clumsy. Here is the code to do with as you please:


package com.mindprod.replicator;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.regex.Pattern;

/**
* This class is similar to java.util.Properties.
* The properties file has a similar key=value format.
* However, spaces in the values are not coded as "\ ", just as plain
" ".
* Values can be a tuple separated by commas.
* Multiple values are returned by get as a String[].
* Single values are returned as String.
* There is no support for extended chars such as \n.
* There is no support for \ as a continuation character.
* Comment lines begin with #.
* Blank lines are ignored.
* The file must end with a separator.
* All values are trimmed of leading and trailing spaces.
*
* It is basically just a Hashtable with a load method.
*
* @author Roedy Green
* @version 1.0
* @since 2003-08-03
*/
public class MultiProperties extends Hashtable
{

/**
* true if you want the debugging harness code.
*/
private static final boolean DEBUG = true;

/**
* Constructs a new, empty hashtable with the specified initial
* capacity and the specified load factor.
* See http://mindprod.com/jgloss/hashtable.html for a full
description of
* what the initialCapacity and loadFactors mean.
*
* @param initialCapacity the initial capacity of the
hashtable.
* @param loadFactor the load factor of the hashtable.
* @exception IllegalArgumentException if the initial capacity is
less
* than zero, or if the load factor is nonpositive.
*/
public MultiProperties ( int initialCapacity, float loadFactor )
{
super( initialCapacity, loadFactor );
}

/**
* Load the properties hasthtable from a text file of key=value
pairs.
*
* @param in where to load the textual key=value pairs from.
*/
public void load ( InputStream in ) throws IOException
{
BufferedReader br = new BufferedReader( new InputStreamReader(
in ) );

while ( true )
{
String line = br.readLine();
if ( line == null )
{
/* eof */
break;
}
if ( line.startsWith( "#" ) )
{
/* ignore comments */
continue;
}
line = line.trim();
if ( line.length() == 0 )
{
/* ignore blank lines */
continue;
}

// split line into key and value
String[] keyValue = keyValueSplitter.split( line );
switch ( keyValue.length )
{
case 1:
{
// key= nothing
String key = keyValue[0].trim();
if ( key.length() == 0 )
{
throw new IllegalArgumentException( "malformed
key=value : " + line );
}
this.put( key, "" );
}
break;

case 2:
{
// key=value
String key = keyValue[0].trim();
if ( key.length() == 0 )
{
throw new IllegalArgumentException( "malformed
key=value : " + line );
}
// Split value into subfields
String[] values = subFieldSplitter.split(
keyValue[1].trim() );
switch ( values.length )
{
case 0:
// empty value
this.put( key, "" );
break;

case 1:
// one value
this.put( key, values[0].trim() );
break;

default:
// multiple values
for ( int i=0; i<values.length; i++ )
{
values = values.trim();
}
this.put ( key, values );
break;
}
}
break;

default:
throw new IllegalArgumentException( "malformed
key=value : " + line );
}
} // end while
br.close();
} // end load

// Pattern to split line into key and value at the =
private static Pattern keyValueSplitter = Pattern.compile ( "=" );

// Pattern to split into words separated by commas. two commas in a
row gives an empty field
private static Pattern subFieldSplitter = Pattern.compile ( "," );

/**
* test harness
*
* @param args not used
*/
public static void main ( String[] args )
{
if ( DEBUG )
{
MultiProperties m = new MultiProperties ( 100, .75f );
try
{
m.load( new FileInputStream ( "replicator.properties" ) );
}
catch ( IOException e )
{
e.printStackTrace();
System.exit( 1 );
}
for ( Enumeration e = m.keys(); e.hasMoreElements(); )
{
String key = (String) e.nextElement();
Object value = m.get( key );
System.out.println( key + " =");
if ( value instanceof String[] )
{
String[] values = (String[]) value;
for ( int i=0; i<values.length; i++ )
{
System.out.println ( values );
}
}
else
{
System.out.println ( (String) value );
}
} // end for
} // end if DEBUG
} // end main
} // end MultiProperties
 
R

Roedy Green

Ordinary java.util.Properties have two drawbacks:

1. spaces must be awkwardly encoded as "\ "
2. there is no provision for a value to have multiple values.

Here is a simple replacement for the Properties class that gets around
that.

It is a little clumsy.

I thought about it then changed the user interface you can specify
specifically what you are expecting, so you don't need to fool around
with the result to decide if it is suitable.

It is fairly slick for initialising a set of static finals from a
properties file.

Typical code to use it looks like this:

/**
* Key=value pairs, used to populate constants from a properties
file.
*/
private static MultiProperties m;
static {
m = new MultiProperties ( 20, .75f );
try
{
m.load( new FileInputStream ( "replicator.properties" ) );
}
catch ( IOException e )
{
e.printStackTrace();
System.exit( 1 );
}
}
/**
* Directory without trailing \ where all the master files to be
distributed are kept.
* Use local platform names e.g. E:\mindprod for Windows. This name
is purely for the local
* master copy, not for the website or the clients.
*/
public static final String SOURCE_BASE_DIR = m.get(
"SOURCE_BASE_DIR", "c:\\" );

/**
* Approximately how many files will be distributed.
* Aim a little on the high side for maximum efficiency.
*/
public static final int ESTIMATED_FILES_TO_DISTRIBUTE = m.getInt(
"ESTIMATED_FILES_TO_DISTRIBUTE", 15000 );

/**
* Which entire directory trees in the base directory will
* be distributed. The directory and all its subdirectories will
* be distributed. The name must not begin or end with a \,
* though it may contain embedded \.
*/
public static final String[] TREES_TO_DISTRIBUTE = m.getMultiple (
"TREES_TO_DISTRIBUTE" );

/**
* Which individual directories in the base directory will
* be distributed. The directory but none of its subdirectories
will
* be distributed. The name may not begin or end with a \,
* though it may contain embedded \.
*/
public static final String[] DIRS_TO_DISTRIBUTE = m.getMultiple (
"DIRS_TO_DISTRIBUTE" );



Here is the new version:

package com.mindprod.replicator;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.regex.Pattern;

/**
* This class is similar to java.util.Properties.
* The properties file has a similar key=value format.
* However, for MultiProperties,
* spaces in the values are not coded
* as "\ ", just as plain " ".
* Values can be a tuple separated by commas.
* The inherited Hashtable get returns a String[].
* There is no support for extended chars such as \n.
* There is no support for \ as a continuation character.
* Comment lines begin with #.
* Blank lines are ignored.
* The file must end with a line separator.
* All values are trimmed of leading and trailing spaces.
*
* It is basically just a Hashtable with a load method
* and a few conveniences added to the get method.
*
* @author Roedy Green
* @version 1.0
* @since 2003-08-03
*/
public class MultiProperties extends Hashtable
{

/**
* true if you want the debugging harness code.
*/
private static final boolean DEBUG = true;

/**
* Constructs a new, empty hashtable with
* the specified initial
* capacity and the specified load factor.
* See http://mindprod.com/jgloss/hashtable.html
* for a full description of
* what the initialCapacity and loadFactors mean.
*
* @param initialCapacity the initial capacity of the
hashtable.
* @param loadFactor the load factor of the hashtable.
* @exception IllegalArgumentException if the initial capacity is
less
* than zero, or if the load factor is nonpositive.
*/
public MultiProperties ( int initialCapacity, float loadFactor )
{
super( initialCapacity, loadFactor );
}

/**
* Load the properties hasthtable usually
* from a text file of key=value pairs.
*
* @param in where to load the textual key=value pairs from.
* @exception IOException
*/
public void load ( InputStream in ) throws IOException
{
BufferedReader br = new BufferedReader( new InputStreamReader(
in ) );

while ( true )
{
String line = br.readLine();
if ( line == null )
{
/* eof */
break;
}
if ( line.startsWith( "#" ) )
{
/* ignore comments */
continue;
}
line = line.trim();
if ( line.length() == 0 )
{
/* ignore blank lines */
continue;
}

// split line into key and value
String[] keyValue = keyValueSplitter.split( line );
switch ( keyValue.length )
{
case 1:
{
// key=nothing
String key = keyValue[0].trim();
if ( key.length() == 0 )
{
complain( line );
}
this.put( key, dummy );
}
break;

case 2:
{
// key=value
String key = keyValue[0].trim();
if ( key.length() == 0 )
{
complain( line );
}
// Split value into subfields
String[] values = subFieldSplitter.split(
keyValue[1].trim() );

// trim the multiple values
for ( int i=0; i<values.length; i++ )
{
values = values.trim();
}
// save in the underlying Hashtable.
this.put ( key, values );
}
break;

default:
case 0:
complain( line );
}
} // end while
br.close();
} // end load

/**
* Complain about malformed data.
*
* @param line Line of key=value that has a problem.
*/
private static void complain ( String line )
{
throw new IllegalArgumentException( "MultiProperties: malformed
key=value : "
+ line );

}

/**
* Get values associated with key.
*
* @param key Key, case sensitive.
*
* @return array of associated Strings, possibly dimension 0.
* If key is undefined returns empty array, not null.
*/
public String[] getMultiple ( String key )
{
String[] value = (String[]) get( key );
if ( value == null )
{
return dummy;
}
else
{
return value;
}
}

/**
* Get value associated with key.
*
* @param key Key, case sensitive.
* @param defaultValue
* Value for the default if the key is not defined.
* key=nothing returns "", not the default value.
*
* @return String for a single value, or the first of a set of
mulitple values, or "".
*/
public String get ( String key, String defaultValue )
{
Object value = get( key );
if ( value == null )
{
return defaultValue;
}
else
{
String[] array = ((String[])value);
if ( array.length != 0 )
{
return array[0];
}
else
{
return ""; // not defaultValue!
}
}
}


/**
* Get single integer value associated with key.
*
* @param key Key, case sensitive.
* @param defaultValue
* Value for the default if the key is not defined.
*
* @return integer value of the key, or defaultValue if not defined
or if key=nothing.
* @throws NumberFormatException if the value is not a valid
integer.
*/
public int getInt ( String key, int defaultValue ) throws
NumberFormatException
{
Object value = get( key );
if ( value == null )
{
return defaultValue;
}
else
{
String[] array = ((String[])value);
if ( array.length != 0 )
{
return Integer.parseInt ( array[0] );
}
else
{
return defaultValue;
}
}

}

/**
* An dummy empty array of Strings.
* No point is allocating a fresh one every time it is needed.
*/
private static String[] dummy = new String[ 0 ];

// Pattern to split line into key and value at the =
private static Pattern keyValueSplitter = Pattern.compile ( "=" );

// Pattern to split into words separated by commas. two commas in a
row gives an empty field
private static Pattern subFieldSplitter = Pattern.compile ( "," );

/**
* test harness
*
* @param args not used
*/
public static void main ( String[] args )
{
if ( DEBUG )
{
MultiProperties m = new MultiProperties ( 100, .75f );
try
{
m.load( new FileInputStream ( "replicator.properties" ) );
}
catch ( IOException e )
{
e.printStackTrace();
System.exit( 1 );
}
for ( Enumeration e = m.keys(); e.hasMoreElements(); )
{
String key = (String) e.nextElement();
String[] values = m.getMultiple( key );
System.out.println( key + " =");
for ( int i=0; i<values.length; i++ )
{
System.out.println ( values );
}
} // end for
} // end if DEBUG
} // end main
} // end MultiProperties
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,780
Messages
2,569,611
Members
45,281
Latest member
Pedroaciny

Latest Threads

Top