PageRenderTime 50ms CodeModel.GetById 13ms app.highlight 28ms RepoModel.GetById 1ms app.codeStats 1ms

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

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