/jEdit/tags/jedit-4-1-pre10/org/gjt/sp/jedit/MiscUtilities.java

# · Java · 1198 lines · 708 code · 96 blank · 394 comment · 180 complexity · 1a7d949e5954ddd80a8080c8de3544e7 MD5 · raw file

  1. /*
  2. * MiscUtilities.java - Various miscallaneous utility functions
  3. * :tabSize=8:indentSize=8:noTabs=false:
  4. * :folding=explicit:collapseFolds=1:
  5. *
  6. * Copyright (C) 1999, 2000, 2001 Slava Pestov
  7. * Portions copyright (C) 2000 Richard S. Hall
  8. * Portions copyright (C) 2001 Dirk Moebius
  9. *
  10. * This program is free software; you can redistribute it and/or
  11. * modify it under the terms of the GNU General Public License
  12. * as published by the Free Software Foundation; either version 2
  13. * of the License, or any later version.
  14. *
  15. * This program 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 General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU General Public License
  21. * along with this program; if not, write to the Free Software
  22. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  23. */
  24. package org.gjt.sp.jedit;
  25. //{{{ Imports
  26. import javax.swing.text.Segment;
  27. import javax.swing.JMenuItem;
  28. import java.io.*;
  29. import java.util.*;
  30. import org.gjt.sp.jedit.io.*;
  31. import org.gjt.sp.util.Log;
  32. //}}}
  33. /**
  34. * Path name manipulation, string manipulation, and more.<p>
  35. *
  36. * The most frequently used members of this class are:<p>
  37. *
  38. * <b>Some path name methods:</b><p>
  39. * <ul>
  40. * <li>{@link #getFileName(String)}</li>
  41. * <li>{@link #getParentOfPath(String)}</li>
  42. * <li>{@link #constructPath(String,String)}</li>
  43. * </ul>
  44. * <b>String comparison:</b><p>
  45. * A {@link #compareStrings(String,String,boolean)} method that unlike
  46. * <function>String.compareTo()</function>, correctly recognizes and handles
  47. * embedded numbers.<p>
  48. *
  49. * This class also defines several inner classes for use with the
  50. * sorting features of the Java collections API:
  51. *
  52. * <ul>
  53. * <li>{@link MiscUtilities.StringCompare}</li>
  54. * <li>{@link MiscUtilities.StringICaseCompare}</li>
  55. * <li>{@link MiscUtilities.MenuItemCompare}</li>
  56. * </ul>
  57. *
  58. * For example, you might call:<p>
  59. *
  60. * <code>Arrays.sort(myListOfStrings,
  61. * new MiscUtilities.StringICaseCompare());</code>
  62. *
  63. * @author Slava Pestov
  64. * @author John Gellene (API documentation)
  65. * @version $Id: MiscUtilities.java 4469 2003-02-08 18:53:03Z spestov $
  66. */
  67. public class MiscUtilities
  68. {
  69. //{{{ Path name methods
  70. //{{{ canonPath() method
  71. /**
  72. * Returns the canonical form of the specified path name. Currently
  73. * only expands a leading <code>~</code>. <b>For local path names
  74. * only.</b>
  75. * @param path The path name
  76. * @since jEdit 4.0pre2
  77. */
  78. public static String canonPath(String path)
  79. {
  80. if(File.separatorChar == '\\')
  81. {
  82. // get rid of mixed paths on Windows
  83. path = path.replace('/','\\');
  84. }
  85. if(path.startsWith("~" + File.separator))
  86. {
  87. path = path.substring(2);
  88. String home = System.getProperty("user.home");
  89. if(home.endsWith(File.separator))
  90. return home + path;
  91. else
  92. return home + File.separator + path;
  93. }
  94. else if(path.equals("~"))
  95. return System.getProperty("user.home");
  96. else
  97. return path;
  98. } //}}}
  99. //{{{ constructPath() method
  100. /**
  101. * Constructs an absolute path name from a directory and another
  102. * path name. This method is VFS-aware.
  103. * @param parent The directory
  104. * @param path The path name
  105. */
  106. public static String constructPath(String parent, String path)
  107. {
  108. if(MiscUtilities.isURL(path))
  109. return path;
  110. else if(path.startsWith("~"))
  111. return path;
  112. else
  113. {
  114. // have to handle these cases specially on windows.
  115. if(OperatingSystem.isDOSDerived())
  116. {
  117. if(path.length() == 2 && path.charAt(1) == ':')
  118. return path;
  119. else if(path.length() > 2 && path.charAt(1) == ':')
  120. {
  121. if(path.charAt(2) != '\\')
  122. {
  123. path = path.substring(0,2) + '\\'
  124. + path.substring(2);
  125. }
  126. return resolveSymlinks(path);
  127. }
  128. else if(path.startsWith("\\\\"))
  129. return resolveSymlinks(path);
  130. }
  131. else if(OperatingSystem.isUnix())
  132. {
  133. // nice and simple
  134. if(path.length() > 0 && path.charAt(0) == '/')
  135. return resolveSymlinks(path);
  136. }
  137. }
  138. if(parent == null)
  139. parent = System.getProperty("user.dir");
  140. if(OperatingSystem.isDOSDerived() && path.startsWith("\\"))
  141. parent = parent.substring(0,2);
  142. VFS vfs = VFSManager.getVFSForPath(parent);
  143. return vfs.constructPath(parent,path);
  144. } //}}}
  145. //{{{ constructPath() method
  146. /**
  147. * Constructs an absolute path name from three path components.
  148. * This method is VFS-aware.
  149. * @param parent The parent directory
  150. * @param path1 The first path
  151. * @param path2 The second path
  152. */
  153. public static String constructPath(String parent,
  154. String path1, String path2)
  155. {
  156. return constructPath(constructPath(parent,path1),path2);
  157. } //}}}
  158. //{{{ concatPath() method
  159. /**
  160. * Like {@link #constructPath}, except <code>path</code> will be
  161. * appended to <code>parent</code> even if it is absolute.
  162. * @param path
  163. * @param parent
  164. */
  165. public static String concatPath(String parent, String path)
  166. {
  167. parent = canonPath(parent);
  168. path = canonPath(path);
  169. // Make all child paths relative.
  170. if (path.startsWith(File.separator))
  171. path = path.substring(1);
  172. else if ((path.length() >= 3) && (path.charAt(1) == ':'))
  173. path = path.replace(':', File.separatorChar);
  174. if (parent == null)
  175. parent = System.getProperty("user.dir");
  176. if (parent.endsWith(File.separator))
  177. return parent + path;
  178. else
  179. return parent + File.separator + path;
  180. } //}}}
  181. //{{{ getFileExtension() method
  182. /**
  183. * Returns the extension of the specified filename, or an empty
  184. * string if there is none.
  185. * @param name The file name
  186. */
  187. public static String getFileExtension(String name)
  188. {
  189. int index = name.indexOf('.');
  190. if(index == -1)
  191. return "";
  192. else
  193. return name.substring(index);
  194. } //}}}
  195. //{{{ getFileName() method
  196. /**
  197. * Returns the last component of the specified path.
  198. * This method is VFS-aware.
  199. * @param path The path name
  200. */
  201. public static String getFileName(String path)
  202. {
  203. return VFSManager.getVFSForPath(path).getFileName(path);
  204. } //}}}
  205. //{{{ getFileNameNoExtension() method
  206. /**
  207. * Returns the last component of the specified path name without the
  208. * trailing extension (if there is one).
  209. * @param path The path name
  210. * @since jEdit 4.0pre8
  211. */
  212. public static String getFileNameNoExtension(String path)
  213. {
  214. String name = getFileName(path);
  215. int index = name.lastIndexOf('.');
  216. if(index == -1)
  217. return name;
  218. else
  219. return name.substring(0,index);
  220. } //}}}
  221. //{{{ getFileParent() method
  222. /**
  223. * @deprecated Call getParentOfPath() instead
  224. */
  225. public static String getFileParent(String path)
  226. {
  227. return getParentOfPath(path);
  228. } //}}}
  229. //{{{ getParentOfPath() method
  230. /**
  231. * Returns the parent of the specified path. This method is VFS-aware.
  232. * @param path The path name
  233. * @since jEdit 2.6pre5
  234. */
  235. public static String getParentOfPath(String path)
  236. {
  237. return VFSManager.getVFSForPath(path).getParentOfPath(path);
  238. } //}}}
  239. //{{{ getFileProtocol() method
  240. /**
  241. * @deprecated Call getProtocolOfURL() instead
  242. */
  243. public static String getFileProtocol(String url)
  244. {
  245. return getProtocolOfURL(url);
  246. } //}}}
  247. //{{{ getProtocolOfURL() method
  248. /**
  249. * Returns the protocol specified by a URL.
  250. * @param url The URL
  251. * @since jEdit 2.6pre5
  252. */
  253. public static String getProtocolOfURL(String url)
  254. {
  255. return url.substring(0,url.indexOf(':'));
  256. } //}}}
  257. //{{{ isURL() method
  258. /**
  259. * Checks if the specified string is a URL.
  260. * @param str The string to check
  261. * @return True if the string is a URL, false otherwise
  262. */
  263. public static boolean isURL(String str)
  264. {
  265. int fsIndex = Math.max(str.indexOf(File.separatorChar),
  266. str.indexOf('/'));
  267. if(fsIndex == 0) // /etc/passwd
  268. return false;
  269. else if(fsIndex == 2) // C:\AUTOEXEC.BAT
  270. return false;
  271. int cIndex = str.indexOf(':');
  272. if(cIndex <= 1) // D:\WINDOWS
  273. return false;
  274. else if(fsIndex != -1 && cIndex > fsIndex) // /tmp/RTF::read.pm
  275. return false;
  276. return true;
  277. } //}}}
  278. //{{{ saveBackup() method
  279. /**
  280. * Saves a backup (optionally numbered) of a file.
  281. * @param file A local file
  282. * @param backups The number of backups. Must be >= 1. If > 1, backup
  283. * files will be numbered.
  284. * @param backupPrefix The backup file name prefix
  285. * @param backupSuffix The backup file name suffix
  286. * @param backupDirectory The directory where to save backups; if null,
  287. * they will be saved in the same directory as the file itself.
  288. * @since jEdit 4.0pre1
  289. */
  290. public static void saveBackup(File file, int backups,
  291. String backupPrefix, String backupSuffix,
  292. String backupDirectory)
  293. {
  294. if(backupPrefix == null)
  295. backupPrefix = "";
  296. if(backupSuffix == null)
  297. backupSuffix = "";
  298. String name = file.getName();
  299. // If backups is 1, create ~ file
  300. if(backups == 1)
  301. {
  302. File backupFile = new File(backupDirectory,
  303. backupPrefix + name + backupSuffix);
  304. backupFile.delete();
  305. file.renameTo(backupFile);
  306. }
  307. // If backups > 1, move old ~n~ files, create ~1~ file
  308. else
  309. {
  310. new File(backupDirectory,
  311. backupPrefix + name + backupSuffix
  312. + backups + backupSuffix).delete();
  313. for(int i = backups - 1; i > 0; i--)
  314. {
  315. File backup = new File(backupDirectory,
  316. backupPrefix + name + backupSuffix
  317. + i + backupSuffix);
  318. backup.renameTo(new File(backupDirectory,
  319. backupPrefix + name + backupSuffix
  320. + (i+1) + backupSuffix));
  321. }
  322. file.renameTo(new File(backupDirectory,
  323. backupPrefix + name + backupSuffix
  324. + "1" + backupSuffix));
  325. }
  326. } //}}}
  327. //{{{ fileToClass() method
  328. /**
  329. * Converts a file name to a class name. All slash characters are
  330. * replaced with periods and the trailing '.class' is removed.
  331. * @param name The file name
  332. */
  333. public static String fileToClass(String name)
  334. {
  335. char[] clsName = name.toCharArray();
  336. for(int i = clsName.length - 6; i >= 0; i--)
  337. if(clsName[i] == '/')
  338. clsName[i] = '.';
  339. return new String(clsName,0,clsName.length - 6);
  340. } //}}}
  341. //{{{ classToFile() method
  342. /**
  343. * Converts a class name to a file name. All periods are replaced
  344. * with slashes and the '.class' extension is added.
  345. * @param name The class name
  346. */
  347. public static String classToFile(String name)
  348. {
  349. return name.replace('.','/').concat(".class");
  350. } //}}}
  351. //}}}
  352. //{{{ Text methods
  353. //{{{ getLeadingWhiteSpace() method
  354. /**
  355. * Returns the number of leading white space characters in the
  356. * specified string.
  357. * @param str The string
  358. */
  359. public static int getLeadingWhiteSpace(String str)
  360. {
  361. int whitespace = 0;
  362. loop: for(;whitespace < str.length();)
  363. {
  364. switch(str.charAt(whitespace))
  365. {
  366. case ' ': case '\t':
  367. whitespace++;
  368. break;
  369. default:
  370. break loop;
  371. }
  372. }
  373. return whitespace;
  374. } //}}}
  375. //{{{ getTrailingWhiteSpace() method
  376. /**
  377. * Returns the number of trailing whitespace characters in the
  378. * specified string.
  379. * @param str The string
  380. * @since jEdit 2.5pre5
  381. */
  382. public static int getTrailingWhiteSpace(String str)
  383. {
  384. int whitespace = 0;
  385. loop: for(int i = str.length() - 1; i >= 0; i--)
  386. {
  387. switch(str.charAt(i))
  388. {
  389. case ' ': case '\t':
  390. whitespace++;
  391. break;
  392. default:
  393. break loop;
  394. }
  395. }
  396. return whitespace;
  397. } //}}}
  398. //{{{ getLeadingWhiteSpaceWidth() method
  399. /**
  400. * Returns the width of the leading white space in the specified
  401. * string.
  402. * @param str The string
  403. * @param tabSize The tab size
  404. */
  405. public static int getLeadingWhiteSpaceWidth(String str, int tabSize)
  406. {
  407. int whitespace = 0;
  408. loop: for(int i = 0; i < str.length(); i++)
  409. {
  410. switch(str.charAt(i))
  411. {
  412. case ' ':
  413. whitespace++;
  414. break;
  415. case '\t':
  416. whitespace += (tabSize - whitespace % tabSize);
  417. break;
  418. default:
  419. break loop;
  420. }
  421. }
  422. return whitespace;
  423. } //}}}
  424. //{{{ getVirtualWidth() method
  425. /**
  426. * Returns the virtual column number (taking tabs into account) of the
  427. * specified offset in the segment.
  428. *
  429. * @param seg The segment
  430. * @param tabSize The tab size
  431. * @since jEdit 4.1pre1
  432. */
  433. public static int getVirtualWidth(Segment seg, int tabSize)
  434. {
  435. int virtualPosition = 0;
  436. for (int i = 0; i < seg.count; i++)
  437. {
  438. char ch = seg.array[seg.offset + i];
  439. if (ch == '\t')
  440. {
  441. virtualPosition += tabSize
  442. - (virtualPosition % tabSize);
  443. }
  444. else
  445. {
  446. ++virtualPosition;
  447. }
  448. }
  449. return virtualPosition;
  450. } //}}}
  451. //{{{ getOffsetOfVirtualColumn() method
  452. /**
  453. * Returns the array offset of a virtual column number (taking tabs
  454. * into account) in the segment.
  455. *
  456. * @param seg The segment
  457. * @param tabSize The tab size
  458. * @param column The virtual column number
  459. * @param totalVirtualWidth If this array is non-null, the total
  460. * virtual width will be stored in its first location if this method
  461. * returns -1.
  462. *
  463. * @return -1 if the column is out of bounds
  464. *
  465. * @since jEdit 4.1pre1
  466. */
  467. public static int getOffsetOfVirtualColumn(Segment seg, int tabSize,
  468. int column, int[] totalVirtualWidth)
  469. {
  470. int virtualPosition = 0;
  471. for (int i = 0; i < seg.count; i++)
  472. {
  473. char ch = seg.array[seg.offset + i];
  474. if (ch == '\t')
  475. {
  476. int tabWidth = tabSize
  477. - (virtualPosition % tabSize);
  478. if(virtualPosition >= column)
  479. return i;
  480. else
  481. virtualPosition += tabWidth;
  482. }
  483. else
  484. {
  485. if(virtualPosition >= column)
  486. return i;
  487. else
  488. ++virtualPosition;
  489. }
  490. }
  491. if(totalVirtualWidth != null)
  492. totalVirtualWidth[0] = virtualPosition;
  493. return -1;
  494. } //}}}
  495. //{{{ createWhiteSpace() method
  496. /**
  497. * Creates a string of white space with the specified length.<p>
  498. *
  499. * To get a whitespace string tuned to the current buffer's
  500. * settings, call this method as follows:
  501. *
  502. * <pre>myWhitespace = MiscUtilities.createWhiteSpace(myLength,
  503. * (buffer.getBooleanProperty("noTabs") ? 0
  504. * : buffer.getTabSize()));</pre>
  505. *
  506. * @param len The length
  507. * @param tabSize The tab size, or 0 if tabs are not to be used
  508. */
  509. public static String createWhiteSpace(int len, int tabSize)
  510. {
  511. StringBuffer buf = new StringBuffer();
  512. if(tabSize == 0)
  513. {
  514. while(len-- > 0)
  515. buf.append(' ');
  516. }
  517. else
  518. {
  519. int count = len / tabSize;
  520. while(count-- > 0)
  521. buf.append('\t');
  522. count = len % tabSize;
  523. while(count-- > 0)
  524. buf.append(' ');
  525. }
  526. return buf.toString();
  527. } //}}}
  528. //{{{ globToRE() method
  529. /**
  530. * Converts a Unix-style glob to a regular expression.<p>
  531. *
  532. * ? becomes ., * becomes .*, {aa,bb} becomes (aa|bb).
  533. * @param glob The glob pattern
  534. */
  535. public static String globToRE(String glob)
  536. {
  537. StringBuffer buf = new StringBuffer();
  538. boolean backslash = false;
  539. boolean insideGroup = false;
  540. boolean insideNegativeLookahead = false;
  541. for(int i = 0; i < glob.length(); i++)
  542. {
  543. char c = glob.charAt(i);
  544. if(backslash)
  545. {
  546. buf.append('\\');
  547. buf.append(c);
  548. backslash = false;
  549. continue;
  550. }
  551. switch(c)
  552. {
  553. case '\\':
  554. backslash = true;
  555. break;
  556. case '?':
  557. buf.append('.');
  558. break;
  559. case '.':
  560. buf.append("\\.");
  561. break;
  562. case '*':
  563. buf.append(".*");
  564. break;
  565. case '{':
  566. buf.append('(');
  567. if(i + 1 != glob.length() && glob.charAt(i + 1) == '!')
  568. {
  569. buf.append('?');
  570. insideNegativeLookahead = true;
  571. }
  572. else
  573. insideGroup = true;
  574. break;
  575. case ',':
  576. if(insideGroup)
  577. {
  578. if(insideNegativeLookahead)
  579. {
  580. buf.append(").*");
  581. insideNegativeLookahead = false;
  582. }
  583. buf.append('|');
  584. }
  585. else
  586. buf.append(',');
  587. break;
  588. case '}':
  589. if(insideNegativeLookahead)
  590. {
  591. buf.append(").*");
  592. insideNegativeLookahead = false;
  593. }
  594. else if(insideGroup)
  595. {
  596. buf.append(')');
  597. insideGroup = false;
  598. }
  599. else
  600. buf.append('}');
  601. break;
  602. default:
  603. buf.append(c);
  604. }
  605. }
  606. return buf.toString();
  607. } //}}}
  608. //{{{ escapesToChars() method
  609. /**
  610. * Converts "\n" and "\t" escapes in the specified string to
  611. * newlines and tabs.
  612. * @param str The string
  613. * @since jEdit 2.3pre1
  614. */
  615. public static String escapesToChars(String str)
  616. {
  617. StringBuffer buf = new StringBuffer();
  618. for(int i = 0; i < str.length(); i++)
  619. {
  620. char c = str.charAt(i);
  621. switch(c)
  622. {
  623. case '\\':
  624. if(i == str.length() - 1)
  625. {
  626. buf.append('\\');
  627. break;
  628. }
  629. c = str.charAt(++i);
  630. switch(c)
  631. {
  632. case 'n':
  633. buf.append('\n');
  634. break;
  635. case 't':
  636. buf.append('\t');
  637. break;
  638. default:
  639. buf.append(c);
  640. break;
  641. }
  642. break;
  643. default:
  644. buf.append(c);
  645. }
  646. }
  647. return buf.toString();
  648. } //}}}
  649. //{{{ charsToEscapes() method
  650. /**
  651. * Escapes newlines, tabs, backslashes, and quotes in the specified
  652. * string.
  653. * @param str The string
  654. * @since jEdit 2.3pre1
  655. */
  656. public static String charsToEscapes(String str)
  657. {
  658. return charsToEscapes(str,"\n\t\\\"'");
  659. } //}}}
  660. //{{{ charsToEscapes() method
  661. /**
  662. * Escapes the specified characters in the specified string.
  663. * @param str The string
  664. * @param extra Any characters that require escaping
  665. * @since jEdit 4.1pre3
  666. */
  667. public static String charsToEscapes(String str, String toEscape)
  668. {
  669. StringBuffer buf = new StringBuffer();
  670. for(int i = 0; i < str.length(); i++)
  671. {
  672. char c = str.charAt(i);
  673. if(toEscape.indexOf(c) != -1)
  674. {
  675. if(c == '\n')
  676. buf.append("\\n");
  677. else if(c == '\t')
  678. buf.append("\\t");
  679. else
  680. {
  681. buf.append('\\');
  682. buf.append(c);
  683. }
  684. }
  685. else
  686. buf.append(c);
  687. }
  688. return buf.toString();
  689. } //}}}
  690. //{{{ compareVersions() method
  691. /**
  692. * @deprecated Call <code>compareStrings()</code> instead
  693. */
  694. public static int compareVersions(String v1, String v2)
  695. {
  696. return compareStrings(v1,v2,false);
  697. } //}}}
  698. //{{{ compareStrings() method
  699. /**
  700. * Compares two strings.<p>
  701. *
  702. * Unlike <function>String.compareTo()</function>,
  703. * this method correctly recognizes and handles embedded numbers.
  704. * For example, it places "My file 2" before "My file 10".<p>
  705. *
  706. * @param str1 The first string
  707. * @param str2 The second string
  708. * @param ignoreCase If true, case will be ignored
  709. * @return negative If str1 &lt; str2, 0 if both are the same,
  710. * positive if str1 &gt; str2
  711. * @since jEdit 4.0pre1
  712. */
  713. public static int compareStrings(String str1, String str2, boolean ignoreCase)
  714. {
  715. char[] char1 = str1.toCharArray();
  716. char[] char2 = str2.toCharArray();
  717. int len = Math.min(char1.length,char2.length);
  718. for(int i = 0, j = 0; i < len && j < len; i++, j++)
  719. {
  720. char ch1 = char1[i];
  721. char ch2 = char2[j];
  722. if(Character.isDigit(ch1) && Character.isDigit(ch2)
  723. && ch1 != '0' && ch2 != '0')
  724. {
  725. int _i = i + 1;
  726. int _j = j + 1;
  727. for(; _i < char1.length; _i++)
  728. {
  729. if(!Character.isDigit(char1[_i]))
  730. {
  731. //_i--;
  732. break;
  733. }
  734. }
  735. for(; _j < char2.length; _j++)
  736. {
  737. if(!Character.isDigit(char2[_j]))
  738. {
  739. //_j--;
  740. break;
  741. }
  742. }
  743. int len1 = _i - i;
  744. int len2 = _j - j;
  745. if(len1 > len2)
  746. return 1;
  747. else if(len1 < len2)
  748. return -1;
  749. else
  750. {
  751. for(int k = 0; k < len1; k++)
  752. {
  753. ch1 = char1[i + k];
  754. ch2 = char2[j + k];
  755. if(ch1 != ch2)
  756. return ch1 - ch2;
  757. }
  758. }
  759. i = _i - 1;
  760. j = _j - 1;
  761. }
  762. else
  763. {
  764. if(ignoreCase)
  765. {
  766. ch1 = Character.toLowerCase(ch1);
  767. ch2 = Character.toLowerCase(ch2);
  768. }
  769. if(ch1 != ch2)
  770. return ch1 - ch2;
  771. }
  772. }
  773. return char1.length - char2.length;
  774. } //}}}
  775. //{{{ stringsEqual() method
  776. /**
  777. * Returns if two strings are equal. This correctly handles null pointers,
  778. * as opposed to calling <code>s1.equals(s2)</code>.
  779. * @since jEdit 4.1pre5
  780. */
  781. public static boolean stringsEqual(String s1, String s2)
  782. {
  783. if(s1 == null)
  784. {
  785. if(s2 == null)
  786. return true;
  787. else
  788. return false;
  789. }
  790. else if(s2 == null)
  791. return false;
  792. else
  793. return s1.equals(s2);
  794. } //}}}
  795. //}}}
  796. //{{{ Sorting methods
  797. //{{{ quicksort() method
  798. /**
  799. * Sorts the specified array. Equivalent to calling
  800. * <code>Arrays.sort()</code>.
  801. * @param obj The array
  802. * @param compare Compares the objects
  803. * @since jEdit 4.0pre4
  804. */
  805. public static void quicksort(Object[] obj, Comparator compare)
  806. {
  807. Arrays.sort(obj,compare);
  808. } //}}}
  809. //{{{ quicksort() method
  810. /**
  811. * Sorts the specified vector.
  812. * @param vector The vector
  813. * @param compare Compares the objects
  814. * @since jEdit 4.0pre4
  815. */
  816. public static void quicksort(Vector vector, Comparator compare)
  817. {
  818. Collections.sort(vector,compare);
  819. } //}}}
  820. //{{{ quicksort() method
  821. /**
  822. * Sorts the specified list.
  823. * @param list The list
  824. * @param compare Compares the objects
  825. * @since jEdit 4.0pre4
  826. */
  827. public static void quicksort(List list, Comparator compare)
  828. {
  829. Collections.sort(list,compare);
  830. } //}}}
  831. //{{{ quicksort() method
  832. /**
  833. * Sorts the specified array. Equivalent to calling
  834. * <code>Arrays.sort()</code>.
  835. * @param obj The array
  836. * @param compare Compares the objects
  837. */
  838. public static void quicksort(Object[] obj, Compare compare)
  839. {
  840. Arrays.sort(obj,compare);
  841. } //}}}
  842. //{{{ quicksort() method
  843. /**
  844. * Sorts the specified vector.
  845. * @param vector The vector
  846. * @param compare Compares the objects
  847. */
  848. public static void quicksort(Vector vector, Compare compare)
  849. {
  850. Collections.sort(vector,compare);
  851. } //}}}
  852. //{{{ Compare interface
  853. /**
  854. * An interface for comparing objects. This is a hold-over from
  855. * they days when jEdit had its own sorting API due to JDK 1.1
  856. * compatibility requirements. Use <code>java.util.Comparable</code>
  857. * instead.
  858. */
  859. public interface Compare extends Comparator
  860. {
  861. int compare(Object obj1, Object obj2);
  862. } //}}}
  863. //{{{ StringCompare class
  864. /**
  865. * Compares strings.
  866. */
  867. public static class StringCompare implements Compare
  868. {
  869. public int compare(Object obj1, Object obj2)
  870. {
  871. return compareStrings(obj1.toString(),
  872. obj2.toString(),false);
  873. }
  874. } //}}}
  875. //{{{ StringICaseCompare class
  876. /**
  877. * Compares strings ignoring case.
  878. */
  879. public static class StringICaseCompare implements Compare
  880. {
  881. public int compare(Object obj1, Object obj2)
  882. {
  883. return compareStrings(obj1.toString(),
  884. obj2.toString(),true);
  885. }
  886. } //}}}
  887. //{{{ MenuItemCompare class
  888. /**
  889. * Compares menu item labels.
  890. */
  891. public static class MenuItemCompare implements Compare
  892. {
  893. public int compare(Object obj1, Object obj2)
  894. {
  895. return compareStrings(((JMenuItem)obj1).getText(),
  896. ((JMenuItem)obj2).getText(),true);
  897. }
  898. } //}}}
  899. //}}}
  900. //{{{ buildToVersion() method
  901. /**
  902. * Converts an internal version number (build) into a
  903. * `human-readable' form.
  904. * @param build The build
  905. */
  906. public static String buildToVersion(String build)
  907. {
  908. if(build.length() != 11)
  909. return "<unknown version: " + build + ">";
  910. // First 2 chars are the major version number
  911. int major = Integer.parseInt(build.substring(0,2));
  912. // Second 2 are the minor number
  913. int minor = Integer.parseInt(build.substring(3,5));
  914. // Then the pre-release status
  915. int beta = Integer.parseInt(build.substring(6,8));
  916. // Finally the bug fix release
  917. int bugfix = Integer.parseInt(build.substring(9,11));
  918. return "" + major + "." + minor
  919. + (beta != 99 ? "pre" + beta :
  920. (bugfix != 0 ? "." + bugfix : "final"));
  921. } //}}}
  922. //{{{ isToolsJarAvailable() method
  923. /**
  924. * If on JDK 1.2 or higher, make sure that tools.jar is available.
  925. * This method should be called by plugins requiring the classes
  926. * in this library.
  927. * <p>
  928. * tools.jar is searched for in the following places:
  929. * <ol>
  930. * <li>the classpath that was used when jEdit was started,
  931. * <li>jEdit's jars folder in the user's home,
  932. * <li>jEdit's system jars folder,
  933. * <li><i>java.home</i>/lib/. In this case, tools.jar is added to
  934. * jEdit's list of known jars using jEdit.addPluginJAR(),
  935. * so that it gets loaded through JARClassLoader.
  936. * </ol><p>
  937. *
  938. * On older JDK's this method does not perform any checks, and returns
  939. * <code>true</code> (even though there is no tools.jar).
  940. *
  941. * @return <code>false</code> if and only if on JDK 1.2 and tools.jar
  942. * could not be found. In this case it prints some warnings on Log,
  943. * too, about the places where it was searched for.
  944. * @since jEdit 3.2.2
  945. */
  946. public static boolean isToolsJarAvailable()
  947. {
  948. Log.log(Log.DEBUG, MiscUtilities.class,"Searching for tools.jar...");
  949. Vector paths = new Vector();
  950. //{{{ 1. Check whether tools.jar is in the system classpath:
  951. paths.addElement("System classpath: "
  952. + System.getProperty("java.class.path"));
  953. try
  954. {
  955. // Either class sun.tools.javac.Main or
  956. // com.sun.tools.javac.Main must be there:
  957. try
  958. {
  959. Class.forName("sun.tools.javac.Main");
  960. }
  961. catch(ClassNotFoundException e1)
  962. {
  963. Class.forName("com.sun.tools.javac.Main");
  964. }
  965. Log.log(Log.DEBUG, MiscUtilities.class,
  966. "- is in classpath. Fine.");
  967. return true;
  968. }
  969. catch(ClassNotFoundException e)
  970. {
  971. //Log.log(Log.DEBUG, MiscUtilities.class,
  972. // "- is not in system classpath.");
  973. } //}}}
  974. //{{{ 2. Check whether it is in the jEdit user settings jars folder:
  975. String settingsDir = jEdit.getSettingsDirectory();
  976. if(settingsDir != null)
  977. {
  978. String toolsPath = constructPath(settingsDir, "jars",
  979. "tools.jar");
  980. paths.addElement(toolsPath);
  981. if(new File(toolsPath).exists())
  982. {
  983. Log.log(Log.DEBUG, MiscUtilities.class,
  984. "- is in the user's jars folder. Fine.");
  985. // jEdit will load it automatically
  986. return true;
  987. }
  988. } //}}}
  989. //{{{ 3. Check whether it is in jEdit's system jars folder:
  990. String jEditDir = jEdit.getJEditHome();
  991. if(jEditDir != null)
  992. {
  993. String toolsPath = constructPath(jEditDir, "jars", "tools.jar");
  994. paths.addElement(toolsPath);
  995. if(new File(toolsPath).exists())
  996. {
  997. Log.log(Log.DEBUG, MiscUtilities.class,
  998. "- is in jEdit's system jars folder. Fine.");
  999. // jEdit will load it automatically
  1000. return true;
  1001. }
  1002. } //}}}
  1003. //{{{ 4. Check whether it is in <java.home>/lib:
  1004. String toolsPath = System.getProperty("java.home");
  1005. if(toolsPath.toLowerCase().endsWith(File.separator + "jre"))
  1006. toolsPath = toolsPath.substring(0, toolsPath.length() - 4);
  1007. toolsPath = constructPath(toolsPath, "lib", "tools.jar");
  1008. paths.addElement(toolsPath);
  1009. if(!(new File(toolsPath).exists()))
  1010. {
  1011. Log.log(Log.WARNING, MiscUtilities.class,
  1012. "Could not find tools.jar.\n"
  1013. + "I checked the following locations:\n"
  1014. + paths.toString());
  1015. return false;
  1016. } //}}}
  1017. //{{{ Load it, if not yet done:
  1018. EditPlugin.JAR jar = jEdit.getPluginJAR(toolsPath);
  1019. if(jar == null)
  1020. {
  1021. Log.log(Log.DEBUG, MiscUtilities.class,
  1022. "- adding " + toolsPath + " to jEdit plugins.");
  1023. try
  1024. {
  1025. jEdit.addPluginJAR(new EditPlugin.JAR(toolsPath,
  1026. new JARClassLoader(toolsPath)));
  1027. }
  1028. catch(IOException ioex)
  1029. {
  1030. Log.log(Log.ERROR, MiscUtilities.class,
  1031. "- I/O error loading " + toolsPath);
  1032. Log.log(Log.ERROR, MiscUtilities.class, ioex);
  1033. return false;
  1034. }
  1035. }
  1036. else
  1037. Log.log(Log.DEBUG, MiscUtilities.class,
  1038. "- has been loaded before.");
  1039. //}}}
  1040. return true;
  1041. } //}}}
  1042. //{{{ parsePermissions() method
  1043. /**
  1044. * Parse a Unix-style permission string (rwxrwxrwx).
  1045. * @param str The string (must be 9 characters long).
  1046. * @since jEdit 4.1pre8
  1047. */
  1048. public static int parsePermissions(String s)
  1049. {
  1050. int permissions = 0;
  1051. if(s.length() == 9)
  1052. {
  1053. if(s.charAt(0) == 'r')
  1054. permissions += 0400;
  1055. if(s.charAt(1) == 'w')
  1056. permissions += 0200;
  1057. if(s.charAt(2) == 'x')
  1058. permissions += 0100;
  1059. else if(s.charAt(2) == 's')
  1060. permissions += 04100;
  1061. else if(s.charAt(2) == 'S')
  1062. permissions += 04000;
  1063. if(s.charAt(3) == 'r')
  1064. permissions += 040;
  1065. if(s.charAt(4) == 'w')
  1066. permissions += 020;
  1067. if(s.charAt(5) == 'x')
  1068. permissions += 010;
  1069. else if(s.charAt(5) == 's')
  1070. permissions += 02010;
  1071. else if(s.charAt(5) == 'S')
  1072. permissions += 02000;
  1073. if(s.charAt(6) == 'r')
  1074. permissions += 04;
  1075. if(s.charAt(7) == 'w')
  1076. permissions += 02;
  1077. if(s.charAt(8) == 'x')
  1078. permissions += 01;
  1079. else if(s.charAt(8) == 't')
  1080. permissions += 01001;
  1081. else if(s.charAt(8) == 'T')
  1082. permissions += 01000;
  1083. }
  1084. return permissions;
  1085. } //}}}
  1086. //{{{ Private members
  1087. private MiscUtilities() {}
  1088. //{{{ resolveSymlinks() method
  1089. private static String resolveSymlinks(String path)
  1090. {
  1091. try
  1092. {
  1093. return new File(path).getCanonicalPath();
  1094. }
  1095. catch(IOException io)
  1096. {
  1097. return path;
  1098. }
  1099. } //}}}
  1100. //}}}
  1101. }