PageRenderTime 211ms CodeModel.GetById 101ms app.highlight 62ms RepoModel.GetById 38ms app.codeStats 0ms

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

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