Click here to Skip to main content
15,900,378 members
Articles / Programming Languages / Java / Java SE

An Enhanced INI File Class for Java

Rate me:
Please Sign up or sign in to vote.
3.35/5 (22 votes)
21 Feb 2017CPOL3 min read 209.6K   3.6K   36   50
An INI file manipulation class with support for environment variables

Introduction

This article is about a Java class to read and write Windows INI files. One good feature of this class is its support for environment variables, i.e., this class allows us to include a reference to an environment variable while defining values in INI files. The following code snippet depicts this feature:

;Sample INI file
[Database]
UserId = DBuser
Sid = DBSid
DBHost = 145.101.56.32
DBPort = 1521
DBLib = %ORACLE_HOME%\lib\classes12.jar

When the DBLib variable is read, the class will try to retrieve the value for the %ORACLE_HOME% variable. Let's say on your system %ORACLE_HOME% points to the C:\Oracle directory, then DBLib will get expanded to C:\Oracle\lib\classes12.jar.

Using the Code

The following code snippet shows the usage of this class. The same code can also be found in the main method of the INIFile class.

Java
public static void main(String[] pstrArgs)
{
    INIFile objINI = null;
    String  strFile = null;

    if (pstrArgs.length == 0) return;

    strFile = pstrArgs[0];
    /* Following call loads the strFile if it exists. */
    objINI = new INIFile(strFile);

    objINI.setStringProperty("Database", "SID", "ORCL", "Database SID");
    objINI.setStringProperty("Database", "UserId", "System", "User Id");
    objINI.setStringProperty("Database", "Password", "Manager", "Password");
    objINI.setStringProperty("Database", "HostName", "DBServer", "Server Host");
    objINI.setIntegerProperty("Database", "Port", 1521, "Server Port");
    /* Save changes back to strFile */
    objINI.save();
    objINI = null;
}

Date and Timestamps Usage

Since all data in INI files is stored as strings, the class provides the following methods to interpret the date and timestamp values in the correct manner.

  • setDateFormat - This method allows to set the date format to be used while converting date strings to date data type and vice versa.
  • setTimeFormat - This method allows to set the timestamp format to be used while converting timestamp strings to timestamp data type and vice versa.

For the supported date time formats, please refer to java.text.SimpleDateFormat.

Methods Summary

The class exposes the following public methods to access INI property values. All these methods require that the INI section name and property name be passed as input parameters.

  • getBooleanProperty - Returns boolean value
  • getStringProperty - Returns string value
  • getIntegerProperty - Returns int value
  • getLongProperty - Returns long value
  • getDoubleProperty - Returns double value
  • getDateProperty - Returns java.util.Date value
  • getTimestampProperty - Returns java.sql.Timestamp value

Additionally, the class also provides the following additional methods to retrieve the names of all the sections present in an INI file, names of all the properties present under a particular section, remove the property, remove a section, and save changes back to the disk. The load method is automatically called from the constructor.

  • getAllSectionNames - Returns a string array of section names
  • getAllPropertyNames - Returns a string array of section names. This method requires a section name as an input parameter.
  • removeProperty - Removes specified property from the specified section
  • removeSection - Removes the specified section
  • save - Persist changes back to INI file

I hope this will be useful to anyone who needs this functionality in Java.

Known Limitations

The class does not support property values which span across multiple lines. Duplicate properties in a section are not supported. The last property value will overwrite previous value.

Points of Interest

JDK 1.3 and 1.4 do not have built-in classes and/or methods to access environment variable values. Thanks to Mr. Réal Gagnon for generously making the code available to read environment variables.

History

  • 01/07/2004 - Initial release
  • 07/07/2004 - Added support for environment variables and data type specific getters and setters
  • 08/07/2004 - Code correction, mostly typing mistakes and a condition in the getStringProperty method
  • 26/08/2004 - Lots of useful modifications:
    • Added support for section and property level comments
    • Introduced a separate class to represent a property
    • Added method to create a named section
    • Changed HashMap to LinkedHashMap, Sections/Properties now retain their order
    • Some of the method implementations/signatures are changed
  • 15/06/2015 - Now Requires JDK 6 & above
    • Section & Property names are now case-insensitive
    • Duplicate property names are handlled correctly, the last value overwrites the previous
    • Environment variables are now fetched using System.getEnv()
  • 23/06/2015 - Fixed an issue in comments not getting saved properly
  • 21/02/2017 - Fixed an issue and new capability.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Freelancer
India India
I am a software professional with over 20 years of commercial business applications design and development experience.

My programming experience includes Java, Spring, .NET, Classic VB & ASP, Scripting, Power Builder, PHP, Magic & far far ago FoxPro, C, Assembly and COBOL.

From last 11 years I am mostly working with Java Technology. I am currently available to take up new assignments.

Comments and Discussions

 
Generalanother implementation Pin
Anonymous17-Mar-05 20:26
Anonymous17-Mar-05 20:26 
GeneralSeemingly duplicate ini entries Pin
cgoudie3-Jan-05 5:05
cgoudie3-Jan-05 5:05 
GeneralRe: Seemingly duplicate ini entries Pin
klizza25-Jan-05 13:14
klizza25-Jan-05 13:14 
Generalnew bug Pin
David Sourek27-Dec-04 11:14
David Sourek27-Dec-04 11:14 
GeneralRe: new bug Pin
lzq1233353-Jun-09 23:23
lzq1233353-Jun-09 23:23 
GeneralHandling of \n \r and \t Pin
Jens Scheidtmann27-Aug-04 4:56
Jens Scheidtmann27-Aug-04 4:56 
GeneralRe: Handling of \n \r and \t Pin
Prasad Khandekar27-Aug-04 5:37
professionalPrasad Khandekar27-Aug-04 5:37 
GeneralBug fixes and additional improvements Pin
Member 40961913-Aug-04 2:23
Member 40961913-Aug-04 2:23 
Yep, as mentioned the other thread, changing from "=" to " = " is not optimal.

A better modification could be to change the toString method in the INISection. However, an even better way would be to trim away spaces when reading so that "KEY = 12" and "KEY=12" would return the same property value for the key "KEY". That way we are less sensitive to errors in the INI file.

So, the final reading loop in loadFile() method would be:

while (objBRdr.ready())
{
   iPos = -1;
   strLine  = null;
   strLine = objBRdr.readLine().trim();
                        
   if (strLine.startsWith("[") && strLine.endsWith("]"))
   {
      // Section start reached, create new section
      strSection = strLine.substring(1, strLine.length() - 1);
      objSec = new INISection(strSection); 
      this.mhmapSections.put(strSection, objSec);
   }
   else if ((iPos = strLine.indexOf("=")) > 0 && objSec != null 
               && (!strLine.startsWith(";"))) // Skip comments
   {
      // read the key value pair "KEY = 789"
      // objSec should be set here, but if it isn't (because of
      // wrongly formatted INI-file, i.e. no section start before property),
      // then ignore the orphaned property
      if (objSec != null )
      // Trim away any space characters around keys and values
         objSec.setProperty(trim(strLine.substring(0, iPos)), 
                             trim(strLine.substring(iPos + 1)));
    }
}

Two notes:
1. I use my own trim(...) method (available later on, but hold on!) since the trim() method in the String class will also trim away control characters.
2. I add a check for comments. Sorry if this puts a constraint on the key, which I find reasonable (i.e. key cannot start with a ';')

Now there is a problem: the reader uses trim() when reading the lines and we trim away spaces from the value, so how do we make sure that we can have string properties with spaces at the beginning and/or the end of the line? A quick answer is to optionally enclose string properties in braces or any other non-space/control character. Quotes would not be a good idea as they may be used frequently. This puts a constraint on the format however: string properties that include enclosing braces must be enclosed within another set of braces. Another constraint I also find reasonable, though. Thus, the line
<br />
KEY={{ your name }}<br />

will map the string '{ your name }' to the key KEY, while
<br />
KEY={ your name }<br />

will map the string ' your name '. I think this is a reasonable constraint. Now note that
<br />
KEY="Enter a value"<br />

will map the string '"Enter a value"', including the enclosing quotes!


If we add the check for braces in the trim() method, we get a bonus: even keys may include initial and terminal spaces. A stupid bonus maybe, but maybe someone finds it useful? So, the trim() method becomes:

/**
 * Trim away any initial/terminal spaces and any enclosing braces
 * @param str The string to be trimmed
 * @return The trimmed string
 */
private String trim(String str)
{
    Pattern pattern = Pattern.compile("(?s)( *)(.*)( *)");
    Matcher matcher = pattern.matcher(str);
    if (matcher.matches()) {
        // Return second group in match
        String strRet = matcher.group(2);
        if (strRet.startsWith("{") && strRet.endsWith("}"))
            return strRet.substring(1, strRet.length()-1);
        else
            return strRet;
    } else {
        // Something went wrong. Return same string
        return str;
    }
}

Then we add an if clause to the while loop in the toString method, which checks for spaces or braces at the beginning or/and the end and in that case adds a set of enclosing braces:

while (iter.hasNext())
{
   strProp = (String) iter.next();
   strVal = (String) this.mhmapProps.get(strProp);
   if ( strVal.startsWith(" ") | strVal.endsWith(" ") | 
       (strVal.startsWith("{") && strVal.endsWith("}")) )
      strVal = "{" + strVal + "}";
   objBuf.append(strProp + "=" + strVal + "\r\n");
   strProp = null;
   strVal = null;
}

I have a working source file with the following complete list of changes:
- Added support for comments
- Added support for initial/terminal spaces in keys & values
- Changed from HashMap to LinkedHashMap to preserve section and key order
- Added support for default values
- Removed main() method.
- Code readability: Changed name of checkFile to fileExists
- Fixed bugs: iter.hasNext() at the wrong place in for clause
- Fixed bug: negation in Constructor for checkFile()

It may be downloaded from the ReqSimile SourceForge project at
http://cvs.sourceforge.net/viewcvs.py/reqsimile/ReqSimile/source/reqsimile/
Revision 1.1 is the original. Revision 1.2 incorporates the above changes.

I hope I didn't miss anything or introduced additional bugs. Smile | :) It has past my non-thorough test. Please post any omissions or errors to this thread.
GeneralRe: Bug fixes and additional improvements Pin
Prasad Khandekar20-Aug-04 2:10
professionalPrasad Khandekar20-Aug-04 2:10 
GeneralRe: Bug fixes and additional improvements Pin
Member 40961920-Aug-04 3:50
Member 40961920-Aug-04 3:50 
GeneralRe: Bug fixes and additional improvements Pin
Prasad Khandekar27-Aug-04 5:33
professionalPrasad Khandekar27-Aug-04 5:33 
GeneralRe: Bug fixes and additional improvements Pin
klizza27-Aug-04 10:44
klizza27-Aug-04 10:44 
GeneralAnother Bug: NPE Pin
klizza23-Aug-04 8:52
klizza23-Aug-04 8:52 
GeneralRe: Bug fixes and additional improvements Pin
HLO8-Jun-05 6:04
HLO8-Jun-05 6:04 
GeneralRe: Bug fixes and additional improvements Pin
Anonymous8-Jun-05 11:11
Anonymous8-Jun-05 11:11 
Generalfew bugs in this class Pin
chcoust11-Jul-04 23:19
susschcoust11-Jul-04 23:19 
GeneralRe: few bugs in this class Pin
FayeStar24-Jul-04 1:11
FayeStar24-Jul-04 1:11 
GeneralRe: few bugs in this class Pin
Mappo3-Aug-04 4:00
Mappo3-Aug-04 4:00 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.