1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 import java.io.File;
29 import java.io.RandomAccessFile;
30 import java.util.ArrayList;
31 import java.util.Enumeration;
32 
33 
34 /**
35  * Simple implementation of a inifile manager
36  */
37 class GlobalLogWriter
38 {
39     public static void println(String _s)
40     {
41         System.out.println(_s);
42     }
43 }
44 
45 /**
46    Helper class to give a simple API to read/write windows like ini files
47 */
48 
49 /* public */ // is only need, if we need this class outside package convwatch
50 public class IniFile implements Enumeration
51 {
52 
53     /**
54      * internal representation of the ini file content.
55      * Problem, if ini file changed why other write something difference, we don't realise this.
56      */
57     private String m_sFilename;
58     // private File m_aFile;
59     private ArrayList<String> m_aList;
60     boolean m_bListContainUnsavedChanges = false;
61     private int m_aEnumerationPos = 0;
62 
63     /**
64        open a ini file by it's name
65        @param _sFilename string a filename, if the file doesn't exist, a new empty ini file will create.
66        write back to disk only if there are really changes.
67     */
68     public IniFile(String _sFilename)
69         {
70             m_sFilename = _sFilename;
71             // m_aFile = new File(_sFilename);
72             m_aList = loadLines();
73             m_aEnumerationPos = findNextSection(0);
74         }
75 
76     /**
77        open a ini file by it's name
78        @param _aFile a java.io.File object, if the file doesn't exist, a new empty ini file will create.
79        write back to disk only if there are really changes.
80     */
81     public IniFile(File _aFile)
82     {
83         m_sFilename = _aFile.getAbsolutePath();
84         m_aList = loadLines();
85         m_aEnumerationPos = findNextSection(0);
86     }
87 
88     public void insertFirstComment(String[] _aList)
89         {
90             if (m_aList.size() == 0)
91             {
92                 // can only insert if there is nothing else already in the ini file
93                 for (int i = 0; i < _aList.length; i++)
94                 {
95                     m_aList.add(_aList[i]);
96                 }
97             }
98         }
99 
100     private ArrayList<String> loadLines()
101         {
102             ArrayList<String> aLines = new ArrayList<String>();
103             File aFile = new File(m_sFilename);
104             if (!aFile.exists())
105             {
106                 // GlobalLogWriter.println("couldn't find file '" + m_sFilename + "', will be created.");
107                 // DebugHelper.exception(BasicErrorCode.SbERR_FILE_NOT_FOUND, "");
108                 // m_bListContainUnsavedChanges = false;
109                 return aLines;
110             }
111             RandomAccessFile aReader = null;
112             // BufferedReader aReader;
113             try
114             {
115                 aReader = new RandomAccessFile(aFile, "r");
116                 String aLine = "";
117                 while (aLine != null)
118                 {
119                     aLine = aReader.readLine();
120                     if (aLine != null && aLine.length() > 0)
121                     {
122                         aLines.add(aLine);
123                     }
124                 }
125             }
126             catch (java.io.FileNotFoundException fne)
127             {
128                 GlobalLogWriter.println("couldn't open file " + m_sFilename);
129                 GlobalLogWriter.println("Message: " + fne.getMessage());
130                 // DebugHelper.exception(BasicErrorCode.SbERR_FILE_NOT_FOUND, "");
131             }
132             catch (java.io.IOException ie)
133             {
134                 GlobalLogWriter.println("Exception occurs while reading from file " + m_sFilename);
135                 GlobalLogWriter.println("Message: " + ie.getMessage());
136                 // DebugHelper.exception(BasicErrorCode.SbERR_INTERNAL_ERROR, ie.getMessage());
137             }
138             try
139             {
140                 aReader.close();
141             }
142             catch (java.io.IOException ie)
143             {
144                 GlobalLogWriter.println("Couldn't close file " + m_sFilename);
145                 GlobalLogWriter.println("Message: " + ie.getMessage());
146                 // DebugHelper.exception(BasicErrorCode.SbERR_INTERNAL_ERROR, ie.getMessage());
147             }
148             return aLines;
149         }
150 
151     /**
152      * @return true, if the ini file contain some readable data
153      */
154     public boolean is()
155         {
156             return m_aList.size() > 1 ? true : false;
157         }
158 
159     /**
160      * Check if a given Section and Key exists in the ini file
161      * @param _sSectionName
162      * @param _sKey
163      * @return true if the given Section, Key exists, now you can get the value
164      */
165     public boolean hasValue(String _sSectionName, String _sKey)
166         {
167             int n = findKey(_sSectionName, _sKey);
168             if (n > 0)
169             {
170                 return true;
171             }
172             return false;
173         }
174     // -----------------------------------------------------------------------------
175 
176     private boolean isRemark(String _sLine)
177         {
178             if (((_sLine.length() < 2)) ||
179                 (_sLine.startsWith("#")) ||
180                 (_sLine.startsWith(";")))
181             {
182                 return true;
183             }
184             return false;
185         }
186 
187     private String getItem(int i)
188         {
189             return m_aList.get(i);
190         }
191 
192     private String buildSectionName(String _sSectionName)
193         {
194             String sFindSection = "[" + _sSectionName + "]";
195             return sFindSection;
196         }
197 
198     private String sectionToString(String _sSectionName)
199         {
200             String sKeyName = _sSectionName;
201             if (sKeyName.startsWith("[") &&
202                 sKeyName.endsWith("]"))
203             {
204                 sKeyName = sKeyName.substring(1, sKeyName.length() - 1);
205             }
206             return sKeyName;
207         }
208 
209     private String toLowerIfNeed(String _sName)
210         {
211             return _sName.toLowerCase();
212         }
213 
214     // return the number where this section starts
215     private int findSection(String _sSection)
216         {
217             String sFindSection = toLowerIfNeed(buildSectionName(_sSection));
218             // ----------- find _sSection ---------------
219             int i;
220             for (i = 0; i < m_aList.size(); i++)
221             {
222                 String sLine = toLowerIfNeed(getItem(i).trim());
223                 if (isRemark(sLine))
224                 {
225                     continue;
226                 }
227                 if (sFindSection.equals("[]"))
228                 {
229                     // special case, empty Section.
230                     return i - 1;
231                 }
232                 if (sLine.startsWith(sFindSection))
233                 {
234                     return i;
235                 }
236             }
237             return -1;
238         }
239 
240     /**
241      * Checks if a given section exists in the ini file
242      * @param _sSection
243      * @return true if the given _sSection was found
244      */
245     public boolean hasSection(String _sSection)
246         {
247             int i = findSection(_sSection);
248             if (i == -1)
249             {
250                 return false;
251             }
252             return true;
253         }
254 
255     // return the line number, where the key is found.
256     private int findKey(String _sSection, String _sKey)
257         {
258             int i = findSection(_sSection);
259             if (i == -1)
260             {
261                 // Section not found, therefore the value can't exist
262                 return -1;
263             }
264             return findKeyFromKnownSection(i, _sKey);
265         }
266 
267     // i must be the index in the list, where the well known section starts
268     private int findKeyFromKnownSection(int _nSectionIndex, String _sKey)
269         {
270             _sKey = toLowerIfNeed(_sKey);
271             for (int j = _nSectionIndex + 1; j < m_aList.size(); j++)
272             {
273                 String sLine = getItem(j).trim();
274 
275                 if (isRemark(sLine))
276                 {
277                     continue;
278                 }
279                 if (sLine.startsWith("[") /* && sLine.endsWith("]") */)
280                 {
281                     // TODO: due to the fact we would like to insert an empty line before new sections
282                     // TODO: we should check if we are in an empty line and if, go back one line.
283 
284                     // found end.
285                     break;
286                 }
287 
288                 int nEqual = sLine.indexOf("=");
289                 if (nEqual >= 0)
290                 {
291                     String sKey = toLowerIfNeed(sLine.substring(0, nEqual).trim());
292                     if (sKey.equals(_sKey))
293                     {
294                         return j;
295                     }
296                 }
297             }
298             return -1;
299         }
300 
301     // i must be the index in the list, where the well known section starts
302     private int findLastKnownKeyIndex(int _nSectionIndex, String _sKey)
303         {
304             _sKey = toLowerIfNeed(_sKey);
305             int i = _nSectionIndex + 1;
306             for (int j = i; j < m_aList.size(); j++)
307             {
308                 String sLine = getItem(j).trim();
309 
310                 if (isRemark(sLine))
311                 {
312                     continue;
313                 }
314 
315                 if (sLine.startsWith("[") /* && sLine.endsWith("]") */)
316                 {
317                     // found end.
318                     return j;
319                 }
320 
321                 int nEqual = sLine.indexOf("=");
322                 if (nEqual >= 0)
323                 {
324                     String sKey = toLowerIfNeed(sLine.substring(0, nEqual).trim());
325                     if (sKey.equals(_sKey))
326                     {
327                         return j;
328                     }
329                 }
330             }
331             return i;
332         }
333 
334     private String getValue(int _nIndex)
335         {
336             String sLine = getItem(_nIndex).trim();
337             if (isRemark(sLine))
338             {
339                 return "";
340             }
341             int nEqual = sLine.indexOf("=");
342             if (nEqual >= 0)
343             {
344                 String sKey = sLine.substring(0, nEqual).trim();
345                 String sValue = sLine.substring(nEqual + 1).trim();
346                 return sValue;
347             }
348             return "";
349         }
350 
351     /**
352        @param _sSection string
353        @param _sKey string
354        @return the value found in the inifile which is given by the section and key parameter
355     */
356     // private int m_nCurrentPosition;
357     // private String m_sOldKey;
358     public String getValue(String _sSection, String _sKey)
359         {
360             String sValue = "";
361             int m_nCurrentPosition = findKey(_sSection, _sKey);
362             if (m_nCurrentPosition == -1)
363             {
364                 // Section not found, therefore the value can't exist
365                 return "";
366             }
367 
368             // m_sOldKey = _sKey;
369             sValue = getValue(m_nCurrentPosition);
370 
371             return sValue;
372         }
373 
374 //    private String getNextValue()
375 //    {
376 //        if (m_nCurrentPosition >= 0)
377 //        {
378 //            ++m_nCurrentPosition;
379 //            String sValue = getValue(m_nCurrentPosition);
380 //            return sValue;
381 //        }
382 //        return "";
383 //    }
384     /**
385      * Returns the value at Section, Key converted to an integer
386      * Check with hasValue(Section, Key) to check before you get into trouble.
387      * @param _sSection
388      * @param _sKey
389      * @param _nDefault if there is a problem, key not found... this value will return
390      * @return the value as integer if possible to convert, if not return default value.
391      */
392     public int getIntValue(String _sSection, String _sKey, int _nDefault)
393         {
394             String sValue = getValue(_sSection, _sKey);
395             int nValue = _nDefault;
396             if (sValue.length() > 0)
397             {
398                 try
399                 {
400                     nValue = Integer.valueOf(sValue).intValue();
401                 }
402                 catch (java.lang.NumberFormatException e)
403                 {
404                     GlobalLogWriter.println("IniFile.getIntValue(): Caught a number format exception, return the default value.");
405                 }
406             }
407             return nValue;
408         }
409 
410 /**
411  * close a open inifile.
412  * If there are changes, all changes will store back to disk.
413  */
414     public void close()
415         {
416             store();
417         }
418 
419     /**
420        write back the ini file to the disk, only if there exist changes
421        * @deprecated use close() instead!
422        */
423 
424     // TODO: make private
425     private void store()
426         {
427             if (m_bListContainUnsavedChanges == false)
428             {
429                 // nothing has changed, so no need to store
430                 return;
431             }
432 
433             File aFile = new File(m_sFilename);
434             if (aFile.exists())
435             {
436                 // System.out.println("couldn't find file " + m_sFilename);
437                 // TODO: little bit unsafe here, first rename, after write is complete, delete the old.
438                 aFile.delete();
439                 if (aFile.exists())
440                 {
441                     GlobalLogWriter.println("Couldn't delete the file " + m_sFilename);
442                     return;
443                     // DebugHelper.exception(BasicErrorCode.SbERR_INTERNAL_ERROR, "Couldn't delete the file " + m_sFilename);
444                 }
445             }
446             // if (! aFile.canWrite())
447             // {
448             //    System.out.println("Couldn't write to file " + m_sFilename);
449             //    DebugHelper.exception(BasicErrorCode.SbERR_INTERNAL_ERROR, "");
450             // }
451             try
452             {
453                 RandomAccessFile aWriter = new RandomAccessFile(aFile, "rw");
454                 for (int i = 0; i < m_aList.size(); i++)
455                 {
456                     String sLine = getItem(i);
457                     if (sLine.startsWith("["))
458                     {
459                         // write an extra empty line before next section.
460                         aWriter.writeByte((int) '\n');
461                     }
462                     aWriter.writeBytes(sLine);
463                     aWriter.writeByte((int) '\n');
464                 }
465                 aWriter.close();
466             }
467             catch (java.io.FileNotFoundException fne)
468             {
469                 GlobalLogWriter.println("couldn't open file for writing " + m_sFilename);
470                 GlobalLogWriter.println("Message: " + fne.getMessage());
471                 // DebugHelper.exception(BasicErrorCode.SbERR_FILE_NOT_FOUND, "");
472             }
473             catch (java.io.IOException ie)
474             {
475                 GlobalLogWriter.println("Exception occurs while writing to file " + m_sFilename);
476                 GlobalLogWriter.println("Message: " + ie.getMessage());
477                 // DebugHelper.exception(BasicErrorCode.SbERR_INTERNAL_ERROR, ie.getMessage());
478             }
479         }
480 
481     public void insertValue(String _sSection, String _sKey, int _nValue)
482         {
483             insertValue(_sSection, _sKey, String.valueOf(_nValue));
484         }
485 
486     public void insertValue(String _sSection, String _sKey, long _nValue)
487         {
488             insertValue(_sSection, _sKey, String.valueOf(_nValue));
489         }
490 
491     /**
492        insert a value
493        there are 3 cases
494        1. section doesn't exist, goto end and insert a new section, insert a new key value pair
495        2. section exist but key not, search section, search key, if key is -1 get last known key position and insert new key value pair there
496        3. section exist and key exist, remove the old key and insert the key value pair at the same position
497      * @param _sSection
498      * @param _sKey
499      * @param _sValue
500      */
501     public void insertValue(String _sSection, String _sKey, String _sValue)
502         {
503             int i = findSection(_sSection);
504             if (i == -1)
505             {
506                 // case 1: section doesn't exist
507                 String sFindSection = buildSectionName(_sSection);
508 
509                 // TODO: before create a new Section, insert a empty line
510                 m_aList.add(sFindSection);
511                 if (_sKey.length() > 0)
512                 {
513                     String sKeyValuePair = _sKey + "=" + _sValue;
514                     m_aList.add(sKeyValuePair);
515                 }
516                 m_bListContainUnsavedChanges = true;
517                 return;
518             }
519             int j = findKeyFromKnownSection(i, _sKey);
520             if (j == -1)
521             {
522                 // case 2: section exist, but not the key
523                 j = findLastKnownKeyIndex(i, _sKey);
524                 if (_sKey.length() > 0)
525                 {
526                     String sKeyValuePair = _sKey + "=" + _sValue;
527                     m_aList.add(j, sKeyValuePair);
528                     m_bListContainUnsavedChanges = true;
529                 }
530                 return;
531             }
532             else
533             {
534                 // case 3: section exist, and also the key
535                 String sKeyValuePair = _sKey + "=" + _sValue;
536                 m_aList.set(j, sKeyValuePair);
537                 m_bListContainUnsavedChanges = true;
538             }
539         }
540     // -----------------------------------------------------------------------------
541     // String replaceEvaluatedValue(String _sSection, String _sValue)
542     //     {
543     //         String sValue = _sValue;
544     //         int nIndex = 0;
545     //         while (( nIndex = sValue.indexOf("$(", nIndex)) >= 0)
546     //         {
547     //             int nNextIndex = sValue.indexOf(")", nIndex);
548     //             if (nNextIndex >= 0)
549     //             {
550     //                 String sKey = sValue.substring(nIndex + 2, nNextIndex);
551     //                 String sNewValue = getValue(_sSection, sKey);
552     //                 if (sNewValue != null && sNewValue.length() > 0)
553     //                 {
554     //                     String sRegexpKey = "\\$\\(" + sKey + "\\)";
555     //                     sValue = sValue.replaceAll(sRegexpKey, sNewValue);
556     //                 }
557     //                 nIndex = nNextIndex;
558     //             }
559     //             else
560     //             {
561     //                 nIndex += 2;
562     //             }
563     //         }
564     //         return sValue;
565     //     }
566     // -----------------------------------------------------------------------------
567 
568     // public String getLocalEvaluatedValue(String _sSection, String _sKey)
569     //     {
570     //         String sValue = getValue(_sSection, _sKey);
571     //         sValue = replaceEvaluatedValue(_sSection, sValue);
572     //         return sValue;
573     //     }
574 
575     // -----------------------------------------------------------------------------
576 
577     // this is a special behaviour.
578     // public String getGlobalLocalEvaluatedValue(String _sSection, String _sKey)
579     //     {
580     //         String sGlobalValue = getKey("global", _sKey);
581     //         String sLocalValue = getKey(_sSection, _sKey);
582     //         if (sLocalValue.length() == 0)
583     //         {
584     //             sGlobalValue = replaceEvaluatedKey(_sSection, sGlobalValue);
585     //             sGlobalValue = replaceEvaluatedKey("global", sGlobalValue);
586     //             return sGlobalValue;
587     //         }
588     //         sLocalValue = replaceEvaluatedKey(_sSection, sLocalValue);
589     //         sLocalValue = replaceEvaluatedKey("global", sLocalValue);
590     //
591     //         return sLocalValue;
592     //     }
593     public void removeSection(String _sSectionToRemove)
594         {
595             // first, search for the name
596             int i = findSection(_sSectionToRemove);
597             if (i == -1)
598             {
599                 // Section to remove not found, do nothing.
600                 return;
601             }
602             // second, find the next section
603             int j = findNextSection(i + 1);
604             if (j == -1)
605             {
606                 // if we are at the end, use size() as second section
607                 j = m_aList.size();
608             }
609             // remove all between first and second section
610             for (int k = i; k < j; k++)
611             {
612                 m_aList.remove(i);
613             }
614             // mark the list as changed
615             m_bListContainUnsavedChanges = true;
616         }
617 
618     /**
619      * some tests for this class
620      */
621 //    public static void main(String[] args)
622 //        {
623 //            String sTempFile = System.getProperty("java.io.tmpdir");
624 //            sTempFile += "inifile";
625 //
626 //
627 //            IniFile aIniFile = new IniFile(sTempFile);
628 //            String sValue = aIniFile.getValue("Section", "Key");
629 //            // insert a new value to a already exist section
630 //            aIniFile.insertValue("Section", "Key2", "a new value in a existing section");
631 //            // replace a value
632 //            aIniFile.insertValue("Section", "Key", "replaced value");
633 //            // create a new value
634 //            aIniFile.insertValue("New Section", "Key", "a new key value pair");
635 //            aIniFile.insertValue("New Section", "Key2", "a new second key value pair");
636 //
637 //            String sValue2 = aIniFile.getValue("Section2", "Key");
638 //
639 //            aIniFile.removeSection("Section");
640 //            aIniFile.removeSection("New Section");
641 //
642 //            aIniFile.close();
643 //        }
644 
645     /**
646      * Enumeration Interface
647      * @return true, if there are more Key values
648      */
649     public boolean hasMoreElements()
650         {
651             if (m_aEnumerationPos >= 0 &&
652                 m_aEnumerationPos < m_aList.size())
653             {
654                 return true;
655             }
656             return false;
657         }
658 
659     /**
660      * Find the next line, which starts with '['
661      * @param i start position
662      * @return the line where '[' found or -1
663      */
664     private int findNextSection(int i)
665         {
666             if (i >= 0)
667             {
668                 while (i < m_aList.size())
669                 {
670                     String sLine =  m_aList.get(i);
671                     if (sLine.startsWith("["))
672                     {
673                         return i;
674                     }
675                     i++;
676                 }
677             }
678             return -1;
679         }
680 
681     /**
682      * Enumeration Interface
683      * @return a key without the enveloped '[' ']'
684      */
685     public Object nextElement()
686         {
687             int nLineWithSection = findNextSection(m_aEnumerationPos);
688             if (nLineWithSection != -1)
689             {
690                 String sSection =  m_aList.get(nLineWithSection);
691                 m_aEnumerationPos = findNextSection(nLineWithSection + 1);
692                 sSection = sectionToString(sSection);
693                 return sSection;
694             }
695             else
696             {
697                 m_aEnumerationPos = m_aList.size();
698             }
699             return null;
700         }
701 
702     /**
703      * Helper to count the occurence of Sections
704      * @return returns the count of '^['.*']$' Elements
705      */
706     public int getElementCount()
707         {
708             int nCount = 0;
709             int nPosition = 0;
710             while ((nPosition = findNextSection(nPosition)) != -1)
711             {
712                 nCount++;
713                 nPosition++;
714             }
715             return nCount;
716         }
717 }
718 
719