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