PageRenderTime 478ms CodeModel.GetById 431ms app.highlight 40ms RepoModel.GetById 1ms app.codeStats 0ms

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

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