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