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