PageRenderTime 62ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/jEdit/tags/jedit-4-3-pre6/org/gjt/sp/jedit/MiscUtilities.java

#
Java | 1836 lines | 1016 code | 155 blank | 665 comment | 318 complexity | 5363354fc9fdc74be1cac588481b1a91 MD5 | raw file
Possible License(s): BSD-3-Clause, AGPL-1.0, Apache-2.0, LGPL-2.0, LGPL-3.0, GPL-2.0, CC-BY-SA-3.0, LGPL-2.1, GPL-3.0, MPL-2.0-no-copyleft-exception, IPL-1.0
  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, 2005 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.net.MalformedURLException;
  30. import java.net.URL;
  31. import java.nio.charset.Charset;
  32. import java.text.DecimalFormat;
  33. import java.util.*;
  34. import java.util.zip.GZIPInputStream;
  35. import org.xml.sax.InputSource;
  36. import org.xml.sax.helpers.DefaultHandler;
  37. import org.gjt.sp.jedit.io.*;
  38. import org.gjt.sp.util.Log;
  39. import org.gjt.sp.util.ProgressObserver;
  40. import org.gjt.sp.util.StandardUtilities;
  41. import org.gjt.sp.util.IOUtilities;
  42. import org.gjt.sp.util.XMLUtilities;
  43. import org.gjt.sp.jedit.menu.EnhancedMenuItem;
  44. import org.gjt.sp.jedit.buffer.BufferIORequest;
  45. import org.gjt.sp.jedit.buffer.JEditBuffer;
  46. //}}}
  47. /**
  48. * Path name manipulation, string manipulation, and more.<p>
  49. *
  50. * The most frequently used members of this class are:<p>
  51. *
  52. * <b>Some path name methods:</b><p>
  53. * <ul>
  54. * <li>{@link #getFileName(String)}</li>
  55. * <li>{@link #getParentOfPath(String)}</li>
  56. * <li>{@link #constructPath(String,String)}</li>
  57. * </ul>
  58. * <b>String comparison:</b><p>
  59. * A {@link #compareStrings(String,String,boolean)} method that unlike
  60. * <function>String.compareTo()</function>, correctly recognizes and handles
  61. * embedded numbers.<p>
  62. *
  63. * This class also defines several inner classes for use with the
  64. * sorting features of the Java collections API:
  65. *
  66. * <ul>
  67. * <li>{@link MiscUtilities.StringCompare}</li>
  68. * <li>{@link MiscUtilities.StringICaseCompare}</li>
  69. * <li>{@link MiscUtilities.MenuItemCompare}</li>
  70. * </ul>
  71. *
  72. * For example, you might call:<p>
  73. *
  74. * <code>Arrays.sort(myListOfStrings,
  75. * new MiscUtilities.StringICaseCompare());</code>
  76. *
  77. * @author Slava Pestov
  78. * @author John Gellene (API documentation)
  79. * @version $Id: MiscUtilities.java 5574 2006-07-13 06:49:00Z kpouer $
  80. */
  81. public class MiscUtilities
  82. {
  83. /**
  84. * This encoding is not supported by Java, yet it is useful.
  85. * A UTF-8 file that begins with 0xEFBBBF.
  86. */
  87. public static final String UTF_8_Y = "UTF-8Y";
  88. //{{{ Path name methods
  89. //{{{ canonPath() method
  90. /**
  91. * Returns the canonical form of the specified path name. Currently
  92. * only expands a leading <code>~</code>. <b>For local path names
  93. * only.</b>
  94. * @param path The path name
  95. * @since jEdit 4.0pre2
  96. */
  97. public static String canonPath(String path)
  98. {
  99. if(path.length() == 0)
  100. return path;
  101. if(path.startsWith("file://"))
  102. path = path.substring("file://".length());
  103. else if(path.startsWith("file:"))
  104. path = path.substring("file:".length());
  105. else if(isURL(path))
  106. return path;
  107. if(File.separatorChar == '\\')
  108. {
  109. // get rid of mixed paths on Windows
  110. path = path.replace('/','\\');
  111. // also get rid of trailing spaces on Windows
  112. int trim = path.length();
  113. while(path.charAt(trim - 1) == ' ')
  114. trim--;
  115. if (path.charAt(trim - 1) == '\\')
  116. while (trim > 1 && path.charAt(trim - 2) == '\\')
  117. {
  118. trim--;
  119. }
  120. path = path.substring(0,trim);
  121. }
  122. else if(OperatingSystem.isMacOS())
  123. {
  124. // do the same on OS X
  125. path = path.replace(':','/');
  126. }
  127. if(path.startsWith('~' + File.separator))
  128. {
  129. path = path.substring(2);
  130. String home = System.getProperty("user.home");
  131. if(home.endsWith(File.separator))
  132. return home + path;
  133. else
  134. return home + File.separator + path;
  135. }
  136. else if(path.equals("~"))
  137. return System.getProperty("user.home");
  138. else
  139. return path;
  140. } //}}}
  141. //{{{ resolveSymlinks() method
  142. /**
  143. * Resolves any symbolic links in the path name specified
  144. * using <code>File.getCanonicalPath()</code>. <b>For local path
  145. * names only.</b>
  146. * @since jEdit 4.2pre1
  147. */
  148. public static String resolveSymlinks(String path)
  149. {
  150. if(isURL(path))
  151. return path;
  152. // 2 aug 2003: OS/2 Java has a broken getCanonicalPath()
  153. if(OperatingSystem.isOS2())
  154. return path;
  155. // 18 nov 2003: calling this on a drive letter on Windows causes
  156. // drive access
  157. if(OperatingSystem.isDOSDerived())
  158. {
  159. if(path.length() == 2 || path.length() == 3)
  160. {
  161. if(path.charAt(1) == ':')
  162. return path;
  163. }
  164. }
  165. try
  166. {
  167. return new File(path).getCanonicalPath();
  168. }
  169. catch(IOException io)
  170. {
  171. return path;
  172. }
  173. } //}}}
  174. //{{{ isAbsolutePath() method
  175. /**
  176. * Returns if the specified path name is an absolute path or URL.
  177. * @since jEdit 4.1pre11
  178. */
  179. public static boolean isAbsolutePath(String path)
  180. {
  181. if(isURL(path))
  182. return true;
  183. else if(path.startsWith("~/") || path.startsWith("~" + File.separator) || path.equals("~"))
  184. return true;
  185. else if(OperatingSystem.isDOSDerived())
  186. {
  187. if(path.length() == 2 && path.charAt(1) == ':')
  188. return true;
  189. if(path.length() > 2 && path.charAt(1) == ':'
  190. && (path.charAt(2) == '\\'
  191. || path.charAt(2) == '/'))
  192. return true;
  193. if(path.startsWith("\\\\")
  194. || path.startsWith("//"))
  195. return true;
  196. }
  197. // not sure if this is correct for OpenVMS.
  198. else if(OperatingSystem.isUnix()
  199. || OperatingSystem.isVMS())
  200. {
  201. // nice and simple
  202. if(path.length() > 0 && path.charAt(0) == '/')
  203. return true;
  204. }
  205. return false;
  206. } //}}}
  207. //{{{ constructPath() method
  208. /**
  209. * Constructs an absolute path name from a directory and another
  210. * path name. This method is VFS-aware.
  211. * @param parent The directory
  212. * @param path The path name
  213. */
  214. public static String constructPath(String parent, String path)
  215. {
  216. if(isAbsolutePath(path))
  217. return canonPath(path);
  218. // have to handle this case specially on windows.
  219. // insert \ between, eg A: and myfile.txt.
  220. if(OperatingSystem.isDOSDerived())
  221. {
  222. if(path.length() == 2 && path.charAt(1) == ':')
  223. return path;
  224. else if(path.length() > 2 && path.charAt(1) == ':'
  225. && path.charAt(2) != '\\')
  226. {
  227. path = path.substring(0,2) + '\\'
  228. + path.substring(2);
  229. return canonPath(path);
  230. }
  231. }
  232. String dd = ".." + File.separator;
  233. String d = '.' + File.separator;
  234. if(parent == null)
  235. parent = System.getProperty("user.dir");
  236. for(;;)
  237. {
  238. if(path.equals("."))
  239. return parent;
  240. else if(path.equals(".."))
  241. return getParentOfPath(parent);
  242. else if(path.startsWith(dd) || path.startsWith("../"))
  243. {
  244. parent = getParentOfPath(parent);
  245. path = path.substring(3);
  246. }
  247. else if(path.startsWith(d) || path.startsWith("./"))
  248. path = path.substring(2);
  249. else
  250. break;
  251. }
  252. if(OperatingSystem.isDOSDerived()
  253. && !isURL(parent)
  254. && path.charAt(0) == '\\')
  255. parent = parent.substring(0,2);
  256. VFS vfs = VFSManager.getVFSForPath(parent);
  257. return canonPath(vfs.constructPath(parent,path));
  258. } //}}}
  259. //{{{ constructPath() method
  260. /**
  261. * Constructs an absolute path name from three path components.
  262. * This method is VFS-aware.
  263. * @param parent The parent directory
  264. * @param path1 The first path
  265. * @param path2 The second path
  266. */
  267. public static String constructPath(String parent,
  268. String path1, String path2)
  269. {
  270. return constructPath(constructPath(parent,path1),path2);
  271. } //}}}
  272. //{{{ concatPath() method
  273. /**
  274. * Like {@link #constructPath}, except <code>path</code> will be
  275. * appended to <code>parent</code> even if it is absolute.
  276. * <b>For local path names only.</b>.
  277. *
  278. * @param path
  279. * @param parent
  280. */
  281. public static String concatPath(String parent, String path)
  282. {
  283. parent = canonPath(parent);
  284. path = canonPath(path);
  285. // Make all child paths relative.
  286. if (path.startsWith(File.separator))
  287. path = path.substring(1);
  288. else if ((path.length() >= 3) && (path.charAt(1) == ':'))
  289. path = path.replace(':', File.separatorChar);
  290. if (parent == null)
  291. parent = System.getProperty("user.dir");
  292. if (parent.endsWith(File.separator))
  293. return parent + path;
  294. else
  295. return parent + File.separator + path;
  296. } //}}}
  297. //{{{ getFirstSeparatorIndex() method
  298. /**
  299. * Return the first index of either / or the OS-specific file
  300. * separator.
  301. * @param path The path
  302. * @since jEdit 4.3pre3
  303. */
  304. public static int getFirstSeparatorIndex(String path)
  305. {
  306. int start = getPathStart(path);
  307. int index = path.indexOf('/',start);
  308. if(index == -1)
  309. index = path.indexOf(File.separatorChar,start);
  310. return index;
  311. } //}}}
  312. //{{{ getLastSeparatorIndex() method
  313. /**
  314. * Return the last index of either / or the OS-specific file
  315. * separator.
  316. * @param path The path
  317. * @since jEdit 4.3pre3
  318. */
  319. public static int getLastSeparatorIndex(String path)
  320. {
  321. int start = getPathStart(path);
  322. if(start != 0)
  323. path = path.substring(start);
  324. int index = Math.max(path.lastIndexOf('/'),
  325. path.lastIndexOf(File.separatorChar));
  326. if(index == -1)
  327. return index;
  328. else
  329. return index + start;
  330. } //}}}
  331. //{{{ getFileExtension() method
  332. /**
  333. * Returns the extension of the specified filename, or an empty
  334. * string if there is none.
  335. * @param path The path
  336. */
  337. public static String getFileExtension(String path)
  338. {
  339. int fsIndex = getLastSeparatorIndex(path);
  340. int index = path.indexOf('.',fsIndex);
  341. if(index == -1)
  342. return "";
  343. else
  344. return path.substring(index);
  345. } //}}}
  346. //{{{ getFileName() method
  347. /**
  348. * Returns the last component of the specified path.
  349. * This method is VFS-aware.
  350. * @param path The path name
  351. */
  352. public static String getFileName(String path)
  353. {
  354. return VFSManager.getVFSForPath(path).getFileName(path);
  355. } //}}}
  356. //{{{ getFileNameNoExtension() method
  357. /**
  358. * Returns the last component of the specified path name without the
  359. * trailing extension (if there is one).
  360. * @param path The path name
  361. * @since jEdit 4.0pre8
  362. */
  363. public static String getFileNameNoExtension(String path)
  364. {
  365. String name = getFileName(path);
  366. int index = name.indexOf('.');
  367. if(index == -1)
  368. return name;
  369. else
  370. return name.substring(0,index);
  371. } //}}}
  372. //{{{ getFileParent() method
  373. /**
  374. * @deprecated Call getParentOfPath() instead
  375. */
  376. public static String getFileParent(String path)
  377. {
  378. return getParentOfPath(path);
  379. } //}}}
  380. //{{{ getParentOfPath() method
  381. /**
  382. * Returns the parent of the specified path. This method is VFS-aware.
  383. * @param path The path name
  384. * @since jEdit 2.6pre5
  385. */
  386. public static String getParentOfPath(String path)
  387. {
  388. return VFSManager.getVFSForPath(path).getParentOfPath(path);
  389. } //}}}
  390. //{{{ getFileProtocol() method
  391. /**
  392. * @deprecated Call getProtocolOfURL() instead
  393. */
  394. public static String getFileProtocol(String url)
  395. {
  396. return getProtocolOfURL(url);
  397. } //}}}
  398. //{{{ getProtocolOfURL() method
  399. /**
  400. * Returns the protocol specified by a URL.
  401. * @param url The URL
  402. * @since jEdit 2.6pre5
  403. */
  404. public static String getProtocolOfURL(String url)
  405. {
  406. return url.substring(0,url.indexOf(':'));
  407. } //}}}
  408. //{{{ isURL() method
  409. /**
  410. * Checks if the specified string is a URL.
  411. * @param str The string to check
  412. * @return True if the string is a URL, false otherwise
  413. */
  414. public static boolean isURL(String str)
  415. {
  416. int fsIndex = getLastSeparatorIndex(str);
  417. if(fsIndex == 0) // /etc/passwd
  418. return false;
  419. else if(fsIndex == 2) // C:\AUTOEXEC.BAT
  420. return false;
  421. int cIndex = str.indexOf(':');
  422. if(cIndex <= 1) // D:\WINDOWS, or doesn't contain : at all
  423. return false;
  424. String protocol = str.substring(0,cIndex);
  425. VFS vfs = VFSManager.getVFSForProtocol(protocol);
  426. if(vfs != null && !(vfs instanceof UrlVFS))
  427. return true;
  428. try
  429. {
  430. new URL(str);
  431. return true;
  432. }
  433. catch(MalformedURLException mf)
  434. {
  435. return false;
  436. }
  437. } //}}}
  438. //{{{ saveBackup() method
  439. /**
  440. * Saves a backup (optionally numbered) of a file.
  441. * @param file A local file
  442. * @param backups The number of backups. Must be >= 1. If > 1, backup
  443. * files will be numbered.
  444. * @param backupPrefix The backup file name prefix
  445. * @param backupSuffix The backup file name suffix
  446. * @param backupDirectory The directory where to save backups; if null,
  447. * they will be saved in the same directory as the file itself.
  448. * @since jEdit 4.0pre1
  449. */
  450. public static void saveBackup(File file, int backups,
  451. String backupPrefix, String backupSuffix,
  452. String backupDirectory)
  453. {
  454. saveBackup(file,backups,backupPrefix,backupSuffix,backupDirectory,0);
  455. } //}}}
  456. //{{{ saveBackup() method
  457. /**
  458. * Saves a backup (optionally numbered) of a file.
  459. * @param file A local file
  460. * @param backups The number of backups. Must be >= 1. If > 1, backup
  461. * files will be numbered.
  462. * @param backupPrefix The backup file name prefix
  463. * @param backupSuffix The backup file name suffix
  464. * @param backupDirectory The directory where to save backups; if null,
  465. * they will be saved in the same directory as the file itself.
  466. * @param backupTimeDistance The minimum time in minutes when a backup
  467. * version 1 shall be moved into version 2; if 0, backups are always
  468. * moved.
  469. * @since jEdit 4.2pre5
  470. */
  471. public static void saveBackup(File file, int backups,
  472. String backupPrefix, String backupSuffix,
  473. String backupDirectory, int backupTimeDistance)
  474. {
  475. if(backupPrefix == null)
  476. backupPrefix = "";
  477. if(backupSuffix == null)
  478. backupSuffix = "";
  479. String name = file.getName();
  480. // If backups is 1, create ~ file
  481. if(backups == 1)
  482. {
  483. File backupFile = new File(backupDirectory,
  484. backupPrefix + name + backupSuffix);
  485. long modTime = backupFile.lastModified();
  486. /* if backup file was created less than
  487. * 'backupTimeDistance' ago, we do not
  488. * create the backup */
  489. if(System.currentTimeMillis() - modTime
  490. >= backupTimeDistance)
  491. {
  492. backupFile.delete();
  493. if (!file.renameTo(backupFile))
  494. moveFile(file, backupFile);
  495. }
  496. }
  497. // If backups > 1, move old ~n~ files, create ~1~ file
  498. else
  499. {
  500. /* delete a backup created using above method */
  501. new File(backupDirectory,
  502. backupPrefix + name + backupSuffix
  503. + backups + backupSuffix).delete();
  504. File firstBackup = new File(backupDirectory,
  505. backupPrefix + name + backupSuffix
  506. + "1" + backupSuffix);
  507. long modTime = firstBackup.lastModified();
  508. /* if backup file was created less than
  509. * 'backupTimeDistance' ago, we do not
  510. * create the backup */
  511. if(System.currentTimeMillis() - modTime
  512. >= backupTimeDistance)
  513. {
  514. for(int i = backups - 1; i > 0; i--)
  515. {
  516. File backup = new File(backupDirectory,
  517. backupPrefix + name
  518. + backupSuffix + i
  519. + backupSuffix);
  520. backup.renameTo(
  521. new File(backupDirectory,
  522. backupPrefix + name
  523. + backupSuffix + (i+1)
  524. + backupSuffix));
  525. }
  526. File backupFile = new File(backupDirectory,
  527. backupPrefix + name + backupSuffix
  528. + "1" + backupSuffix);
  529. if (!file.renameTo(backupFile))
  530. moveFile(file, backupFile);
  531. }
  532. }
  533. } //}}}
  534. //{{{ moveFile() method
  535. /**
  536. * Moves the source file to the destination.
  537. *
  538. * If the destination cannot be created or is a read-only file, the
  539. * method returns <code>false</code>. Otherwise, the contents of the
  540. * source are copied to the destination, the source is deleted,
  541. * and <code>true</code> is returned.
  542. *
  543. * @param source The source file to move.
  544. * @param dest The destination where to move the file.
  545. * @return true on success, false otherwise.
  546. *
  547. * @since jEdit 4.3pre1
  548. */
  549. public static boolean moveFile(File source, File dest)
  550. {
  551. boolean ok = false;
  552. if ((dest.exists() && dest.canWrite())
  553. || (!dest.exists() && dest.getParentFile().canWrite()))
  554. {
  555. OutputStream fos = null;
  556. InputStream fis = null;
  557. try
  558. {
  559. fos = new FileOutputStream(dest);
  560. fis = new FileInputStream(source);
  561. ok = copyStream(32768,null,fis,fos,false);
  562. }
  563. catch (IOException ioe)
  564. {
  565. Log.log(Log.WARNING, MiscUtilities.class,
  566. "Error moving file: " + ioe + " : " + ioe.getMessage());
  567. }
  568. finally
  569. {
  570. try
  571. {
  572. if(fos != null)
  573. fos.close();
  574. if(fis != null)
  575. fis.close();
  576. }
  577. catch(Exception e)
  578. {
  579. Log.log(Log.ERROR,MiscUtilities.class,e);
  580. }
  581. }
  582. if(ok)
  583. source.delete();
  584. }
  585. return ok;
  586. } //}}}
  587. //{{{ copyStream() method
  588. /**
  589. * Copy an input stream to an output stream.
  590. *
  591. * @param bufferSize the size of the buffer
  592. * @param progress the progress observer it could be null
  593. * @param in the input stream
  594. * @param out the output stream
  595. * @param canStop if true, the copy can be stopped by interrupting the thread
  596. * @return <code>true</code> if the copy was done, <code>false</code> if it was interrupted
  597. * @throws IOException IOException If an I/O error occurs
  598. * @since jEdit 4.3pre3
  599. * @deprecated use {@link IOUtilities#copyStream(int, org.gjt.sp.util.ProgressObserver, java.io.InputStream, java.io.OutputStream, boolean)}
  600. */
  601. public static boolean copyStream(int bufferSize, ProgressObserver progress,
  602. InputStream in, OutputStream out, boolean canStop)
  603. throws IOException
  604. {
  605. return IOUtilities.copyStream(bufferSize, progress, in, out, canStop);
  606. } //}}}
  607. //{{{ copyStream() method
  608. /**
  609. * Copy an input stream to an output stream with a buffer of 4096 bytes.
  610. *
  611. * @param progress the progress observer it could be null
  612. * @param in the input stream
  613. * @param out the output stream
  614. * @param canStop if true, the copy can be stopped by interrupting the thread
  615. * @return <code>true</code> if the copy was done, <code>false</code> if it was interrupted
  616. * @throws IOException IOException If an I/O error occurs
  617. * @since jEdit 4.3pre3
  618. * @deprecated use {@link IOUtilities#copyStream(org.gjt.sp.util.ProgressObserver, java.io.InputStream, java.io.OutputStream, boolean)}
  619. */
  620. public static boolean copyStream(ProgressObserver progress,
  621. InputStream in, OutputStream out, boolean canStop)
  622. throws IOException
  623. {
  624. return copyStream(4096,progress, in, out, canStop);
  625. } //}}}
  626. //{{{ isBinaryFile() method
  627. /**
  628. * Check if a Reader is binary.
  629. * To check if a file is binary, we will check the first characters 100
  630. * (jEdit property vfs.binaryCheck.length)
  631. * If more than 1 (jEdit property vfs.binaryCheck.count), the
  632. * file is declared binary.
  633. * This is not 100% because sometimes the autodetection could fail.
  634. * This method will not close your reader. You have to do it yourself
  635. *
  636. * @param reader the reader
  637. * @return <code>true</code> if the Reader was detected as binary
  638. * @throws IOException IOException If an I/O error occurs
  639. * @since jEdit 4.3pre5
  640. */
  641. public static boolean isBinary(Reader reader)
  642. throws IOException
  643. {
  644. int nbChars = jEdit.getIntegerProperty("vfs.binaryCheck.length",100);
  645. int authorized = jEdit.getIntegerProperty("vfs.binaryCheck.count",1);
  646. for (long i = 0L;i < nbChars;i++)
  647. {
  648. int c = reader.read();
  649. if (c == -1)
  650. return false;
  651. if (c == 0)
  652. {
  653. authorized--;
  654. if (authorized == 0)
  655. return true;
  656. }
  657. }
  658. return false;
  659. } //}}}
  660. //{{{ isBackup() method
  661. /**
  662. * Check if the filename is a backup file.
  663. * @param filename the filename to check
  664. * @return true if this is a backup file.
  665. * @since jEdit 4.3pre5
  666. */
  667. public static boolean isBackup( String filename ) {
  668. if (filename.startsWith("#")) return true;
  669. if (filename.endsWith("~")) return true;
  670. if (filename.endsWith(".bak")) return true;
  671. return false;
  672. } //}}}
  673. //{{{ autodetect() method
  674. /**
  675. * Tries to detect if the stream is gzipped, and if it has an encoding
  676. * specified with an XML PI.
  677. *
  678. * @param in the input stream reader that must be autodetected
  679. * @param buffer a buffer. It can be null if you only want to autodetect the encoding of a file
  680. * @return a reader using the detected encoding
  681. * @throws IOException io exception during read
  682. * @since jEdit 4.3pre5
  683. */
  684. public static Reader autodetect(InputStream in, Buffer buffer) throws IOException
  685. {
  686. in = new BufferedInputStream(in);
  687. String encoding;
  688. if (buffer == null)
  689. encoding = System.getProperty("file.encoding");
  690. else
  691. encoding = buffer.getStringProperty(JEditBuffer.ENCODING);
  692. if(!in.markSupported())
  693. Log.log(Log.WARNING,MiscUtilities.class,"Mark not supported: " + in);
  694. else if(buffer == null || buffer.getBooleanProperty(Buffer.ENCODING_AUTODETECT))
  695. {
  696. in.mark(BufferIORequest.XML_PI_LENGTH);
  697. int b1 = in.read();
  698. int b2 = in.read();
  699. int b3 = in.read();
  700. if(b1 == BufferIORequest.GZIP_MAGIC_1 && b2 == BufferIORequest.GZIP_MAGIC_2)
  701. {
  702. in.reset();
  703. in = new GZIPInputStream(in);
  704. if (buffer != null)
  705. buffer.setBooleanProperty(Buffer.GZIPPED,true);
  706. // auto-detect encoding within the gzip stream.
  707. return autodetect(in, buffer);
  708. }
  709. else if (b1 == BufferIORequest.UNICODE_MAGIC_1
  710. && b2 == BufferIORequest.UNICODE_MAGIC_2)
  711. {
  712. in.reset();
  713. in.read();
  714. in.read();
  715. encoding = "UTF-16BE";
  716. if (buffer != null)
  717. buffer.setProperty(JEditBuffer.ENCODING,encoding);
  718. }
  719. else if (b1 == BufferIORequest.UNICODE_MAGIC_2
  720. && b2 == BufferIORequest.UNICODE_MAGIC_1)
  721. {
  722. in.reset();
  723. in.read();
  724. in.read();
  725. encoding = "UTF-16LE";
  726. if (buffer != null)
  727. buffer.setProperty(JEditBuffer.ENCODING,encoding);
  728. }
  729. else if(b1 == BufferIORequest.UTF8_MAGIC_1 && b2 == BufferIORequest.UTF8_MAGIC_2
  730. && b3 == BufferIORequest.UTF8_MAGIC_3)
  731. {
  732. // do not reset the stream and just treat it
  733. // like a normal UTF-8 file.
  734. if (buffer != null)
  735. buffer.setProperty(JEditBuffer.ENCODING, MiscUtilities.UTF_8_Y);
  736. encoding = "UTF-8";
  737. }
  738. else
  739. {
  740. in.reset();
  741. byte[] _xmlPI = new byte[BufferIORequest.XML_PI_LENGTH];
  742. int offset = 0;
  743. int count;
  744. while((count = in.read(_xmlPI,offset,
  745. BufferIORequest.XML_PI_LENGTH - offset)) != -1)
  746. {
  747. offset += count;
  748. if(offset == BufferIORequest.XML_PI_LENGTH)
  749. break;
  750. }
  751. String xmlEncoding = getXMLEncoding(new String(
  752. _xmlPI,0,offset,"ASCII"));
  753. if(xmlEncoding != null)
  754. {
  755. encoding = xmlEncoding;
  756. if (buffer != null)
  757. buffer.setProperty(JEditBuffer.ENCODING,encoding);
  758. }
  759. if(encoding.equals(MiscUtilities.UTF_8_Y))
  760. encoding = "UTF-8";
  761. in.reset();
  762. }
  763. }
  764. return new InputStreamReader(in,encoding);
  765. } //}}}
  766. //{{{ getXMLEncoding() method
  767. /**
  768. * Extract XML encoding name from PI.
  769. */
  770. private static String getXMLEncoding(String xmlPI)
  771. {
  772. if(!xmlPI.startsWith("<?xml"))
  773. return null;
  774. int index = xmlPI.indexOf("encoding=");
  775. if(index == -1 || index + 9 == xmlPI.length())
  776. return null;
  777. char ch = xmlPI.charAt(index + 9);
  778. int endIndex = xmlPI.indexOf(ch,index + 10);
  779. if(endIndex == -1)
  780. return null;
  781. String encoding = xmlPI.substring(index + 10,endIndex);
  782. if(Charset.isSupported(encoding))
  783. return encoding;
  784. else
  785. {
  786. Log.log(Log.WARNING,MiscUtilities.class,"XML PI specifies "
  787. + "unsupported encoding: " + encoding);
  788. return null;
  789. }
  790. } //}}}
  791. //{{{ closeQuietly() method
  792. /**
  793. * Method that will close an {@link InputStream} ignoring it if it is null and ignoring exceptions.
  794. *
  795. * @param in the InputStream to close.
  796. * @since jEdit 4.3pre3
  797. * @deprecated use {@link IOUtilities#closeQuietly(java.io.InputStream)}
  798. */
  799. public static void closeQuietly(InputStream in)
  800. {
  801. IOUtilities.closeQuietly(in);
  802. } //}}}
  803. //{{{ copyStream() method
  804. /**
  805. * Method that will close an {@link OutputStream} ignoring it if it is null and ignoring exceptions.
  806. *
  807. * @param out the OutputStream to close.
  808. * @since jEdit 4.3pre3
  809. * @deprecated use {@link IOUtilities#closeQuietly(java.io.OutputStream)}
  810. */
  811. public static void closeQuietly(OutputStream out)
  812. {
  813. IOUtilities.closeQuietly(out);
  814. } //}}}
  815. //{{{ fileToClass() method
  816. /**
  817. * Converts a file name to a class name. All slash characters are
  818. * replaced with periods and the trailing '.class' is removed.
  819. * @param name The file name
  820. */
  821. public static String fileToClass(String name)
  822. {
  823. char[] clsName = name.toCharArray();
  824. for(int i = clsName.length - 6; i >= 0; i--)
  825. if(clsName[i] == '/')
  826. clsName[i] = '.';
  827. return new String(clsName,0,clsName.length - 6);
  828. } //}}}
  829. //{{{ classToFile() method
  830. /**
  831. * Converts a class name to a file name. All periods are replaced
  832. * with slashes and the '.class' extension is added.
  833. * @param name The class name
  834. */
  835. public static String classToFile(String name)
  836. {
  837. return name.replace('.','/').concat(".class");
  838. } //}}}
  839. //{{{ pathsEqual() method
  840. /**
  841. * @param p1 A path name
  842. * @param p2 A path name
  843. * @return True if both paths are equal, ignoring trailing slashes, as
  844. * well as case insensitivity on Windows.
  845. * @since jEdit 4.3pre2
  846. */
  847. public static boolean pathsEqual(String p1, String p2)
  848. {
  849. VFS v1 = VFSManager.getVFSForPath(p1);
  850. VFS v2 = VFSManager.getVFSForPath(p2);
  851. if(v1 != v2)
  852. return false;
  853. if(p1.endsWith("/") || p1.endsWith(File.separator))
  854. p1 = p1.substring(0,p1.length() - 1);
  855. if(p2.endsWith("/") || p2.endsWith(File.separator))
  856. p2 = p2.substring(0,p2.length() - 1);
  857. if((v1.getCapabilities() & VFS.CASE_INSENSITIVE_CAP) != 0)
  858. return p1.equalsIgnoreCase(p2);
  859. else
  860. return p1.equals(p2);
  861. } //}}}
  862. //}}}
  863. //{{{ Text methods
  864. //{{{ getLeadingWhiteSpace() method
  865. /**
  866. * Returns the number of leading white space characters in the
  867. * specified string.
  868. * @param str The string
  869. * @deprecated use {@link StandardUtilities#getLeadingWhiteSpace(String)}
  870. */
  871. public static int getLeadingWhiteSpace(String str)
  872. {
  873. return StandardUtilities.getLeadingWhiteSpace(str);
  874. } //}}}
  875. //{{{ getTrailingWhiteSpace() method
  876. /**
  877. * Returns the number of trailing whitespace characters in the
  878. * specified string.
  879. * @param str The string
  880. * @since jEdit 2.5pre5
  881. * @deprecated use {@link StandardUtilities#getTrailingWhiteSpace(String)}
  882. */
  883. public static int getTrailingWhiteSpace(String str)
  884. {
  885. return StandardUtilities.getTrailingWhiteSpace(str);
  886. } //}}}
  887. //{{{ getLeadingWhiteSpaceWidth() method
  888. /**
  889. * Returns the width of the leading white space in the specified
  890. * string.
  891. * @param str The string
  892. * @param tabSize The tab size
  893. * @deprecated use {@link StandardUtilities#getLeadingWhiteSpace(String)}
  894. */
  895. public static int getLeadingWhiteSpaceWidth(String str, int tabSize)
  896. {
  897. return StandardUtilities.getLeadingWhiteSpaceWidth(str, tabSize);
  898. } //}}}
  899. //{{{ getVirtualWidth() method
  900. /**
  901. * Returns the virtual column number (taking tabs into account) of the
  902. * specified offset in the segment.
  903. *
  904. * @param seg The segment
  905. * @param tabSize The tab size
  906. * @since jEdit 4.1pre1
  907. * @deprecated use {@link StandardUtilities#getVirtualWidth(javax.swing.text.Segment, int)}
  908. */
  909. public static int getVirtualWidth(Segment seg, int tabSize)
  910. {
  911. return StandardUtilities.getVirtualWidth(seg, tabSize);
  912. } //}}}
  913. //{{{ getOffsetOfVirtualColumn() method
  914. /**
  915. * Returns the array offset of a virtual column number (taking tabs
  916. * into account) in the segment.
  917. *
  918. * @param seg The segment
  919. * @param tabSize The tab size
  920. * @param column The virtual column number
  921. * @param totalVirtualWidth If this array is non-null, the total
  922. * virtual width will be stored in its first location if this method
  923. * returns -1.
  924. *
  925. * @return -1 if the column is out of bounds
  926. *
  927. * @since jEdit 4.1pre1
  928. * @deprecated use {@link StandardUtilities#getVirtualWidth(javax.swing.text.Segment, int)}
  929. */
  930. public static int getOffsetOfVirtualColumn(Segment seg, int tabSize,
  931. int column, int[] totalVirtualWidth)
  932. {
  933. return StandardUtilities.getOffsetOfVirtualColumn(seg, tabSize, column, totalVirtualWidth);
  934. } //}}}
  935. //{{{ createWhiteSpace() method
  936. /**
  937. * Creates a string of white space with the specified length.<p>
  938. *
  939. * To get a whitespace string tuned to the current buffer's
  940. * settings, call this method as follows:
  941. *
  942. * <pre>myWhitespace = MiscUtilities.createWhiteSpace(myLength,
  943. * (buffer.getBooleanProperty("noTabs") ? 0
  944. * : buffer.getTabSize()));</pre>
  945. *
  946. * @param len The length
  947. * @param tabSize The tab size, or 0 if tabs are not to be used
  948. * @deprecated use {@link StandardUtilities#createWhiteSpace(int, int)}
  949. */
  950. public static String createWhiteSpace(int len, int tabSize)
  951. {
  952. return StandardUtilities.createWhiteSpace(len,tabSize,0);
  953. } //}}}
  954. //{{{ createWhiteSpace() method
  955. /**
  956. * Creates a string of white space with the specified length.<p>
  957. *
  958. * To get a whitespace string tuned to the current buffer's
  959. * settings, call this method as follows:
  960. *
  961. * <pre>myWhitespace = MiscUtilities.createWhiteSpace(myLength,
  962. * (buffer.getBooleanProperty("noTabs") ? 0
  963. * : buffer.getTabSize()));</pre>
  964. *
  965. * @param len The length
  966. * @param tabSize The tab size, or 0 if tabs are not to be used
  967. * @param start The start offset, for tab alignment
  968. * @since jEdit 4.2pre1
  969. * @deprecated use {@link StandardUtilities#createWhiteSpace(int, int, int)}
  970. */
  971. public static String createWhiteSpace(int len, int tabSize, int start)
  972. {
  973. return StandardUtilities.createWhiteSpace(len, tabSize, start);
  974. } //}}}
  975. //{{{ globToRE() method
  976. /**
  977. * Converts a Unix-style glob to a regular expression.<p>
  978. *
  979. * ? becomes ., * becomes .*, {aa,bb} becomes (aa|bb).
  980. * @param glob The glob pattern
  981. */
  982. public static String globToRE(String glob)
  983. {
  984. final Object NEG = new Object();
  985. final Object GROUP = new Object();
  986. Stack state = new Stack();
  987. StringBuffer buf = new StringBuffer();
  988. boolean backslash = false;
  989. for(int i = 0; i < glob.length(); i++)
  990. {
  991. char c = glob.charAt(i);
  992. if(backslash)
  993. {
  994. buf.append('\\');
  995. buf.append(c);
  996. backslash = false;
  997. continue;
  998. }
  999. switch(c)
  1000. {
  1001. case '\\':
  1002. backslash = true;
  1003. break;
  1004. case '?':
  1005. buf.append('.');
  1006. break;
  1007. case '.':
  1008. case '+':
  1009. case '(':
  1010. case ')':
  1011. buf.append('\\');
  1012. buf.append(c);
  1013. break;
  1014. case '*':
  1015. buf.append(".*");
  1016. break;
  1017. case '|':
  1018. if(backslash)
  1019. buf.append("\\|");
  1020. else
  1021. buf.append('|');
  1022. break;
  1023. case '{':
  1024. buf.append('(');
  1025. if(i + 1 != glob.length() && glob.charAt(i + 1) == '!')
  1026. {
  1027. buf.append('?');
  1028. state.push(NEG);
  1029. }
  1030. else
  1031. state.push(GROUP);
  1032. break;
  1033. case ',':
  1034. if(!state.isEmpty() && state.peek() == GROUP)
  1035. buf.append('|');
  1036. else
  1037. buf.append(',');
  1038. break;
  1039. case '}':
  1040. if(!state.isEmpty())
  1041. {
  1042. buf.append(")");
  1043. if(state.pop() == NEG)
  1044. buf.append(".*");
  1045. }
  1046. else
  1047. buf.append('}');
  1048. break;
  1049. default:
  1050. buf.append(c);
  1051. }
  1052. }
  1053. return buf.toString();
  1054. } //}}}
  1055. //{{{ escapesToChars() method
  1056. /**
  1057. * Converts "\n" and "\t" escapes in the specified string to
  1058. * newlines and tabs.
  1059. * @param str The string
  1060. * @since jEdit 2.3pre1
  1061. */
  1062. public static String escapesToChars(String str)
  1063. {
  1064. StringBuffer buf = new StringBuffer();
  1065. for(int i = 0; i < str.length(); i++)
  1066. {
  1067. char c = str.charAt(i);
  1068. switch(c)
  1069. {
  1070. case '\\':
  1071. if(i == str.length() - 1)
  1072. {
  1073. buf.append('\\');
  1074. break;
  1075. }
  1076. c = str.charAt(++i);
  1077. switch(c)
  1078. {
  1079. case 'n':
  1080. buf.append('\n');
  1081. break;
  1082. case 't':
  1083. buf.append('\t');
  1084. break;
  1085. default:
  1086. buf.append(c);
  1087. break;
  1088. }
  1089. break;
  1090. default:
  1091. buf.append(c);
  1092. }
  1093. }
  1094. return buf.toString();
  1095. } //}}}
  1096. //{{{ charsToEscapes() method
  1097. /**
  1098. * Escapes newlines, tabs, backslashes, and quotes in the specified
  1099. * string.
  1100. * @param str The string
  1101. * @since jEdit 2.3pre1
  1102. */
  1103. public static String charsToEscapes(String str)
  1104. {
  1105. return charsToEscapes(str,"\n\t\\\"'");
  1106. } //}}}
  1107. //{{{ charsToEscapes() method
  1108. /**
  1109. * Escapes the specified characters in the specified string.
  1110. * @param str The string
  1111. * @param toEscape Any characters that require escaping
  1112. * @since jEdit 4.1pre3
  1113. */
  1114. public static String charsToEscapes(String str, String toEscape)
  1115. {
  1116. StringBuffer buf = new StringBuffer();
  1117. for(int i = 0; i < str.length(); i++)
  1118. {
  1119. char c = str.charAt(i);
  1120. if(toEscape.indexOf(c) != -1)
  1121. {
  1122. if(c == '\n')
  1123. buf.append("\\n");
  1124. else if(c == '\t')
  1125. buf.append("\\t");
  1126. else
  1127. {
  1128. buf.append('\\');
  1129. buf.append(c);
  1130. }
  1131. }
  1132. else
  1133. buf.append(c);
  1134. }
  1135. return buf.toString();
  1136. } //}}}
  1137. //{{{ compareVersions() method
  1138. /**
  1139. * @deprecated Call <code>compareStrings()</code> instead
  1140. */
  1141. public static int compareVersions(String v1, String v2)
  1142. {
  1143. return StandardUtilities.compareStrings(v1,v2,false);
  1144. } //}}}
  1145. //{{{ compareStrings() method
  1146. /**
  1147. * Compares two strings.<p>
  1148. *
  1149. * Unlike <function>String.compareTo()</function>,
  1150. * this method correctly recognizes and handles embedded numbers.
  1151. * For example, it places "My file 2" before "My file 10".<p>
  1152. *
  1153. * @param str1 The first string
  1154. * @param str2 The second string
  1155. * @param ignoreCase If true, case will be ignored
  1156. * @return negative If str1 &lt; str2, 0 if both are the same,
  1157. * positive if str1 &gt; str2
  1158. * @since jEdit 4.0pre1
  1159. * @deprecated use {@link StandardUtilities#compareStrings(String, String, boolean)}
  1160. */
  1161. public static int compareStrings(String str1, String str2, boolean ignoreCase)
  1162. {
  1163. return StandardUtilities.compareStrings(str1, str2, ignoreCase);
  1164. } //}}}
  1165. //{{{ stringsEqual() method
  1166. /**
  1167. * @deprecated Call <code>objectsEqual()</code> instead.
  1168. */
  1169. public static boolean stringsEqual(String s1, String s2)
  1170. {
  1171. return StandardUtilities.objectsEqual(s1,s2);
  1172. } //}}}
  1173. //{{{ objectsEqual() method
  1174. /**
  1175. * Returns if two strings are equal. This correctly handles null pointers,
  1176. * as opposed to calling <code>o1.equals(o2)</code>.
  1177. * @since jEdit 4.2pre1
  1178. * @deprecated use {@link StandardUtilities#objectsEqual(Object, Object)}
  1179. */
  1180. public static boolean objectsEqual(Object o1, Object o2)
  1181. {
  1182. return StandardUtilities.objectsEqual(o1, o2);
  1183. } //}}}
  1184. //{{{ charsToEntities() method
  1185. /**
  1186. * Converts &lt;, &gt;, &amp; in the string to their HTML entity
  1187. * equivalents.
  1188. * @param str The string
  1189. * @since jEdit 4.2pre1
  1190. * @deprecated Use {@link XMLUtilities#charToEntiries(String,boolean)}.
  1191. */
  1192. public static String charsToEntities(String str)
  1193. {
  1194. return XMLUtilities.charsToEntities(str,false);
  1195. } //}}}
  1196. //{{{ formatFileSize() method
  1197. public static final DecimalFormat KB_FORMAT = new DecimalFormat("#.# KB");
  1198. public static final DecimalFormat MB_FORMAT = new DecimalFormat("#.# MB");
  1199. /**
  1200. * Formats the given file size into a nice string (123 bytes, 10.6 KB,
  1201. * 1.2 MB).
  1202. * @param length The size
  1203. * @since jEdit 4.2pre1
  1204. */
  1205. public static String formatFileSize(long length)
  1206. {
  1207. if(length < 1024)
  1208. return length + " bytes";
  1209. else if(length < 1024*1024)
  1210. return KB_FORMAT.format((double)length / 1024);
  1211. else
  1212. return MB_FORMAT.format((double)length / 1024 / 1024);
  1213. } //}}}
  1214. //{{{ getLongestPrefix() method
  1215. /**
  1216. * Returns the longest common prefix in the given set of strings.
  1217. * @param str The strings
  1218. * @param ignoreCase If true, case insensitive
  1219. * @since jEdit 4.2pre2
  1220. */
  1221. public static String getLongestPrefix(List str, boolean ignoreCase)
  1222. {
  1223. if(str.size() == 0)
  1224. return "";
  1225. int prefixLength = 0;
  1226. loop: for(;;)
  1227. {
  1228. String s = str.get(0).toString();
  1229. if(prefixLength >= s.length())
  1230. break loop;
  1231. char ch = s.charAt(prefixLength);
  1232. for(int i = 1; i < str.size(); i++)
  1233. {
  1234. s = str.get(i).toString();
  1235. if(prefixLength >= s.length())
  1236. break loop;
  1237. if(!compareChars(s.charAt(prefixLength),ch,ignoreCase))
  1238. break loop;
  1239. }
  1240. prefixLength++;
  1241. }
  1242. return str.get(0).toString().substring(0,prefixLength);
  1243. } //}}}
  1244. //{{{ getLongestPrefix() method
  1245. /**
  1246. * Returns the longest common prefix in the given set of strings.
  1247. * @param str The strings
  1248. * @param ignoreCase If true, case insensitive
  1249. * @since jEdit 4.2pre2
  1250. */
  1251. public static String getLongestPrefix(String[] str, boolean ignoreCase)
  1252. {
  1253. return getLongestPrefix((Object[])str,ignoreCase);
  1254. } //}}}
  1255. //{{{ getLongestPrefix() method
  1256. /**
  1257. * Returns the longest common prefix in the given set of strings.
  1258. * @param str The strings (calls <code>toString()</code> on each object)
  1259. * @param ignoreCase If true, case insensitive
  1260. * @since jEdit 4.2pre6
  1261. */
  1262. public static String getLongestPrefix(Object[] str, boolean ignoreCase)
  1263. {
  1264. if(str.length == 0)
  1265. return "";
  1266. int prefixLength = 0;
  1267. String first = str[0].toString();
  1268. loop: for(;;)
  1269. {
  1270. if(prefixLength >= first.length())
  1271. break loop;
  1272. char ch = first.charAt(prefixLength);
  1273. for(int i = 1; i < str.length; i++)
  1274. {
  1275. String s = str[i].toString();
  1276. if(prefixLength >= s.length())
  1277. break loop;
  1278. if(!compareChars(s.charAt(prefixLength),ch,ignoreCase))
  1279. break loop;
  1280. }
  1281. prefixLength++;
  1282. }
  1283. return first.substring(0,prefixLength);
  1284. } //}}}
  1285. //}}}
  1286. //{{{ Sorting methods
  1287. //{{{ quicksort() method
  1288. /**
  1289. * Sorts the specified array. Equivalent to calling
  1290. * <code>Arrays.sort()</code>.
  1291. * @param obj The array
  1292. * @param compare Compares the objects
  1293. * @since jEdit 4.0pre4
  1294. * @deprecated use <code>Arrays.sort()</code>
  1295. */
  1296. public static void quicksort(Object[] obj, Comparator compare)
  1297. {
  1298. Arrays.sort(obj,compare);
  1299. } //}}}
  1300. //{{{ quicksort() method
  1301. /**
  1302. * Sorts the specified vector.
  1303. * @param vector The vector
  1304. * @param compare Compares the objects
  1305. * @since jEdit 4.0pre4
  1306. * @deprecated <code>Collections.sort()</code>
  1307. */
  1308. public static void quicksort(Vector vector, Comparator compare)
  1309. {
  1310. Collections.sort(vector,compare);
  1311. } //}}}
  1312. //{{{ quicksort() method
  1313. /**
  1314. * Sorts the specified list.
  1315. * @param list The list
  1316. * @param compare Compares the objects
  1317. * @since jEdit 4.0pre4
  1318. * @deprecated <code>Collections.sort()</code>
  1319. */
  1320. public static void quicksort(List list, Comparator compare)
  1321. {
  1322. Collections.sort(list,compare);
  1323. } //}}}
  1324. //{{{ quicksort() method
  1325. /**
  1326. * Sorts the specified array. Equivalent to calling
  1327. * <code>Arrays.sort()</code>.
  1328. * @param obj The array
  1329. * @param compare Compares the objects
  1330. * @deprecated use <code>Arrays.sort()</code>
  1331. */
  1332. public static void quicksort(Object[] obj, Compare compare)
  1333. {
  1334. Arrays.sort(obj,compare);
  1335. } //}}}
  1336. //{{{ quicksort() method
  1337. /**
  1338. * Sorts the specified vector.
  1339. * @param vector The vector
  1340. * @param compare Compares the objects
  1341. * @deprecated <code>Collections.sort()</code>
  1342. */
  1343. public static void quicksort(Vector vector, Compare compare)
  1344. {
  1345. Collections.sort(vector,compare);
  1346. } //}}}
  1347. //{{{ Compare interface
  1348. /**
  1349. * An interface for comparing objects. This is a hold-over from
  1350. * they days when jEdit had its own sorting API due to JDK 1.1
  1351. * compatibility requirements. Use <code>java.util.Comparable</code>
  1352. * instead.
  1353. * @deprecated
  1354. */
  1355. public interface Compare extends Comparator
  1356. {
  1357. int compare(Object obj1, Object obj2);
  1358. } //}}}
  1359. //{{{ StringCompare class
  1360. /**
  1361. * Compares strings.
  1362. * @deprecated use {@link StandardUtilities.StringCompare}
  1363. */
  1364. public static class StringCompare implements Compare
  1365. {
  1366. public int compare(Object obj1, Object obj2)
  1367. {
  1368. return StandardUtilities.compareStrings(obj1.toString(),
  1369. obj2.toString(),false);
  1370. }
  1371. } //}}}
  1372. //{{{ StringICaseCompare class
  1373. /**
  1374. * Compares strings ignoring case.
  1375. */
  1376. public static class StringICaseCompare implements Compare
  1377. {
  1378. public int compare(Object obj1, Object obj2)
  1379. {
  1380. return StandardUtilities.compareStrings(obj1.toString(),
  1381. obj2.toString(),true);
  1382. }
  1383. } //}}}
  1384. //{{{ MenuItemCompare class
  1385. /**
  1386. * Compares menu item labels.
  1387. */
  1388. public static class MenuItemCompare implements Compare
  1389. {
  1390. public int compare(Object obj1, Object obj2)
  1391. {
  1392. boolean obj1E, obj2E;
  1393. obj1E = obj1 instanceof EnhancedMenuItem;
  1394. obj2E = obj2 instanceof EnhancedMenuItem;
  1395. if(obj1E && !obj2E)
  1396. return 1;
  1397. else if(obj2E && !obj1E)
  1398. return -1;
  1399. else
  1400. return StandardUtilities.compareStrings(((JMenuItem)obj1).getText(),
  1401. ((JMenuItem)obj2).getText(),true);
  1402. }
  1403. } //}}}
  1404. //}}}
  1405. //{{{ buildToVersion() method
  1406. /**
  1407. * Converts an internal version number (build) into a
  1408. * `human-readable' form.
  1409. * @param build The build
  1410. */
  1411. public static String buildToVersion(String build)
  1412. {
  1413. if(build.length() != 11)
  1414. return "<unknown version: " + build + ">";
  1415. // First 2 chars are the major version number
  1416. int major = Integer.parseInt(build.substring(0,2));
  1417. // Second 2 are the minor number
  1418. int minor = Integer.parseInt(build.substring(3,5));
  1419. // Then the pre-release status
  1420. int beta = Integer.parseInt(build.substring(6,8));
  1421. // Finally the bug fix release
  1422. int bugfix = Integer.parseInt(build.substring(9,11));
  1423. return major + "." + minor
  1424. + (beta != 99 ? "pre" + beta :
  1425. (bugfix != 0 ? "." + bugfix : "final"));
  1426. } //}}}
  1427. //{{{ isToolsJarAvailable() method
  1428. /**
  1429. * If on JDK 1.2 or higher, make sure that tools.jar is available.
  1430. * This method should be called by plugins requiring the classes
  1431. * in this library.
  1432. * <p>
  1433. * tools.jar is searched for in the following places:
  1434. * <ol>
  1435. * <li>the classpath that was used when jEdit was started,
  1436. * <li>jEdit's jars folder in the user's home,
  1437. * <li>jEdit's system jars folder,
  1438. * <li><i>java.home</i>/lib/. In this case, tools.jar is added to
  1439. * jEdit's list of known jars using jEdit.addPluginJAR(),
  1440. * so that it gets loaded through JARClassLoader.
  1441. * </ol><p>
  1442. *
  1443. * On older JDK's this method does not perform any checks, and returns
  1444. * <code>true</code> (even though there is no tools.jar).
  1445. *
  1446. * @return <code>false</code> if and only if on JDK 1.2 and tools.jar
  1447. * could not be found. In this case it prints some warnings on Log,
  1448. * too, about the places where it was searched for.
  1449. * @since jEdit 3.2.2
  1450. */
  1451. public static boolean isToolsJarAvailable()
  1452. {
  1453. Log.log(Log.DEBUG, MiscUtilities.class,"Searching for tools.jar...");
  1454. Vector paths = new Vector();
  1455. //{{{ 1. Check whether tools.jar is in the system classpath:
  1456. paths.addElement("System classpath: "
  1457. + System.getProperty("java.class.path"));
  1458. try
  1459. {
  1460. // Either class sun.tools.javac.Main or
  1461. // com.sun.tools.javac.Main must be there:
  1462. try
  1463. {
  1464. Class.forName("sun.tools.javac.Main");
  1465. }
  1466. catch(ClassNotFoundException e1)
  1467. {
  1468. Class.forName("com.sun.tools.javac.Main");
  1469. }
  1470. Log.log(Log.DEBUG, MiscUtilities.class,
  1471. "- is in classpath. Fine.");
  1472. return true;
  1473. }
  1474. catch(ClassNotFoundException e)
  1475. {
  1476. //Log.log(Log.DEBUG, MiscUtilities.class,
  1477. // "- is not in system classpath.");
  1478. } //}}}
  1479. //{{{ 2. Check whether it is in the jEdit user settings jars folder:
  1480. String settingsDir = jEdit.getSettingsDirectory();
  1481. if(settingsDir != null)
  1482. {
  1483. String toolsPath = constructPath(settingsDir, "jars",
  1484. "tools.jar");
  1485. paths.addElement(toolsPath);
  1486. if(new File(toolsPath).exists())
  1487. {
  1488. Log.log(Log.DEBUG, MiscUtilities.class,
  1489. "- is in the user's jars folder. Fine.");
  1490. // jEdit will load it automatically
  1491. return true;
  1492. }
  1493. } //}}}
  1494. //{{{ 3. Check whether it is in jEdit's system jars folder:
  1495. String jEditDir = jEdit.getJEditHome();
  1496. if(jEditDir != null)
  1497. {
  1498. String toolsPath = constructPath(jEditDir, "jars", "tools.jar");
  1499. paths.addElement(toolsPath);
  1500. if(new File(toolsPath).exists())
  1501. {
  1502. Log.log(Log.DEBUG, MiscUtilities.class,
  1503. "- is in jEdit's system jars folder. Fine.");
  1504. // jEdit will load it automatically
  1505. return true;
  1506. }
  1507. } //}}}
  1508. //{{{ 4. Check whether it is in <java.home>/lib:
  1509. String toolsPath = System.getProperty("java.home");
  1510. if(toolsPath.toLowerCase().endsWith(File.separator + "jre"))
  1511. toolsPath = toolsPath.substring(0, toolsPath.length() - 4);
  1512. toolsPath = constructPath(toolsPath, "lib", "tools.jar");
  1513. paths.addElement(toolsPath);
  1514. if(!(new File(toolsPath).exists()))
  1515. {
  1516. Log.log(Log.WARNING, MiscUtilities.class,
  1517. "Could not find tools.jar.\n"
  1518. + "I checked the following locations:\n"
  1519. + paths.toString());
  1520. return false;
  1521. } //}}}
  1522. //{{{ Load it, if not yet done:
  1523. PluginJAR jar = jEdit.getPluginJAR(toolsPath);
  1524. if(jar == null)
  1525. {
  1526. Log.log(Log.DEBUG, MiscUtilities.class,
  1527. "- adding " + toolsPath + " to jEdit plugins.");
  1528. jEdit.addPluginJAR(toolsPath);
  1529. }
  1530. else
  1531. Log.log(Log.DEBUG, MiscUtilities.class,
  1532. "- has been loaded before.");
  1533. //}}}
  1534. return true;
  1535. } //}}}
  1536. //{{{ parsePermissions() method
  1537. /**
  1538. * Parse a Unix-style permission string (rwxrwxrwx).
  1539. * @param s The string (must be 9 characters long).
  1540. * @since jEdit 4.1pre8
  1541. */
  1542. public static int parsePermissions(String s)
  1543. {
  1544. int permissions = 0;
  1545. if(s.length() == 9)
  1546. {
  1547. if(s.charAt(0) == 'r')
  1548. permissions += 0400;
  1549. if(s.charAt(1) == 'w')
  1550. permissions += 0200;
  1551. if(s.charAt(2) == 'x')
  1552. permissions += 0100;
  1553. else if(s.charAt(2) == 's')
  1554. permissions += 04100;
  1555. else if(s.charAt(2) == 'S')
  1556. permissions += 04000;
  1557. if(s.charAt(3) == 'r')
  1558. permissions += 040;
  1559. if(s.charAt(4) == 'w')
  1560. permissions += 020;
  1561. if(s.charAt(5) == 'x')
  1562. permissions += 010;
  1563. else if(s.charAt(5) == 's')
  1564. permissions += 02010;
  1565. else if(s.charAt(5) == 'S')
  1566. permissions += 02000;
  1567. if(s.charAt(6) == 'r')
  1568. permissions += 04;
  1569. if(s.charAt(7) == 'w')
  1570. permissions += 02;
  1571. if(s.charAt(8) == 'x')
  1572. permissions += 01;
  1573. else if(s.charAt(8) == 't')
  1574. permissions += 01001;
  1575. else if(s.charAt(8) == 'T')
  1576. permissions += 01000;
  1577. }
  1578. return permissions;
  1579. } //}}}
  1580. //{{{ getEncodings() method
  1581. /**
  1582. * Returns a list of supported character encodings.
  1583. * @since jEdit 4.2pre5
  1584. * @deprecated See #getEncodings( boolean )
  1585. */
  1586. public static String[] getEncodings()
  1587. {
  1588. return getEncodings(false);
  1589. } //}}}
  1590. //{{{ getEncodings() method
  1591. /**
  1592. * Returns a list of supported character encodings.
  1593. * @since jEdit 4.3pre5
  1594. * @param getSelected Whether to return just the selected encodings or all.
  1595. */
  1596. public static String[] getEncodings(boolean getSelected)
  1597. {
  1598. List returnValue = new ArrayList();
  1599. Map map = Charset.availableCharsets();
  1600. Iterator iter = map.keySet().iterator();
  1601. if ((getSelected && !jEdit.getBooleanProperty("encoding.opt-out."+UTF_8_Y,false)) ||
  1602. !getSelected)
  1603. {
  1604. returnValue.add(UTF_8_Y);
  1605. }
  1606. while(iter.hasNext())
  1607. {
  1608. String encoding = (String)iter.next();
  1609. if ((getSelected && !jEdit.getBooleanProperty("encoding.opt-out."+encoding,false)) ||
  1610. !getSelected)
  1611. {
  1612. returnValue.add(encoding);
  1613. }
  1614. }
  1615. return (String[])returnValue.toArray(
  1616. new String[returnValue.size()]);
  1617. } //}}}
  1618. //{{{ throwableToString() method
  1619. /**
  1620. * Returns a string containing the stack trace of the given throwable.
  1621. * @since jEdit 4.2pre6
  1622. */
  1623. public static String throwableToString(Throwable t)
  1624. {
  1625. StringWriter s = new StringWriter();
  1626. t.printStackTrace(new PrintWriter(s));
  1627. return s.toString();
  1628. } //}}}
  1629. //{{{ parseXML() method
  1630. /**
  1631. * Convenience method for parsing an XML file.
  1632. *
  1633. * @return Whether any error occured during parsing.
  1634. * @since jEdit 4.3pre5
  1635. * @deprecated Use {@link XMLUtilities#parseXML(InputStream,DefaultHandler)}.
  1636. */
  1637. public static boolean parseXML(InputStream in, DefaultHandler handler)
  1638. throws IOException
  1639. {
  1640. return XMLUtilities.parseXML(in, handler);
  1641. } //}}}
  1642. //{{{ resolveEntity() method
  1643. /**
  1644. * Tries to find the given systemId in the context of the given
  1645. * class.
  1646. *
  1647. * @deprecated Use {@link XMLUtilities#findEntity(String,String,Class)}.
  1648. */
  1649. public static InputSource findEntity(String systemId, String test, Class where)
  1650. {
  1651. return XMLUtilities.findEntity(systemId, test, where);
  1652. } //}}}
  1653. //{{{ Private members
  1654. private MiscUtilities() {}
  1655. //{{{ compareChars()
  1656. /** should this be public? */
  1657. private static boolean compareChars(char ch1, char ch2, boolean ignoreCase)
  1658. {
  1659. if(ignoreCase)
  1660. return Character.toUpperCase(ch1) == Character.toUpperCase(ch2);
  1661. else
  1662. return ch1 == ch2;
  1663. } //}}}
  1664. //{{{ getPathStart()
  1665. private static int getPathStart(String path)
  1666. {
  1667. int start = 0;
  1668. if(path.startsWith("/"))
  1669. return 1;
  1670. else if(OperatingSystem.isDOSDerived()
  1671. && path.length() >= 3
  1672. && path.charAt(1) == ':'
  1673. && (path.charAt(2) == '/'
  1674. || path.charAt(2) == '\\'))
  1675. return 3;
  1676. else
  1677. return 0;
  1678. } //}}}
  1679. //}}}
  1680. }