001/*------------------------------------------------------------------------------
002 * PACKAGE: com.freeware.IniFiles
003 * FILE   : IniFile.java
004 * CREATED: Jun 30, 2004
005 * AUTHOR : Prasad P. Khandekar
006 *------------------------------------------------------------------------------
007 * Change Log:
008 * 05/07/2004    - Added support for date time formats.
009 *                 Added support for environment variables.
010 * 07/07/2004    - Added support for data type specific getters and setters.
011 *                 Updated main method to reflect above changes.
012 * 26/08/2004    - Added support for section level and property level comments.
013 *                 Introduction of seperate class for property values.
014 *                 Added addSection method.
015 *                 Sections and properties now retail their order (LinkedHashMap)
016 *                 Method implementation changes.
017 *-----------------------------------------------------------------------------*/
018package org.hl7.fhir.utilities;
019
020import java.io.BufferedReader;
021import java.io.File;
022import java.io.FileNotFoundException;
023import java.io.FileReader;
024import java.io.FileWriter;
025import java.io.IOException;
026import java.io.InputStream;
027import java.io.InputStreamReader;
028import java.io.OutputStream;
029import java.io.OutputStreamWriter;
030import java.io.Reader;
031import java.io.Writer;
032import java.sql.Timestamp;
033import java.text.DateFormat;
034import java.text.ParseException;
035import java.text.SimpleDateFormat;
036import java.util.Collections;
037import java.util.Date;
038import java.util.Iterator;
039import java.util.LinkedHashMap;
040import java.util.Map;
041import java.util.NoSuchElementException;
042import java.util.Properties;
043import java.util.Set;
044
045
046/**
047 * IniFile class provides methods for manipulating (Read/Write) windows ini files.
048 * 
049 * @author Prasad P. Khandekar
050 * @version 1.0
051 * @since 1.0
052 */
053public final class IniFile
054{
055    /** Variable to represent the date format */
056    private String mstrDateFmt = "yyyy-MM-dd";
057
058    /** Variable to represent the timestamp format */
059    private String mstrTimeStampFmt = "yyyy-MM-dd HH:mm:ss";
060
061    /** Variable to denote the successful load operation. */
062    @SuppressWarnings("unused")
063    private boolean mblnLoaded = false;
064
065    /** Variable to hold the ini file name and full path */
066    private String mstrFile;
067
068    /** Variable to hold the sections in an ini file. */
069    private LinkedHashMap<String, INISection> mhmapSections;
070
071    /** Variable to hold environment variables **/
072    private Properties mpropEnv;
073
074    /**
075     * Create a IniFile object from the file named in the parameter.
076     * @param pstrPathAndName The full path and name of the ini file to be used.
077     */
078    public IniFile(String pstrPathAndName)
079    {
080        this.mpropEnv = getEnvVars();
081        this.mhmapSections = new LinkedHashMap<String, INISection>();
082        this.mstrFile = pstrPathAndName;
083        // Load the specified INI file.
084        if (checkFile(pstrPathAndName)) loadFile();
085    }
086
087    public IniFile(InputStream stream) {
088      this.mpropEnv = getEnvVars();
089      this.mhmapSections = new LinkedHashMap<String, INISection>();
090      this.mstrFile = null;
091      // Load the specified INI file.
092      loadStream(stream);
093    }
094
095    /*------------------------------------------------------------------------------
096 * Getters
097------------------------------------------------------------------------------*/
098    /**
099     * Returns the ini file name being used.
100     * @return the INI file name.
101     */
102    public String getFileName()
103    {
104        return this.mstrFile;
105    }
106
107    /**
108     * Returns the specified string property from the specified section.
109     * @param pstrSection the INI section name.
110     * @param pstrProp the property to be retrieved.
111     * @return the string property value.
112     */
113    public String getStringProperty(String pstrSection, String pstrProp)
114    {
115        String      strRet   = null;
116        INIProperty objProp  = null;
117        INISection  objSec   = null;
118
119        objSec = (INISection) this.mhmapSections.get(pstrSection);
120        if (objSec != null)
121        {
122            objProp = objSec.getProperty(pstrProp);
123            if (objProp != null)
124            {
125                strRet = objProp.getPropValue();
126                objProp = null;
127            }
128            objSec = null;
129        }
130        return strRet;
131    }
132
133    /**
134     * Returns the specified boolean property from the specified section.
135     * This method considers the following values as boolean values.
136     * <ol>
137     *      <li>YES/yes/Yes - boolean true</li>
138     *      <li>NO/no/No  - boolean false</li>
139     *      <li>1 - boolean true</li>
140     *      <li>0 - boolean false</li>
141     *      <li>TRUE/True/true - boolean true</li>
142     *      <li>FALSE/False/false - boolean false</li>
143     * </ol>
144     * @param pstrSection the INI section name.
145     * @param pstrProp the property to be retrieved.
146     * @return the boolean value
147     */
148    public Boolean getBooleanProperty(String pstrSection, String pstrProp)
149    {
150        boolean     blnRet  = false;
151        String      strVal  = null;
152        INIProperty objProp = null;
153        INISection  objSec  = null;
154
155        objSec = (INISection) this.mhmapSections.get(pstrSection);
156        if (objSec != null)
157        {
158            objProp = objSec.getProperty(pstrProp);
159            if (objProp != null)
160            {
161                strVal = objProp.getPropValue().toUpperCase();
162                if (strVal.equals("YES") || strVal.equals("TRUE") ||
163                    strVal.equals("1"))
164                {
165                    blnRet = true;
166                }
167                objProp = null;
168            }
169            objSec = null;
170        }
171        return new Boolean(blnRet);
172    }
173
174    /**
175     * Returns the specified integer property from the specified section.
176     * @param pstrSection the INI section name.
177     * @param pstrProp the property to be retrieved.
178     * @return the integer property value.
179     */
180    public Integer getIntegerProperty(String pstrSection, String pstrProp)
181    {
182        Integer     intRet  = null;
183        String      strVal  = null;
184        INIProperty objProp = null;
185        INISection  objSec  = null;
186
187        objSec = (INISection) this.mhmapSections.get(pstrSection);
188        if (objSec != null)
189        {
190            objProp = objSec.getProperty(pstrProp);
191            try
192            {
193                if (objProp != null)
194                {
195                    strVal = objProp.getPropValue();
196                    if (strVal != null) intRet = new Integer(strVal);
197                }
198            }
199            catch (NumberFormatException NFExIgnore)
200            {
201            }
202            finally
203            {
204                if (objProp != null) objProp = null;
205            }
206            objSec = null;
207        }
208        return intRet;
209    }
210
211    /**
212     * Returns the specified long property from the specified section.
213     * @param pstrSection the INI section name.
214     * @param pstrProp the property to be retrieved.
215     * @return the long property value.
216     */
217    public Long getLongProperty(String pstrSection, String pstrProp)
218    {
219        Long        lngRet  = null;
220        String      strVal  = null;
221        INIProperty objProp = null;
222        INISection  objSec  = null;
223
224        objSec = (INISection) this.mhmapSections.get(pstrSection);
225        if (objSec != null)
226        {
227            objProp = objSec.getProperty(pstrProp);
228            try
229            {
230                if (objProp != null)
231                {
232                    strVal = objProp.getPropValue();
233                    if (strVal != null) lngRet = new Long(strVal);
234                }
235            }
236            catch (NumberFormatException NFExIgnore)
237            {
238            }
239            finally
240            {
241                if (objProp != null) objProp = null;
242            }
243            objSec = null;
244        }
245        return lngRet;
246    }
247
248    /**
249     * Returns the specified double property from the specified section.
250     * @param pstrSection the INI section name.
251     * @param pstrProp the property to be retrieved.
252     * @return the double property value.
253     */
254    public Double getDoubleProperty(String pstrSection, String pstrProp)
255    {
256        Double      dblRet  = null;
257        String      strVal  = null;
258        INIProperty objProp = null;
259        INISection  objSec  = null;
260
261        objSec = (INISection) this.mhmapSections.get(pstrSection);
262        if (objSec != null)
263        {
264            objProp = objSec.getProperty(pstrProp);
265            try
266            {
267                if (objProp != null)
268                {
269                    strVal = objProp.getPropValue();
270                    if (strVal != null) dblRet = new Double(strVal);
271                }
272            }
273            catch (NumberFormatException NFExIgnore)
274            {
275            }
276            finally
277            {
278                if (objProp != null) objProp = null;
279            }
280            objSec = null;
281        }
282        return dblRet;
283    }
284
285    /**
286     * Returns the specified date property from the specified section.
287     * @param pstrSection the INI section name.
288     * @param pstrProp the property to be retrieved.
289     * @return the date property value.
290     */
291    public Date getDateProperty(String pstrSection, String pstrProp)
292    {
293        Date        dtRet   = null;
294        String      strVal  = null;
295        DateFormat  dtFmt   = null;
296        INIProperty objProp = null;
297        INISection  objSec  = null;
298
299        objSec = (INISection) this.mhmapSections.get(pstrSection);
300        if (objSec != null)
301        {
302            objProp = objSec.getProperty(pstrProp);
303            try
304            {
305                if (objProp != null) strVal = objProp.getPropValue();
306                if (strVal != null)
307                {
308                    dtFmt = new SimpleDateFormat(this.mstrDateFmt);
309                    dtRet = dtFmt.parse(strVal);
310                }
311            }
312            catch (ParseException PExIgnore)
313            {
314            }
315            catch (IllegalArgumentException IAEx)
316            {
317            }
318            finally
319            {
320                if (objProp != null) objProp = null;
321            }
322            objSec = null;
323        }
324        return dtRet;
325    }
326
327    /**
328     * Returns the specified date property from the specified section.
329     * @param pstrSection the INI section name.
330     * @param pstrProp the property to be retrieved.
331     * @return the date property value.
332     */
333    public Date getTimestampProperty(String pstrSection, String pstrProp)
334    {
335        Timestamp   tsRet   = null;
336        Date        dtTmp   = null;
337        String      strVal  = null;
338        DateFormat  dtFmt   = null;
339        INIProperty objProp = null;
340        INISection  objSec  = null;
341
342        objSec = (INISection) this.mhmapSections.get(pstrSection);
343        if (objSec != null)
344        {
345            objProp = objSec.getProperty(pstrProp);
346            try
347            {
348                if (objProp != null) strVal = objProp.getPropValue();
349                if (strVal != null)
350                {
351                    dtFmt = new SimpleDateFormat(this.mstrDateFmt);
352                    dtTmp = dtFmt.parse(strVal);
353                    tsRet = new Timestamp(dtTmp.getTime());
354                }
355            }
356            catch (ParseException PExIgnore)
357            {
358            }
359            catch (IllegalArgumentException IAEx)
360            {
361            }
362            finally
363            {
364                if (objProp != null) objProp = null;
365            }
366            objSec = null;
367        }
368        return tsRet;
369    }
370
371/*------------------------------------------------------------------------------
372 * Setters
373------------------------------------------------------------------------------*/
374    /**
375     * Sets the comments associated with a section.
376     * @param pstrSection the section name
377     * @param pstrComments the comments.
378     */
379    public void addSection(String pstrSection, String pstrComments)
380    {
381        INISection objSec   = null;
382
383        objSec = (INISection) this.mhmapSections.get(pstrSection);
384        if (objSec == null)
385        {
386            objSec = new INISection(pstrSection);
387            this.mhmapSections.put(pstrSection, objSec);
388        }
389        objSec.setSecComments(delRemChars(pstrComments));
390        objSec = null;
391    }
392
393    /**
394     * Sets the specified string property.
395     * @param pstrSection the INI section name.
396     * @param pstrProp the property to be set.
397     * @pstrVal the string value to be persisted
398     */
399    public void setStringProperty(String pstrSection, String pstrProp, 
400                                                String pstrVal, String pstrComments)
401    {
402        INISection objSec   = null;
403
404        objSec = (INISection) this.mhmapSections.get(pstrSection);
405        if (objSec == null)
406        {
407            objSec = new INISection(pstrSection);
408            this.mhmapSections.put(pstrSection, objSec);
409        }
410        objSec.setProperty(pstrProp, pstrVal, pstrComments);
411    }
412
413    /**
414     * Sets the specified boolean property.
415     * @param pstrSection the INI section name.
416     * @param pstrProp the property to be set.
417     * @param pblnVal the boolean value to be persisted
418     */
419    public void setBooleanProperty(String pstrSection, String pstrProp, 
420                                                boolean pblnVal, String pstrComments)
421    {
422        INISection objSec   = null;
423
424        objSec = (INISection) this.mhmapSections.get(pstrSection);
425        if (objSec == null)
426        {
427            objSec = new INISection(pstrSection);
428            this.mhmapSections.put(pstrSection, objSec);
429        }
430        if (pblnVal)
431            objSec.setProperty(pstrProp, "TRUE", pstrComments);
432        else
433            objSec.setProperty(pstrProp, "FALSE", pstrComments);
434    }
435
436    /**
437     * Sets the specified integer property.
438     * @param pstrSection the INI section name.
439     * @param pstrProp the property to be set.
440     * @param pintVal the int property to be persisted.
441     */
442    public void setIntegerProperty(String pstrSection, String pstrProp, 
443                                                int pintVal, String pstrComments)
444    {
445        INISection objSec   = null;
446
447        objSec = (INISection) this.mhmapSections.get(pstrSection);
448        if (objSec == null)
449        {
450            objSec = new INISection(pstrSection);
451            this.mhmapSections.put(pstrSection, objSec);
452        }
453        objSec.setProperty(pstrProp, Integer.toString(pintVal), pstrComments);
454    }
455
456    /**
457     * Sets the specified long property.
458     * @param pstrSection the INI section name.
459     * @param pstrProp the property to be set.
460     * @param plngVal the long value to be persisted.
461     */
462    public void setLongProperty(String pstrSection, String pstrProp, 
463                                        long plngVal, String pstrComments)
464    {
465        INISection objSec   = null;
466
467        objSec = (INISection) this.mhmapSections.get(pstrSection);
468        if (objSec == null)
469        {
470            objSec = new INISection(pstrSection);
471            this.mhmapSections.put(pstrSection, objSec);
472        }
473        objSec.setProperty(pstrProp, Long.toString(plngVal), pstrComments);
474    }
475
476    /**
477     * Sets the specified double property.
478     * @param pstrSection the INI section name.
479     * @param pstrProp the property to be set.
480     * @param pdblVal the double value to be persisted.
481     */
482    public void setDoubleProperty(String pstrSection, String pstrProp, 
483                                                double pdblVal, String pstrComments)
484    {
485        INISection objSec   = null;
486
487        objSec = (INISection) this.mhmapSections.get(pstrSection);
488        if (objSec == null)
489        {
490            objSec = new INISection(pstrSection);
491            this.mhmapSections.put(pstrSection, objSec);
492        }
493        objSec.setProperty(pstrProp, Double.toString(pdblVal), pstrComments);
494    }
495
496    /**
497     * Sets the specified java.util.Date property.
498     * @param pstrSection the INI section name.
499     * @param pstrProp the property to be set.
500     * @param pdtVal the date value to be persisted.
501     */
502    public void setDateProperty(String pstrSection, String pstrProp, 
503                                        Date pdtVal, String pstrComments)
504    {
505        INISection objSec   = null;
506
507        objSec = (INISection) this.mhmapSections.get(pstrSection);
508        if (objSec == null)
509        {
510            objSec = new INISection(pstrSection);
511            this.mhmapSections.put(pstrSection, objSec);
512        }
513        objSec.setProperty(pstrProp, utilDateToStr(pdtVal, this.mstrDateFmt), 
514                                pstrComments);
515    }
516
517    /**
518     * Sets the specified java.sql.Timestamp property.
519     * @param pstrSection the INI section name.
520     * @param pstrProp the property to be set.
521     * @param ptsVal the timestamp value to be persisted.
522     */
523    public void setTimestampProperty(String pstrSection, String pstrProp, 
524                                                        Timestamp ptsVal, String pstrComments)
525    {
526        INISection objSec   = null;
527
528        objSec = (INISection) this.mhmapSections.get(pstrSection);
529        if (objSec == null)
530        {
531            objSec = new INISection(pstrSection);
532            this.mhmapSections.put(pstrSection, objSec);
533        }
534        objSec.setProperty(pstrProp, timeToStr(ptsVal, this.mstrTimeStampFmt), 
535                                pstrComments);
536    }
537
538    /**
539     * Sets the format to be used to interpreat date values.
540     * @param pstrDtFmt the format string
541     * @throws IllegalArgumentException if the if the given pattern is invalid
542     */
543    public void setDateFormat(String pstrDtFmt) throws IllegalArgumentException
544    {
545        if (!checkDateTimeFormat(pstrDtFmt))
546            throw new IllegalArgumentException("The specified date pattern is invalid!");
547        this.mstrDateFmt = pstrDtFmt;
548    }
549
550    /**
551     * Sets the format to be used to interpreat timestamp values.
552     * @param pstrTSFmt the format string
553     * @throws IllegalArgumentException if the if the given pattern is invalid
554     */
555    public void setTimeStampFormat(String pstrTSFmt)
556    {
557        if (!checkDateTimeFormat(pstrTSFmt))
558            throw new IllegalArgumentException("The specified timestamp pattern is invalid!");
559        this.mstrTimeStampFmt = pstrTSFmt;
560    }
561
562/*------------------------------------------------------------------------------
563 * Public methods
564------------------------------------------------------------------------------*/
565    public int getTotalSections()
566    {
567        return this.mhmapSections.size();
568    }
569
570    /**
571     * Returns a string array containing names of all sections in INI file.
572     * @return the string array of section names
573     */
574    public String[] getAllSectionNames()
575    {
576        int        iCntr  = 0;
577        Iterator<String>   iter   = null;
578        String[]   arrRet = null;
579
580        try
581        {
582            if (this.mhmapSections.size() > 0)
583            {
584                arrRet = new String[this.mhmapSections.size()];
585                for (iter = this.mhmapSections.keySet().iterator();;iter.hasNext())
586                {
587                    arrRet[iCntr] = (String) iter.next();
588                    iCntr++;
589                }
590            }
591        }
592        catch (NoSuchElementException NSEExIgnore)
593        {
594        }
595        finally
596        {
597            if (iter != null) iter = null;
598        }
599        return arrRet;
600    }
601
602    /**
603     * Returns a string array containing names of all the properties under specified section.
604     * @param pstrSection the name of the section for which names of properties is to be retrieved.
605     * @return the string array of property names.
606     */
607    public String[] getPropertyNames(String pstrSection)
608    {
609        String[]   arrRet = null;
610        INISection objSec = null;
611
612        objSec = (INISection) this.mhmapSections.get(pstrSection);
613        if (objSec != null)
614        {
615            arrRet = objSec.getPropNames();
616            objSec = null;
617        }
618        return arrRet;
619    }
620
621    /**
622     * Returns a map containing all the properties under specified section.
623     * @param pstrSection the name of the section for which properties are to be retrieved.
624     * @return the map of properties.
625     */
626    public Map<String, INIProperty> getProperties(String pstrSection)
627    {
628        Map<String, INIProperty>        hmRet = null;
629        INISection objSec = null;
630
631        objSec = (INISection) this.mhmapSections.get(pstrSection);
632        if (objSec != null)
633        {
634            hmRet = objSec.getProperties();
635            objSec = null;
636        }
637        return hmRet;
638    }
639
640    /**
641     * Removed specified property from the specified section. If the specified
642     * section or the property does not exist, does nothing.
643     * @param pstrSection the section name.
644     * @param pstrProp the name of the property to be removed.
645     */
646    public void removeProperty(String pstrSection, String pstrProp)
647    {
648        INISection objSec = null;
649
650        objSec = (INISection) this.mhmapSections.get(pstrSection);
651        if (objSec != null)
652        {
653            objSec.removeProperty(pstrProp);
654                objSec = null;
655        }
656    }
657
658    /**
659     * Removes the specified section if one exists, otherwise does nothing.
660     * @param pstrSection the name of the section to be removed.
661     */
662    public void removeSection(String pstrSection)
663    {
664        if (this.mhmapSections.containsKey(pstrSection))
665            this.mhmapSections.remove(pstrSection);
666    }
667
668    /**
669     * Flush changes back to the disk file. If the disk file does not exists then
670     * creates the new one. 
671     * @ 
672     */
673    public boolean save() 
674    {
675        boolean    blnRet    = false;
676        File       objFile   = null;
677        String     strName   = null;
678        String     strTemp   = null;
679        Iterator<String>   itrSec    = null;
680        INISection objSec    = null;
681        FileWriter objWriter = null;
682
683        try
684        {
685            if (this.mhmapSections.size() == 0) return false;
686            objFile = new CSFile(this.mstrFile);
687            if (objFile.exists()) objFile.delete();
688            objWriter = new FileWriter(objFile);
689            itrSec = this.mhmapSections.keySet().iterator();
690            while (itrSec.hasNext())
691            {
692                strName = (String) itrSec.next();
693                objSec = (INISection) this.mhmapSections.get(strName);
694                strTemp = objSec.toString();
695                objWriter.write(strTemp);
696                objWriter.write("\r\n");
697                objSec = null;
698            }
699            blnRet = true;
700        }
701        catch (IOException IOExIgnore)
702        {
703        }
704        finally
705        {
706            if (objWriter != null)
707            {
708                closeWriter(objWriter);
709                objWriter = null;
710            }
711            if (objFile != null) objFile = null;
712            if (itrSec != null) itrSec = null;
713        }
714        return blnRet;
715    }
716
717    public boolean save(OutputStream stream) 
718    {
719        boolean    blnRet    = false;
720        String     strName   = null;
721        String     strTemp   = null;
722        Iterator<String>   itrSec    = null;
723        INISection objSec    = null;
724        OutputStreamWriter objWriter = null;
725
726        try
727        {
728            if (this.mhmapSections.size() == 0) return false;
729            objWriter = new OutputStreamWriter(stream, "UTF-8");
730            itrSec = this.mhmapSections.keySet().iterator();
731            while (itrSec.hasNext())
732            {
733                strName = (String) itrSec.next();
734                objSec = (INISection) this.mhmapSections.get(strName);
735                strTemp = objSec.toString();
736                objWriter.write(strTemp);
737                objWriter.write("\r\n");
738                objSec = null;
739            }
740            blnRet = true;
741        }
742        catch (IOException IOExIgnore)
743        {
744        }
745        finally
746        {
747            if (objWriter != null)
748            {
749                closeWriter(objWriter);
750                objWriter = null;
751            }
752            if (itrSec != null) itrSec = null;
753        }
754        return blnRet;
755    }
756
757    
758/*------------------------------------------------------------------------------
759 * Helper functions
760 *----------------------------------------------------------------------------*/
761    /**
762     * Procedure to read environment variables.
763     * Thanx to http://www.rgagnon.com/howto.html for this implementation.
764     */
765    private Properties getEnvVars()
766    {
767        Process p = null;
768        Properties envVars = new Properties();
769
770        try
771        {
772            Runtime r = Runtime.getRuntime();
773            String OS = System.getProperty("os.name").toLowerCase();
774
775            if (OS.indexOf("windows 9") > -1)
776            {
777                p = r.exec("command.com /c set");
778            }
779            else if ((OS.indexOf("nt") > -1) ||
780                     (OS.indexOf("windows 2000") > -1) ||
781                     (OS.indexOf("windows xp") > -1))
782            {
783                p = r.exec("cmd.exe /c set");
784            }
785            else
786            {
787                // our last hope, we assume Unix (thanks to H. Ware for the fix)
788                p = r.exec("env");
789            }
790            BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
791            String line;
792            while((line = br.readLine()) != null)
793            {
794                int idx = line.indexOf('=');
795                String key = line.substring(0, idx);
796                String value = line.substring(idx + 1);
797                envVars.setProperty(key, value);
798            }
799        }
800        catch (Exception ExIgnore)
801        {
802        }
803        return envVars;
804    }
805
806    /**
807     * Helper function to check the date time formats.
808     * @param pstrDtFmt the date time format string to be checked.
809     * @return true for valid date/time format, false otherwise.
810     */
811    private boolean checkDateTimeFormat(String pstrDtFmt)
812    {
813        boolean    blnRet = false;
814        DateFormat objFmt = null;
815
816        try
817        {
818            objFmt = new SimpleDateFormat(pstrDtFmt);
819            blnRet = true;
820        }
821        catch (NullPointerException NPExIgnore)
822        {
823        }
824        catch (IllegalArgumentException IAExIgnore)
825        {
826        }
827        finally
828        {
829            if (objFmt != null) objFmt = null;
830        }
831        return blnRet;
832    }
833
834    /**
835     * Reads the INI file and load its contentens into a section collection after 
836     * parsing the file line by line. 
837     */
838    private void loadStream(InputStream stream)
839    {
840        int            iPos       = -1;
841        String         strLine    = null;
842        String         strSection = null;
843        String         strRemarks = null;
844        BufferedReader objBRdr    = null;
845        InputStreamReader     objFRdr    = null;
846        INISection     objSec     = null;
847
848        try
849        {
850            objFRdr = new InputStreamReader(stream);
851            if (objFRdr != null)
852            {
853                objBRdr = new BufferedReader(objFRdr);
854                if (objBRdr != null)
855                {
856                    while (objBRdr.ready())
857                    {
858                        iPos = -1;
859                        strLine  = null;
860                        strLine = objBRdr.readLine().trim();
861                        if (strLine == null)
862                        {
863                        }
864                        else if (strLine.length() == 0)
865                        {
866                        }
867                        else if (strLine.substring(0, 1).equals(";"))
868                        {
869                            if (strRemarks == null)
870                                strRemarks = strLine.substring(1);
871                            else if (strRemarks.length() == 0)
872                                strRemarks = strLine.substring(1);
873                            else
874                                strRemarks = strRemarks + "\r\n" + strLine.substring(1);
875                        }
876                        else if (strLine.startsWith("[") && strLine.endsWith("]"))
877                        {
878                            // Section start reached create new section
879                            if (objSec != null) 
880                                this.mhmapSections.put(strSection.trim(), objSec);
881                            objSec = null;
882                            strSection = strLine.substring(1, strLine.length() - 1);
883                            objSec = new INISection(strSection.trim(), strRemarks);
884                            strRemarks = null;
885                        }
886                        else if ((iPos = strLine.indexOf("=")) > 0 && objSec != null)
887                        {
888                            // read the key value pair 012345=789
889                            objSec.setProperty(strLine.substring(0, iPos).trim(), 
890                                                strLine.substring(iPos + 1).trim(), 
891                                                strRemarks);
892                            strRemarks = null;
893                        }
894                        else 
895                        {
896                            objSec.setProperty(strLine, "", strRemarks);
897                      
898                        }
899                    }
900                    if (objSec != null)
901                        this.mhmapSections.put(strSection.trim(), objSec);
902                    this.mblnLoaded = true;
903                }
904            }
905        }
906        catch (FileNotFoundException FNFExIgnore)
907        {
908            this.mhmapSections.clear();
909        }
910        catch (IOException IOExIgnore)
911        {
912            this.mhmapSections.clear();
913        }
914        catch (NullPointerException NPExIgnore)
915        {
916            this.mhmapSections.clear();
917        }
918        finally
919        {
920            if (objBRdr != null)
921            {
922                closeReader(objBRdr);
923                objBRdr = null;
924            }
925            if (objFRdr != null)
926            {
927                closeReader(objFRdr);
928                objFRdr = null;
929            }
930            if (objSec != null) objSec = null;
931        }
932    }
933
934    /**
935     * Reads the INI file and load its contentens into a section collection after 
936     * parsing the file line by line. 
937     */
938    private void loadFile()
939    {
940        int            iPos       = -1;
941        String         strLine    = null;
942        String         strSection = null;
943        String         strRemarks = null;
944        BufferedReader objBRdr    = null;
945        FileReader     objFRdr    = null;
946        INISection     objSec     = null;
947
948        try
949        {
950            objFRdr = new FileReader(this.mstrFile);
951            if (objFRdr != null)
952            {
953                objBRdr = new BufferedReader(objFRdr);
954                if (objBRdr != null)
955                {
956                    while (objBRdr.ready())
957                    {
958                        iPos = -1;
959                        strLine  = null;
960                        strLine = objBRdr.readLine().trim();
961                        if (strLine == null)
962                        {
963                        }
964                        else if (strLine.length() == 0)
965                        {
966                        }
967                        else if (strLine.substring(0, 1).equals(";"))
968                        {
969                            if (strRemarks == null)
970                                strRemarks = strLine.substring(1);
971                            else if (strRemarks.length() == 0)
972                                strRemarks = strLine.substring(1);
973                            else
974                                strRemarks = strRemarks + "\r\n" + strLine.substring(1);
975                        }
976                        else if (strLine.startsWith("[") && strLine.endsWith("]"))
977                        {
978                            // Section start reached create new section
979                            if (objSec != null) 
980                                this.mhmapSections.put(strSection.trim(), objSec);
981                            objSec = null;
982                            strSection = strLine.substring(1, strLine.length() - 1);
983                            objSec = new INISection(strSection.trim(), strRemarks);
984                            strRemarks = null;
985                        }
986                        else if ((iPos = strLine.indexOf("=")) > 0 && objSec != null)
987                        {
988                            // read the key value pair 012345=789
989                            objSec.setProperty(strLine.substring(0, iPos).trim(), 
990                                                strLine.substring(iPos + 1).trim(), 
991                                                strRemarks);
992                            strRemarks = null;
993                        }
994                        else 
995                        {
996                            objSec.setProperty(strLine, "", strRemarks);
997                      
998                        }
999                    }
1000                    if (objSec != null)
1001                        this.mhmapSections.put(strSection.trim(), objSec);
1002                    this.mblnLoaded = true;
1003                }
1004            }
1005        }
1006        catch (FileNotFoundException FNFExIgnore)
1007        {
1008            this.mhmapSections.clear();
1009        }
1010        catch (IOException IOExIgnore)
1011        {
1012            this.mhmapSections.clear();
1013        }
1014        catch (NullPointerException NPExIgnore)
1015        {
1016            this.mhmapSections.clear();
1017        }
1018        finally
1019        {
1020            if (objBRdr != null)
1021            {
1022                closeReader(objBRdr);
1023                objBRdr = null;
1024            }
1025            if (objFRdr != null)
1026            {
1027                closeReader(objFRdr);
1028                objFRdr = null;
1029            }
1030            if (objSec != null) objSec = null;
1031        }
1032    }
1033
1034    /**
1035     * Helper function to close a reader object.
1036     * @param pobjRdr the reader to be closed.
1037     */
1038    private void closeReader(Reader pobjRdr)
1039    {
1040        if (pobjRdr == null) return;
1041        try
1042        {
1043            pobjRdr.close();
1044        }
1045        catch (IOException IOExIgnore)
1046        {
1047        }
1048    }
1049
1050    /**
1051     * Helper function to close a writer object.
1052     * @param pobjWriter the writer to be closed.
1053     */
1054    private void closeWriter(Writer pobjWriter)
1055    {
1056        if (pobjWriter == null) return;
1057
1058        try
1059        {
1060            pobjWriter.close();
1061        }
1062        catch (IOException IOExIgnore)
1063        {
1064        }
1065    }
1066    
1067    /**
1068     * Helper method to check the existance of a file.
1069     * @param the full path and name of the file to be checked.
1070     * @return true if file exists, false otherwise.
1071     */
1072    private boolean checkFile(String pstrFile)
1073    {
1074        boolean blnRet  = false;
1075        File    objFile = null;
1076
1077        try
1078        {
1079            objFile = new CSFile(pstrFile);
1080            blnRet = (objFile.exists() && objFile.isFile());
1081        }
1082        catch (Exception e)
1083        {
1084            blnRet = false;
1085        }
1086        finally
1087        {
1088            if (objFile != null) objFile = null;
1089        }
1090        return blnRet;
1091    }
1092
1093    /**
1094     * Converts a java.util.date into String 
1095     * @param pd Date that need to be converted to String 
1096     * @param pstrFmt The date format pattern.
1097     * @return String
1098     */
1099    private String utilDateToStr(Date pdt, String pstrFmt)
1100    {
1101        String strRet = null;
1102        SimpleDateFormat dtFmt = null;
1103
1104        try
1105        {
1106            dtFmt = new SimpleDateFormat(pstrFmt);
1107            strRet = dtFmt.format(pdt);
1108        }
1109        catch (Exception e)
1110        {
1111            strRet = null;
1112        }
1113        finally
1114        {
1115            if (dtFmt != null) dtFmt = null;
1116        }
1117        return strRet;
1118    }
1119
1120    /**
1121     * Converts the given sql timestamp object to a string representation. The format
1122     * to be used is to be obtained from the configuration file.
1123     *  
1124     * @param pobjTS the sql timestamp object to be converted.
1125     * @param pblnGMT If true formats the string using GMT  timezone 
1126     * otherwise using local timezone. 
1127     * @return the formatted string representation of the timestamp.
1128     */
1129    private String timeToStr(Timestamp pobjTS, String pstrFmt)
1130    {
1131        String strRet = null;
1132        SimpleDateFormat dtFmt = null;
1133
1134        try
1135        {
1136            dtFmt = new SimpleDateFormat(pstrFmt);
1137            strRet = dtFmt.format(pobjTS);
1138        }
1139        catch (IllegalArgumentException  iae)
1140        {
1141            strRet = "";
1142        }
1143        catch (NullPointerException npe)
1144        {
1145            strRet = "";
1146        }
1147        finally
1148        {
1149            if (dtFmt != null) dtFmt = null;
1150        }
1151        return strRet;
1152    }
1153
1154    /**
1155     * This function deletes the remark characters ';' from source string
1156     * @param pstrSrc the source  string
1157     * @return the converted string
1158     */
1159    private String delRemChars(String pstrSrc)
1160    {
1161        int    intPos = 0;
1162
1163        if (pstrSrc == null) return null;
1164        while ((intPos = pstrSrc.indexOf(";")) >= 0)
1165        {
1166            if (intPos == 0)
1167                pstrSrc = pstrSrc.substring(intPos + 1);
1168            else if (intPos > 0)
1169                pstrSrc = pstrSrc.substring(0, intPos) + pstrSrc.substring(intPos + 1);
1170        }
1171        return pstrSrc;
1172    }
1173
1174    /**
1175     * This function adds a remark character ';' in source string.
1176     * @param pstrSrc source string
1177     * @return converted string.
1178     */
1179    private String addRemChars(String pstrSrc)
1180    {
1181        int intLen  = 2;
1182        int intPos  = 0;
1183        int intPrev = 0;
1184
1185        String strLeft  = null;
1186        String strRight = null;
1187
1188        if (pstrSrc == null) return null;
1189        while (intPos >= 0)
1190        {
1191            intLen = 2;
1192            intPos = pstrSrc.indexOf("\r\n", intPrev);
1193            if (intPos < 0)
1194            {
1195                intLen = 1;
1196                intPos = pstrSrc.indexOf("\n", intPrev);
1197                if (intPos < 0) intPos = pstrSrc.indexOf("\r", intPrev);
1198            }
1199            if (intPos == 0)
1200            {
1201                pstrSrc = ";\r\n" + pstrSrc.substring(intPos + intLen);
1202                intPrev = intPos + intLen + 1;
1203            }
1204            else if (intPos > 0)
1205            {
1206                strLeft = pstrSrc.substring(0, intPos);
1207                strRight = pstrSrc.substring(intPos + intLen);
1208                if (strRight == null)
1209                    pstrSrc = strLeft;
1210                else if (strRight.length() == 0)
1211                    pstrSrc = strLeft;
1212                else
1213                    pstrSrc = strLeft + "\r\n;" + strRight;
1214                intPrev = intPos + intLen + 1;
1215            }
1216        }
1217        if (!pstrSrc.substring(0, 1).equals(";"))
1218            pstrSrc = ";" + pstrSrc;
1219        pstrSrc = pstrSrc + "\r\n";
1220        return pstrSrc;
1221    }
1222/*------------------------------------------------------------------------------
1223 * Main entry point to test the functionality.
1224 *----------------------------------------------------------------------------*/
1225    /**
1226     * The main entry point for testing.
1227     * @param pstrArgs the command line arguments array if any.
1228     * @ 
1229     */
1230    public static void main(String[] pstrArgs) 
1231    {
1232        IniFile objINI = null;
1233        String  strFile = null;
1234
1235        if (pstrArgs.length == 0) return;
1236
1237        strFile = pstrArgs[0];
1238        // Following call will load the strFile if one exists.
1239        objINI = new IniFile(strFile);
1240
1241//        objINI.addSection("QADatabase", "QA database connection details\nUsed for QA Testing");
1242//        objINI.setStringProperty("QADatabase", "SID", "ORCL", null);
1243//        objINI.setStringProperty("QADatabase", "UserId", "System", null);
1244//        objINI.setStringProperty("QADatabase", "Password", "Manager", null);
1245//        objINI.setStringProperty("QADatabase", "HostName", "DBServer", null);
1246//        objINI.setIntegerProperty("QADatabase", "Port", 1521, null);
1247//        objINI.setStringProperty("QADatabase", "OracleHome", "%ORACLE_HOME%", null);
1248//        
1249        // objINI.setSectionComments("Folders", "Directories where generated files are stored");
1250        objINI.setStringProperty("Folders", "folder1", "G:\\Temp", null);
1251        objINI.setStringProperty("Folders", "folder2", "G:\\Temp\\Backup", null);
1252
1253        // Save changes back to strFile.
1254        objINI.save();
1255        objINI = null;
1256    }
1257
1258/*------------------------------------------------------------------------------
1259 * Private class representing the INI Section.
1260 *----------------------------------------------------------------------------*/
1261    /**
1262     * Class to represent the individual ini file section.
1263     * @author Prasad P. Khandekar
1264     * @version 1.0
1265     * @since 1.0
1266     */
1267    private class INISection
1268    {
1269        /** Variable to hold any comments associated with this section */
1270        private String mstrComment;
1271
1272        /** Variable to hold the section name. */
1273        private String mstrName;
1274        
1275        /** Variable to hold the properties falling under this section. */
1276        private LinkedHashMap<String, INIProperty> mhmapProps;
1277
1278        /**
1279         * Construct a new section object identified by the name specified in 
1280         * parameter.
1281         * @param pstrSection The new sections name.
1282         */
1283        public INISection(String pstrSection)
1284        {
1285            this.mstrName =  pstrSection;
1286            this.mhmapProps = new LinkedHashMap<String, INIProperty>();
1287        }
1288
1289        /**
1290         * Construct a new section object identified by the name specified in 
1291         * parameter and associated comments.
1292         * @param pstrSection The new sections name.
1293         * @param pstrComments the comments associated with this section.
1294         */
1295        public INISection(String pstrSection, String pstrComments)
1296        {
1297            this.mstrName =  pstrSection;
1298            this.mstrComment = delRemChars(pstrComments);
1299            this.mhmapProps = new LinkedHashMap<String, INIProperty>();
1300        }
1301        
1302        /**
1303         * Sets the comments associated with this section.
1304         * @param pstrComments the comments
1305         */
1306        public void setSecComments(String pstrComments)
1307        {
1308            this.mstrComment = delRemChars(pstrComments);
1309        }
1310
1311        /**
1312         * Removes specified property value from this section. 
1313         * @param pstrProp The name of the property to be removed.
1314         */
1315        public void removeProperty(String pstrProp)
1316        {
1317            if (this.mhmapProps.containsKey(pstrProp))
1318                this.mhmapProps.remove(pstrProp);
1319        }
1320
1321        /**
1322         * Creates or modifies the specified property value.
1323         * @param pstrProp The name of the property to be created or modified. 
1324         * @param pstrValue The new value for the property.
1325         * @param pstrComments the associated comments
1326         */
1327        public void setProperty(String pstrProp, String pstrValue, String pstrComments)
1328        {
1329            this.mhmapProps.put(pstrProp, new INIProperty(pstrProp, pstrValue, pstrComments));
1330        }
1331
1332        /**
1333         * Returns a map of all properties.
1334         * @return a map of all properties
1335         */
1336        public Map<String, INIProperty> getProperties()
1337        {
1338            return Collections.unmodifiableMap(this.mhmapProps);
1339        }
1340
1341        /**
1342         * Returns a string array containing names of all the properties under 
1343         * this section. 
1344         * @return the string array of property names.
1345         */
1346        public String[] getPropNames()
1347        {
1348            int      iCntr  = 0;
1349            String[] arrRet = null;
1350            Iterator<String> iter   = null;
1351
1352            try
1353            {
1354                if (this.mhmapProps.size() > 0)
1355                {
1356                    arrRet = new String[this.mhmapProps.size()]; 
1357                    for (iter = this.mhmapProps.keySet().iterator();iter.hasNext();)
1358                    {
1359                        arrRet[iCntr] = (String) iter.next();
1360                        iCntr++;
1361                    }
1362                }
1363            }
1364            catch (NoSuchElementException NSEExIgnore)
1365            {
1366                arrRet = null;
1367            }
1368            return arrRet;
1369        }
1370
1371        /**
1372         * Returns underlying value of the specified property. 
1373         * @param pstrProp the property whose underlying value is to be etrieved.
1374         * @return the property value.
1375         */
1376        public INIProperty getProperty(String pstrProp)
1377        {
1378            INIProperty objRet = null;
1379
1380            if (this.mhmapProps.containsKey(pstrProp))
1381                objRet = (INIProperty) this.mhmapProps.get(pstrProp);
1382            return objRet;
1383        }
1384
1385        /* (non-Javadoc)
1386         * @see java.lang.Object#toString()
1387         */
1388        @Override
1389                public String toString()
1390        {
1391            Set<String>          colKeys = null;
1392            String       strRet  = "";
1393            Iterator<String>     iter    = null;
1394            INIProperty  objProp = null;
1395            StringBuffer objBuf  = new StringBuffer();
1396
1397            if (this.mstrComment != null)
1398                objBuf.append(addRemChars(this.mstrComment));
1399            objBuf.append("[" + this.mstrName + "]\r\n");
1400            colKeys = this.mhmapProps.keySet();
1401            if (colKeys != null)
1402            {
1403                iter = colKeys.iterator();
1404                if (iter != null)
1405                {
1406                    while (iter.hasNext())
1407                    {
1408                        objProp = (INIProperty) this.mhmapProps.get(iter.next());
1409                        objBuf.append(objProp.toString());
1410                        objBuf.append("\r\n");
1411                        objProp = null;
1412                    }
1413                }
1414            }
1415            strRet = objBuf.toString();
1416
1417            objBuf  = null;
1418            iter    = null;
1419            colKeys = null;
1420            return strRet;
1421        }
1422    }
1423
1424/*------------------------------------------------------------------------------
1425 * Private class representing the INI Property.
1426 *----------------------------------------------------------------------------*/
1427    /**
1428     * This class represents a key value pair called property in an INI file. 
1429     * @author Prasad P. Khandekar
1430     * @version 1.0
1431     * @since 1.0
1432     */
1433    private class INIProperty
1434    {
1435        /** Variable to hold name of this property */
1436        private String mstrName;
1437        /** Variable to hold value of this property */
1438        private String mstrValue;
1439        /** Variable to hold comments associated with this property */
1440        private String mstrComments;
1441
1442        /**
1443         * Constructor
1444         * @param pstrName the name of this property.
1445         * @param pstrValue the value of this property.
1446         * @param pstrComments the comments associated with this property.
1447         */
1448        public INIProperty(String pstrName, String pstrValue, String pstrComments)
1449        {
1450            this.mstrName = pstrName;
1451            this.mstrValue = pstrValue;
1452            this.mstrComments = delRemChars(pstrComments);
1453        }
1454
1455        /**
1456         * Returns value of this property. If value contains a reference to 
1457         * environment avriable then this reference is replaced by actual value
1458         * before the value is returned.
1459         * @return the value of this property.
1460         */
1461        public String getPropValue()
1462        {
1463            int    intStart = 0;
1464            int    intEnd   = 0;
1465            String strVal   = null;
1466            String strVar   = null;
1467            String strRet   = null;
1468
1469            strRet = this.mstrValue;
1470            intStart = strRet.indexOf("%");
1471            if (intStart >= 0)
1472            {
1473                intEnd = strRet.indexOf("%", intStart + 1);
1474                strVar = strRet.substring(intStart + 1, intEnd);
1475                strVal = mpropEnv.getProperty(strVar);
1476                if (strVal != null)
1477                {
1478                    strRet = strRet.substring(0, intStart) + strVal + 
1479                                strRet.substring(intEnd + 1);
1480                }
1481            }
1482            return strRet;
1483        }
1484
1485        /* (non-Javadoc)
1486         * @see java.lang.Object#toString()
1487         */
1488        @Override
1489                public String toString()
1490        {
1491            String strRet = "";
1492
1493            if (this.mstrComments != null)
1494                strRet = addRemChars(mstrComments);
1495            strRet = strRet + this.mstrName + " = " + this.mstrValue;
1496            return strRet;
1497        }
1498    }
1499}