/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is NetBeans. The Initial Developer of the Original * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun * Microsystems, Inc. All Rights Reserved. */ package org.openide.util; import java.awt.*; import java.awt.event.KeyEvent; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; import java.util.*; import java.util.List; import java.lang.reflect.*; import java.lang.ref.Reference; import java.lang.ref.SoftReference; import java.io.InputStreamReader; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.lang.ref.ReferenceQueue; import java.net.MalformedURLException; import java.text.BreakIterator; import java.net.URL; import javax.swing.Action; import javax.swing.KeyStroke; import javax.swing.Timer; import org.openide.ErrorManager; import org.openide.modules.Dependency; import org.openide.modules.SpecificationVersion; import org.openide.util.ContextAwareAction; /** Otherwise uncategorized useful static methods. * * @author Jan Palka, Ian Formanek, Jaroslav Tulach */ public final class Utilities { private Utilities() {} /** Operating system is Windows NT. */ public static final int OS_WINNT = 1; /** Operating system is Windows 95. */ public static final int OS_WIN95 = 2; /** Operating system is Windows 98. */ public static final int OS_WIN98 = 4; /** Operating system is Solaris. */ public static final int OS_SOLARIS = 8; /** Operating system is Linux. */ public static final int OS_LINUX = 16; /** Operating system is HP-UX. */ public static final int OS_HP = 32; /** Operating system is IBM AIX. */ public static final int OS_AIX = 64; /** Operating system is SGI IRIX. */ public static final int OS_IRIX = 128; /** Operating system is Sun OS. */ public static final int OS_SUNOS = 256; /** Operating system is Compaq TRU64 Unix */ public static final int OS_TRU64 = 512; /** @deprecated please use OS_TRU64 instead */ public static final int OS_DEC = OS_TRU64; /** Operating system is OS/2. */ public static final int OS_OS2 = 1024; /** Operating system is Mac. */ public static final int OS_MAC = 2048; /** Operating system is Windows 2000. */ public static final int OS_WIN2000 = 4096; /** Operating system is Compaq OpenVMS */ public static final int OS_VMS = 8192; /** *Operating system is one of the Windows variants but we don't know which *one it is */ public static final int OS_WIN_OTHER = 16384; /** Operating system is unknown. */ public static final int OS_OTHER = 65536; /** A mask for Windows platforms. */ public static final int OS_WINDOWS_MASK = OS_WINNT | OS_WIN95 | OS_WIN98 | OS_WIN2000 | OS_WIN_OTHER; /** A mask for Unix platforms. */ public static final int OS_UNIX_MASK = OS_SOLARIS | OS_LINUX | OS_HP | OS_AIX | OS_IRIX | OS_SUNOS | OS_TRU64 | OS_MAC; /** A height of the windows's taskbar */ public static final int TYPICAL_WINDOWS_TASKBAR_HEIGHT = 27; /** A height of the Mac OS X's menu */ private static final int TYPICAL_MACOSX_MENU_HEIGHT = 24; /** variable holding the activeReferenceQueue */ private static ReferenceQueue activeReferenceQueue; /** Useful queue for all parts of system that use <code>java.lang.ref.Reference</code>s * together with some <code>ReferenceQueue</code> and need to do some clean up * when the reference is enqued. Usually, in order to be notified about that, one * needs to either create a dedicated thread that blocks on the queue and is * <code>Object.notify</code>-ed, which is the right approach but consumes * valuable system resources (threads) or one can peridically check the content * of the queue by <code>RequestProcessor.Task.schedule</code> which is * completelly wrong, because it wakes up the system every (say) 15 seconds. * In order to provide useful support for this problem, this queue has been * provided. * <P> * If you have a reference that needs clean up, make it implement <link>Runnable</link> * inteface and register it with the <code>activeReferenceQueue</code>: * <PRE> * class MyReference extends WeakReference implements Runnable { * private Object dataToCleanUp; * * public MyReference (Object ref, Object data) { * super (ref, Utilities.activeReferenceQueue ()); // here you specify the queue * dataToCleanUp = data; * } * * public void run () { * // clean up your data * } * } * </PRE> * When the <code>ref</code> object is garbage collected, your run method * will be invoked by calling * <code>((Runnable)reference).run ()</code> * and you can perform what ever cleanup is necessary. Be sure not to block * in such cleanup for a long time as this prevents other waiting references * to cleanup themselves. * <P> * Please do not call any methods of the ReferenceQueue yourself. They * will throw exceptions. * * @since 3.11 */ public static synchronized ReferenceQueue activeReferenceQueue () { if (activeReferenceQueue == null) { activeReferenceQueue = new ActiveQueue (false); } return activeReferenceQueue; } /** reference to map that maps allowed key names to their values (String, Integer) and reference to map for mapping of values to their names */ private static Reference namesAndValues; /** Get the operating system on which the IDE is running. * @return one of the <code>OS_*</code> constants (such as {@link #OS_WINNT}) */ public static final int getOperatingSystem () { if (operatingSystem == -1) { String osName = System.getProperty ("os.name"); if ("Windows NT".equals (osName)) // NOI18N operatingSystem = OS_WINNT; else if ("Windows 95".equals (osName)) // NOI18N operatingSystem = OS_WIN95; else if ("Windows 98".equals (osName)) // NOI18N operatingSystem = OS_WIN98; else if ("Windows 2000".equals (osName)) // NOI18N operatingSystem = OS_WIN2000; else if (osName.startsWith("Windows ")) // NOI18N operatingSystem = OS_WIN_OTHER; else if ("Solaris".equals (osName)) // NOI18N operatingSystem = OS_SOLARIS; else if (osName.startsWith ("SunOS")) // NOI18N operatingSystem = OS_SOLARIS; // JDK 1.4 b2 defines os.name for me as "Redhat Linux" -jglick else if (osName.endsWith ("Linux")) // NOI18N operatingSystem = OS_LINUX; else if ("HP-UX".equals (osName)) // NOI18N operatingSystem = OS_HP; else if ("AIX".equals (osName)) // NOI18N operatingSystem = OS_AIX; else if ("Irix".equals (osName)) // NOI18N operatingSystem = OS_IRIX; else if ("SunOS".equals (osName)) // NOI18N operatingSystem = OS_SUNOS; else if ("Digital UNIX".equals (osName)) // NOI18N operatingSystem = OS_TRU64; else if ("OS/2".equals (osName)) // NOI18N operatingSystem = OS_OS2; else if ("OpenVMS".equals (osName)) // NOI18N operatingSystem = OS_VMS; else if (osName.equals ("Mac OS X")) // NOI18N operatingSystem = OS_MAC; else if (osName.startsWith ("Darwin")) // NOI18N operatingSystem = OS_MAC; else operatingSystem = OS_OTHER; } return operatingSystem; } /** Test whether the IDE is running on some variant of Windows. * @return <code>true</code> if Windows, <code>false</code> if some other manner of operating system */ public static final boolean isWindows () { return (getOperatingSystem () & OS_WINDOWS_MASK) != 0; } /** Test whether the IDE is running on some variant of Unix. * Linux is included as well as the commercial vendors. * @return <code>true</code> some sort of Unix, <code>false</code> if some other manner of operating system */ public static final boolean isUnix () { return (getOperatingSystem () & OS_UNIX_MASK) != 0; } /** The operating system on which NetBeans runs*/ private static int operatingSystem = -1; /** Hashtable contains keywords. It is forbidden to use this keywords as a java identifier */ private static Reference keywords; private static synchronized HashMap keywords () { if (keywords != null) { HashMap map = (HashMap)keywords.get (); if (map != null) { return map; } } HashMap keywords = new HashMap (71); keywords.put("abstract","abstract"); keywords.put("default","default"); // NOI18N keywords.put("if","if"); keywords.put("private","private"); // NOI18N keywords.put("throw","throw"); keywords.put("boolean","boolean"); // NOI18N keywords.put("do","do"); keywords.put("implements","implements"); // NOI18N keywords.put("protected","protected"); keywords.put("throws","throws"); // NOI18N keywords.put("break","break"); keywords.put("double","double"); // NOI18N keywords.put("import","import"); keywords.put("public","public"); // NOI18N keywords.put("transient","transient");keywords.put("byte","byte"); // NOI18N keywords.put("else","else");keywords.put("instanceof","instanceof"); // NOI18N keywords.put("return","return");keywords.put("try","try"); // NOI18N keywords.put("case","case");keywords.put("extends","extends"); // NOI18N keywords.put("int","int");keywords.put("short","short"); // NOI18N keywords.put("void","void");keywords.put("catch","catch"); // NOI18N keywords.put("final","final");keywords.put("interface","interface"); // NOI18N keywords.put("static","static");keywords.put("volatile","volatile"); // NOI18N keywords.put("char","char");keywords.put("finally","finally"); // NOI18N keywords.put("long","long");keywords.put("class","class"); // NOI18N keywords.put("while","while");keywords.put("super","super"); // NOI18N keywords.put("float","float");keywords.put("native","native"); // NOI18N keywords.put("switch","switch");keywords.put("const","const"); // NOI18N keywords.put("for","for");keywords.put("new","new"); // NOI18N keywords.put("synchronized","synchronized");keywords.put("continue","continue"); // NOI18N keywords.put("continue","continue");keywords.put("goto","goto"); // NOI18N keywords.put("package","package");keywords.put("this","this"); // NOI18N keywords.put("null","null");keywords.put("true","true"); // NOI18N keywords.put("false","false"); // NOI18N keywords.put("assert", "assert"); //JDK 1.4 NOI18N Utilities.keywords = new SoftReference (keywords); return keywords; } /** Test whether a given string is a valid Java identifier. * @param id string which should be checked * @return <code>true</code> if a valid identifier */ public static final boolean isJavaIdentifier(String id) { if (id == null) return false; if (id.equals("")) return false; // NOI18N if (!(java.lang.Character.isJavaIdentifierStart(id.charAt(0))) ) return false; for (int i = 1; i < id.length(); i++) { if (!(java.lang.Character.isJavaIdentifierPart(id.charAt(i))) ) return false; } // test if id is a keyword if (keywords ().containsKey(id)) return false; return true; } /** Central method for obtaining <code>BeanInfo</code> for potential JavaBean classes. * @param clazz class of the bean to provide the <code>BeanInfo</code> for * @return the bean info * @throws java.beans.IntrospectionException for the usual reasons * @see java.beans.Introspector#getBeanInfo(Class) */ public static java.beans.BeanInfo getBeanInfo(Class clazz) throws java.beans.IntrospectionException { java.beans.BeanInfo bi; try { bi = java.beans.Introspector.getBeanInfo(clazz); } catch (java.beans.IntrospectionException ie) { ErrorManager.getDefault().annotate(ie, ErrorManager.UNKNOWN, "Encountered while introspecting " + clazz.getName(), null, null, null); // NOI18N throw ie; } catch (Error e) { // Could be a bug in Introspector triggered by NB code. ErrorManager.getDefault().annotate(e, ErrorManager.UNKNOWN, "Encountered while introspecting " + clazz.getName(), null, null, null); // NOI18N throw e; } if (java.awt.Component.class.isAssignableFrom (clazz)) { java.beans.PropertyDescriptor[] pds = bi.getPropertyDescriptors (); for (int i = 0; i < pds.length; i++) { if (pds[i].getName ().equals ("cursor")) { // NOI18N try { Method getter = Component.class.getDeclaredMethod ("getCursor", new Class[0]); // NOI18N Method setter = Component.class.getDeclaredMethod ("setCursor", new Class[] { Cursor.class }); // NOI18N pds[i] = new java.beans.PropertyDescriptor ("cursor", getter, setter); // NOI18N } catch (NoSuchMethodException e) { e.printStackTrace(); } break; } } } // clears about 1000 instances of Method if (bi != null) { if (clearIntrospector == null) { doClear = new ActionListener() { public void actionPerformed(ActionEvent ev) { java.beans.Introspector.flushCaches(); } }; clearIntrospector = new Timer(15000, doClear); clearIntrospector.setRepeats(false); } clearIntrospector.restart(); } return bi; } private static Timer clearIntrospector; private static ActionListener doClear; /** Central method for obtaining <code>BeanInfo</code> for potential JavaBean classes, with a stop class. * @param clazz class of the bean to provide the <code>BeanInfo</code> for * @param stopClass the stop class * @return the bean info * @throws java.beans.IntrospectionException for the usual reasons * @see java.beans.Introspector#getBeanInfo(Class, Class) */ public static java.beans.BeanInfo getBeanInfo (Class clazz, Class stopClass) throws java.beans.IntrospectionException { return java.beans.Introspector.getBeanInfo(clazz, stopClass); } /** Wrap multi-line strings (and get the individual lines). * @param original the original string to wrap * @param width the maximum width of lines * @param wrapWords if <code>true</code>, the lines are wrapped on word boundaries (if possible); * if <code>false</code>, character boundaries are used * @param removeNewLines if <code>true</code>, any newlines in the original string are ignored * @return the lines after wrapping * @deprecated use {@link #wrapStringToArray(String, int, BreakIterator, boolean)} since it is better for I18N */ public static String[] wrapStringToArray(String original, int width, boolean wrapWords, boolean removeNewLines) { BreakIterator bi = (wrapWords ? BreakIterator.getWordInstance() : BreakIterator.getCharacterInstance()); return wrapStringToArray(original, width, bi, removeNewLines); } /** Wrap multi-line strings (and get the individual lines). * @param original the original string to wrap * @param width the maximum width of lines * @param breakIterator breaks original to chars, words, sentences, depending on what instance you provide. * @param removeNewLines if <code>true</code>, any newlines in the original string are ignored * @return the lines after wrapping */ public static String[] wrapStringToArray(String original, int width, BreakIterator breakIterator, boolean removeNewLines) { if (original.length() == 0) { return new String[] { original }; } String[] workingSet; // substitute original newlines with spaces, // remove newlines from head and tail if (removeNewLines) { original = trimString(original); original = original.replace ('\n', ' '); workingSet = new String[] { original }; } else { StringTokenizer tokens = new StringTokenizer(original, "\n"); // NOI18N int len = tokens.countTokens(); workingSet = new String[len]; for (int i = 0; i < len; i++) { workingSet[i] = tokens.nextToken(); } } if (width < 1) width = 1; if (original.length () <= width) { return workingSet; } widthcheck: { boolean ok = true; for (int i = 0; i < workingSet.length; i++) { ok = ok && (workingSet[i].length() < width); if (!ok) { break widthcheck; } } return workingSet; } java.util.ArrayList lines = new java.util.ArrayList(); int lineStart = 0; // the position of start of currently processed line in the original string for (int i = 0; i < workingSet.length; i++) { if (workingSet[i].length() < width) { lines.add(workingSet[i]); } else { breakIterator.setText(workingSet[i]); int nextStart = breakIterator.next(); int prevStart = 0; do { while (((nextStart - lineStart) < width) && (nextStart != BreakIterator.DONE)) { prevStart = nextStart; nextStart = breakIterator.next(); } if (nextStart == BreakIterator.DONE) { nextStart = prevStart = workingSet[i].length(); } if (prevStart == 0) { prevStart = nextStart; } lines.add(workingSet[i].substring(lineStart, prevStart)); lineStart = prevStart; prevStart = 0; } while (lineStart < workingSet[i].length()); lineStart = 0; } } String s[] = new String [lines.size()]; return (String[]) lines.toArray(s); } /** trims String * @param s a String to trim * @return trimmed String */ private static String trimString(String s) { int idx = 0; char c; final int slen = s.length(); if (slen == 0) { return s; } do { c = s.charAt(idx++); } while ((c == '\n' || c == '\r') && (idx < slen)); s = s.substring(--idx); idx = s.length() - 1; if (idx < 0) { return s; } do { c = s.charAt(idx--); } while ((c == '\n' || c == '\r') && (idx >= 0)); return s.substring(0, idx + 2); } /** Wrap multi-line strings. * @param original the original string to wrap * @param width the maximum width of lines * @param breakIterator algorithm for breaking lines * @param removeNewLines if <code>true</code>, any newlines in the original string are ignored * @return the whole string with embedded newlines */ public static String wrapString (String original, int width, BreakIterator breakIterator, boolean removeNewLines) { String[] sarray = wrapStringToArray(original, width, breakIterator, removeNewLines); StringBuffer retBuf = new StringBuffer (); for (int i = 0; i < sarray.length; i++) { retBuf.append (sarray[i]); retBuf.append ('\n'); } return retBuf.toString (); } /** Wrap multi-line strings. * @param original the original string to wrap * @param width the maximum width of lines * @param wrapWords if <code>true</code>, the lines are wrapped on word boundaries (if possible); * if <code>false</code>, character boundaries are used * @param removeNewLines if <code>true</code>, any newlines in the original string are ignored * @return the whole string with embedded newlines * @deprecated Use {@link #wrapString (String, int, BreakIterator, boolean)} as it is friendlier to I18N. */ public static String wrapString (String original, int width, boolean wrapWords, boolean removeNewLines) { // substitute original newlines with spaces, // remove newlines from head and tail if (removeNewLines) { while (original.startsWith ("\n")) // NOI18N original = original.substring (1); while (original.endsWith ("\n")) // NOI18N original = original.substring (0, original.length () - 1); original = original.replace ('\n', ' '); } if (width < 1) width = 1; if (original.length () <= width) return original; java.util.Vector lines = new java.util.Vector (); int lineStart = 0; // the position of start of currently processed line in the original string int lastSpacePos = -1; for (int i = 0; i < original.length (); i++) { if (lineStart >= original.length () - 1) break; // newline in the original string if (original.charAt (i) == '\n') { lines.addElement (original.substring (lineStart, i)); lineStart = i+1; lastSpacePos = -1; continue; } // remember last space position if (Character.isSpaceChar (original.charAt (i))) lastSpacePos = i; // last position in the original string if (i == original.length () - 1) { lines.addElement (original.substring (lineStart)); break; } // reached width if (i - lineStart == width) { if (wrapWords && (lastSpacePos != -1)) { lines.addElement (original.substring (lineStart, lastSpacePos)); lineStart = lastSpacePos + 1; // the space is consumed for the newline lastSpacePos = -1; } else { lines.addElement (original.substring (lineStart, i)); lineStart = i; lastSpacePos = -1; } } } StringBuffer retBuf = new StringBuffer (); for (java.util.Enumeration e = lines.elements (); e.hasMoreElements ();) { retBuf.append ((String) e.nextElement ()); retBuf.append ('\n'); } return retBuf.toString (); } /** Search-and-replace fixed string matches within a string. * @param original the original string * @param replaceFrom the substring to be find * @param replaceTo the substring to replace it with * @return a new string with all occurrences replaced */ public static String replaceString (String original, String replaceFrom, String replaceTo) { int index = 0; if ("".equals (replaceFrom)) return original; // NOI18N StringBuffer buf = new StringBuffer (); while (true) { int pos = original.indexOf (replaceFrom, index); if (pos == -1) { buf.append (original.substring (index)); return buf.toString (); } buf.append (original.substring (index, pos)); buf.append (replaceTo); index = pos + replaceFrom.length (); if (index == original.length ()) return buf.toString (); } } /** Turn full name of an inner class into its pure form. * @param fullName e.g. <code>some.pkg.SomeClass$Inner</code> * @return e.g. <code>Inner</code> */ public static final String pureClassName (final String fullName) { final int index = fullName.indexOf('$'); if ((index >= 0) && (index < fullName.length())) return fullName.substring(index+1, fullName.length()); return fullName; } /** Test whether the operating system supports icons on frames (windows). * @return <code>true</code> if it does <em>not</em> * */ public static final boolean isLargeFrameIcons() { return (getOperatingSystem () == OS_SOLARIS) || (getOperatingSystem () == OS_HP); } /** Compute hash code of array. * Asks all elements for their own code and composes the * values. * @param arr array of objects, can contain <code>null</code>s * @return the hash code * @see Object#hashCode */ public static int arrayHashCode (Object[] arr) { int c = 0; int len = arr.length; for (int i = 0; i < len; i++) { Object o = arr[i]; int v = o == null ? 1 : o.hashCode (); c += (v ^ i); } return c; } /** Safe equality check. * The supplied objects are equal if: <UL> * <LI> both are <code>null</code> * <LI> both are arrays with same length and equal items (if the items are arrays, * they are <em>not</em> checked the same way again) * <LI> the two objects are {@link Object#equals} * </UL> * This method is <code>null</code>-safe, so if one of the parameters is true and the second not, * it returns <code>false</code>. * @param o1 the first object to compare * @param o2 the second object to compare * @return <code>true</code> if the objects are equal */ public static boolean compareObjects (Object o1, Object o2) { return compareObjectsImpl (o1, o2, 1); } /** Safe equality check with array recursion. * @param o1 the first object to compare * @param o2 the second object to compare * @param checkArraysDepth the depth to which arrays should be compared for equality (negative for infinite depth, zero for no comparison of elements, one for shallow, etc.) * @return <code>true</code> if the objects are equal * @see #compareObjects(Object, Object) */ public static boolean compareObjectsImpl (Object o1, Object o2, int checkArraysDepth) { // handle null values if (o1 == null) return (o2 == null); else if (o2 == null) return false; // handle arrays if (checkArraysDepth > 0) { if ((o1 instanceof Object[]) && (o2 instanceof Object[])) { // Note: also handles multidimensional arrays of primitive types correctly. // I.e. new int[0][] instanceof Object[] Object[] o1a = (Object[]) o1; Object[] o2a = (Object[]) o2; int l1 = o1a.length; int l2 = o2a.length; if (l1 != l2) return false; for (int i = 0; i < l1; i++) { if (! compareObjectsImpl (o1a[i], o2a[i], checkArraysDepth - 1)) { return false; } } return true; } else if ((o1 instanceof byte[]) && (o2 instanceof byte[])) { byte[] o1a = (byte[]) o1; byte[] o2a = (byte[]) o2; int l1 = o1a.length; int l2 = o2a.length; if (l1 != l2) return false; for (int i = 0; i < l1; i++) if (o1a[i] != o2a[i]) return false; return true; } else if ((o1 instanceof short[]) && (o2 instanceof short[])) { short[] o1a = (short[]) o1; short[] o2a = (short[]) o2; int l1 = o1a.length; int l2 = o2a.length; if (l1 != l2) return false; for (int i = 0; i < l1; i++) if (o1a[i] != o2a[i]) return false; return true; } else if ((o1 instanceof int[]) && (o2 instanceof int[])) { int[] o1a = (int[]) o1; int[] o2a = (int[]) o2; int l1 = o1a.length; int l2 = o2a.length; if (l1 != l2) return false; for (int i = 0; i < l1; i++) if (o1a[i] != o2a[i]) return false; return true; } else if ((o1 instanceof long[]) && (o2 instanceof long[])) { long[] o1a = (long[]) o1; long[] o2a = (long[]) o2; int l1 = o1a.length; int l2 = o2a.length; if (l1 != l2) return false; for (int i = 0; i < l1; i++) if (o1a[i] != o2a[i]) return false; return true; } else if ((o1 instanceof float[]) && (o2 instanceof float[])) { float[] o1a = (float[]) o1; float[] o2a = (float[]) o2; int l1 = o1a.length; int l2 = o2a.length; if (l1 != l2) return false; for (int i = 0; i < l1; i++) if (o1a[i] != o2a[i]) return false; return true; } else if ((o1 instanceof double[]) && (o2 instanceof double[])) { double[] o1a = (double[]) o1; double[] o2a = (double[]) o2; int l1 = o1a.length; int l2 = o2a.length; if (l1 != l2) return false; for (int i = 0; i < l1; i++) if (o1a[i] != o2a[i]) return false; return true; } else if ((o1 instanceof char[]) && (o2 instanceof char[])) { char[] o1a = (char[]) o1; char[] o2a = (char[]) o2; int l1 = o1a.length; int l2 = o2a.length; if (l1 != l2) return false; for (int i = 0; i < l1; i++) if (o1a[i] != o2a[i]) return false; return true; } else if ((o1 instanceof boolean[]) && (o2 instanceof boolean[])) { boolean[] o1a = (boolean[]) o1; boolean[] o2a = (boolean[]) o2; int l1 = o1a.length; int l2 = o2a.length; if (l1 != l2) return false; for (int i = 0; i < l1; i++) if (o1a[i] != o2a[i]) return false; return true; } // else not array type } // handle common objects--non-arrays, or arrays when depth == 0 return o1.equals (o2); } /** Assemble a human-presentable class name for a specified class. * Arrays are represented as e.g. <code>java.lang.String[]</code>. * @param clazz the class to name * @return the human-presentable name */ public static String getClassName (Class clazz) { // if it is an array, get short name of element type and append [] if (clazz.isArray ()) return getClassName (clazz.getComponentType ()) + "[]"; // NOI18N else return clazz.getName (); } /** Assemble a human-presentable class name for a specified class (omitting the package). * Arrays are represented as e.g. <code>String[]</code>. * @param clazz the class to name * @return the human-presentable name */ public static String getShortClassName (Class clazz) { // if it is an array, get short name of element type and append [] if (clazz.isArray ()) return getShortClassName (clazz.getComponentType ()) + "[]"; // NOI18N String name = clazz.getName ().replace ('$', '.'); return name.substring (name.lastIndexOf (".") + 1, name.length ()); // NOI18N } /** * Convert an array of objects to an array of primitive types. * E.g. an <code>Integer[]</code> would be changed to an <code>int[]</code>. * @param array the wrapper array * @return a primitive array * @throws IllegalArgumentException if the array element type is not a primitive wrapper */ public static Object toPrimitiveArray (Object[] array) { if (array instanceof Integer[]) { int[] r = new int [array.length]; int i, k = array.length; for (i = 0; i < k; i++) r [i] = (((Integer)array[i]) == null) ? 0 : ((Integer)array[i]).intValue (); return r; } if (array instanceof Boolean[]) { boolean[] r = new boolean [array.length]; int i, k = array.length; for (i = 0; i < k; i++) r [i] = (((Boolean)array[i]) == null) ? false : ((Boolean)array[i]).booleanValue (); return r; } if (array instanceof Byte[]) { byte[] r = new byte [array.length]; int i, k = array.length; for (i = 0; i < k; i++) r [i] = (((Byte)array[i]) == null) ? 0 : ((Byte)array[i]).byteValue (); return r; } if (array instanceof Character[]) { char[] r = new char [array.length]; int i, k = array.length; for (i = 0; i < k; i++) r [i] = (((Character)array[i]) == null) ? 0 : ((Character)array[i]).charValue (); return r; } if (array instanceof Double[]) { double[] r = new double [array.length]; int i, k = array.length; for (i = 0; i < k; i++) r [i] = (((Double)array[i]) == null) ? 0 : ((Double)array[i]).doubleValue (); return r; } if (array instanceof Float[]) { float[] r = new float [array.length]; int i, k = array.length; for (i = 0; i < k; i++) r [i] = (((Float)array[i]) == null) ? 0 : ((Float)array[i]).floatValue (); return r; } if (array instanceof Long[]) { long[] r = new long [array.length]; int i, k = array.length; for (i = 0; i < k; i++) r [i] = (((Long)array[i]) == null) ? 0 : ((Long)array[i]).longValue (); return r; } if (array instanceof Short[]) { short[] r = new short [array.length]; int i, k = array.length; for (i = 0; i < k; i++) r [i] = (((Short)array[i]) == null) ? 0 : ((Short)array[i]).shortValue (); return r; } throw new IllegalArgumentException (); } /** * Convert an array of primitive types to an array of objects. * E.g. an <code>int[]</code> would be turned into an <code>Integer[]</code>. * @param array the primitive array * @return a wrapper array * @throws IllegalArgumentException if the array element type is not primitive */ public static Object[] toObjectArray (Object array) { if (array instanceof Object[]) return (Object[]) array; if (array instanceof int[]) { int i, k = ((int[])array).length; Integer[] r = new Integer [k]; for (i = 0; i < k; i++) r [i] = new Integer (((int[]) array)[i]); return r; } if (array instanceof boolean[]) { int i, k = ((boolean[])array).length; Boolean[] r = new Boolean [k]; for (i = 0; i < k; i++) r [i] = ((boolean[]) array)[i] ? Boolean.TRUE : Boolean.FALSE; return r; } if (array instanceof byte[]) { int i, k = ((byte[])array).length; Byte[] r = new Byte [k]; for (i = 0; i < k; i++) r [i] = new Byte (((byte[]) array)[i]); return r; } if (array instanceof char[]) { int i, k = ((char[])array).length; Character[] r = new Character [k]; for (i = 0; i < k; i++) r [i] = new Character (((char[]) array)[i]); return r; } if (array instanceof double[]) { int i, k = ((double[])array).length; Double[] r = new Double [k]; for (i = 0; i < k; i++) r [i] = new Double (((double[]) array)[i]); return r; } if (array instanceof float[]) { int i, k = ((float[])array).length; Float[] r = new Float [k]; for (i = 0; i < k; i++) r [i] = new Float (((float[]) array)[i]); return r; } if (array instanceof long[]) { int i, k = ((long[])array).length; Long[] r = new Long [k]; for (i = 0; i < k; i++) r [i] = new Long (((long[]) array)[i]); return r; } if (array instanceof short[]) { int i, k = ((short[])array).length; Short[] r = new Short [k]; for (i = 0; i < k; i++) r [i] = new Short (((short[]) array)[i]); return r; } throw new IllegalArgumentException (); } /** * Get the object type for given primitive type. * * @param c primitive type (e.g. <code>int</code>) * @return object type (e.g. <code>Integer</code>) */ public static Class getObjectType (Class c) { if (!c.isPrimitive ()) return c; if (c == Integer.TYPE) return Integer.class; if (c == Boolean.TYPE) return Boolean.class; if (c == Byte.TYPE) return Byte.class; if (c == Character.TYPE) return Character.class; if (c == Double.TYPE) return Double.class; if (c == Float.TYPE) return Float.class; if (c == Long.TYPE) return Long.class; if (c == Short.TYPE) return Short.class; throw new IllegalArgumentException (); } /** * Get the primitive type for given object type. * * @param c object type (e.g. <code>Integer</code>) * @return primitive type (e.g. <code>int</code>) */ public static Class getPrimitiveType (Class c) { if (!c.isPrimitive ()) return c; if (c == Integer.class) return Integer.TYPE; if (c == Boolean.class) return Boolean.TYPE; if (c == Byte.class) return Byte.TYPE; if (c == Character.class) return Character.TYPE; if (c == Double.class) return Double.TYPE; if (c == Float.class) return Float.TYPE; if (c == Long.class) return Long.TYPE; if (c == Short.class) return Short.TYPE; throw new IllegalArgumentException (); } /** Find a focus-traverable component. * @param c the component to look in * @return the same component if traversable, else a child component if present, else <code>null</code> * @see Component#isFocusTraversable */ public static Component getFocusTraversableComponent (Component c) { if (c.isFocusTraversable ()) return c; if (!(c instanceof Container)) return null; int i, k = ((Container)c).getComponentCount (); for (i = 0; i < k; i++) { Component v = ((Container)c).getComponent (i); if (v != null) return v; } return null; } /** Parses parameters from a given string in shell-like manner. * Users of the Bourne shell (e.g. on Unix) will already be familiar with the behavior. * For example, when using <code>org.openide.execution.NbProcessDescriptor</code> (Execution API) * you should be able to: * <ul> * <li>Include command names with embedded spaces, such as <code>c:\Program Files\jdk\bin\javac</code>. * <li>Include extra command arguments, such as <code>-Dname=value</code>. * <li>Do anything else which might require unusual characters or processing. For example: * <p><code><pre> * "c:\program files\jdk\bin\java" -Dmessage="Hello /\\/\\ there!" -Xmx128m * </pre></code> * <p>This example would create the following executable name and arguments: * <ol> * <li> <code>c:\program files\jdk\bin\java</code> * <li> <code>-Dmessage=Hello /\/\ there!</code> * <li> <code>-Xmx128m</code> * </ol> * Note that the command string does not escape its backslashes--under the assumption * that Windows users will not think to do this, meaningless escapes are just left * as backslashes plus following character. * </ul> * <em>Caveat</em>: even after parsing, Windows programs (such as the Java launcher) * may not fully honor certain * characters, such as quotes, in command names or arguments. This is because programs * under Windows frequently perform their own parsing and unescaping (since the shell * cannot be relied on to do this). On Unix, this problem should not occur. * @param s a string to parse * @return an array of parameters */ public static String[] parseParameters(String s) { int NULL = 0x0; // STICK + whitespace or NULL + non_" int INPARAM = 0x1; // NULL + " or STICK + " or INPARAMPENDING + "\ // NOI18N int INPARAMPENDING = 0x2; // INPARAM + \ int STICK = 0x4; // INPARAM + " or STICK + non_" // NOI18N int STICKPENDING = 0x8; // STICK + \ Vector params = new Vector(5,5); char c; int state = NULL; StringBuffer buff = new StringBuffer(20); int slength = s.length(); for (int i = 0; i < slength; i++) { c = s.charAt(i); if (Character.isWhitespace(c)) { if (state == NULL) { if (buff.length () > 0) { params.addElement(buff.toString()); buff.setLength(0); } } else if (state == STICK) { params.addElement(buff.toString()); buff.setLength(0); state = NULL; } else if (state == STICKPENDING) { buff.append('\\'); params.addElement(buff.toString()); buff.setLength(0); state = NULL; } else if (state == INPARAMPENDING) { state = INPARAM; buff.append('\\'); buff.append(c); } else { // INPARAM buff.append(c); } continue; } if (c == '\\') { if (state == NULL) { ++i; if (i < slength) { char cc = s.charAt(i); if (cc == '"' || cc == '\\') { buff.append(cc); } else if (Character.isWhitespace(cc)) { buff.append(c); --i; } else { buff.append(c); buff.append(cc); } } else { buff.append('\\'); break; } continue; } else if (state == INPARAM) { state = INPARAMPENDING; } else if (state == INPARAMPENDING) { buff.append('\\'); state = INPARAM; } else if (state == STICK) { state = STICKPENDING; } else if (state == STICKPENDING) { buff.append('\\'); state = STICK; } continue; } if (c == '"') { if (state == NULL) { state = INPARAM; } else if (state == INPARAM) { state = STICK; } else if (state == STICK) { state = INPARAM; } else if (state == STICKPENDING) { buff.append('"'); state = STICK; } else { // INPARAMPENDING buff.append('"'); state = INPARAM; } continue; } if (state == INPARAMPENDING) { buff.append('\\'); state = INPARAM; } else if (state == STICKPENDING) { buff.append('\\'); state = STICK; } buff.append(c); } // collect if (state == INPARAM) { params.addElement(buff.toString()); } else if ((state & (INPARAMPENDING | STICKPENDING)) != 0) { buff.append('\\'); params.addElement(buff.toString()); } else { // NULL or STICK if (buff.length() != 0) { params.addElement(buff.toString()); } } String[] ret = new String[params.size()]; params.copyInto(ret); return ret; } /** Complementary method to parseParameters * @see #parseParameters */ public static String escapeParameters(String[] params) { StringBuffer sb = new StringBuffer(); for (int i = 0; i < params.length; i++) { escapeString(params[i], sb); sb.append(' '); } final int len = sb.length(); if (len > 0) { sb.setLength(len - 1); } return sb.toString().trim(); } /** Escapes one string * @see #escapeParameters */ private static void escapeString(String s, StringBuffer sb) { if (s.length() == 0) { sb.append("\"\""); return; } boolean hasSpace = false; final int sz = sb.length(); final int slen = s.length(); char c; for (int i = 0; i < slen; i++) { c = s.charAt(i); if (Character.isWhitespace(c)) { hasSpace = true; sb.append(c); continue; } if (c == '\\') { sb.append('\\').append('\\'); continue; } if (c == '"') { sb.append('\\').append('"'); continue; } sb.append(c); } if (hasSpace) { sb.insert(sz, '"'); sb.append('"'); } } // // Key conversions // /** Initialization of the names and values * @return array of two hashmaps first maps * allowed key names to their values (String, Integer) * and second * hashtable for mapping of values to their names (Integer, String) */ private static synchronized HashMap[] initNameAndValues () { if (namesAndValues != null) { HashMap[] arr = (HashMap[])namesAndValues.get (); if (arr != null) { return arr; } } Field[] fields = KeyEvent.class.getDeclaredFields (); HashMap names = new HashMap (fields.length * 4 / 3 + 1, 0.75f); HashMap values = new HashMap (fields.length * 4 / 3 + 1, 0.75f); for (int i = 0; i < fields.length; i++) { if (Modifier.isStatic (fields[i].getModifiers ())) { String name = fields[i].getName (); if (name.startsWith ("VK_")) { // NOI18N // exclude VK name = name.substring (3); try { int numb = fields[i].getInt (null); Integer value = new Integer (numb); names.put (name, value); values.put (value, name); } catch (IllegalArgumentException ex) { } catch (IllegalAccessException ex) { } } } } HashMap[] arr = { names, values }; namesAndValues = new SoftReference (arr); return arr; } /** Converts a Swing key stroke descriptor to a familiar Emacs-like name. * @param stroke key description * @return name of the key (e.g. <code>CS-F1</code> for control-shift-function key one) * @see #stringToKey */ public static String keyToString (KeyStroke stroke) { StringBuffer sb = new StringBuffer (); // add modifiers that must be pressed if (addModifiers (sb, stroke.getModifiers ())) { sb.append ('-'); } HashMap[] namesAndValues = initNameAndValues (); String c = (String)namesAndValues[1].get (new Integer (stroke.getKeyCode ())); if (c == null) { sb.append (stroke.getKeyChar ()); } else { sb.append (c); } return sb.toString (); } /** Construct a new key description from a given universal string * description. * Provides mapping between Emacs-like textual key descriptions and the * <code>KeyStroke</code> object used in Swing. * <P> * This format has following form: * <P><code>[C][A][S][M]-<em>identifier</em></code> * <p>Where: * <UL> * <LI> <code>C</code> stands for the Control key * <LI> <code>A</code> stands for the Alt key * <LI> <code>S</code> stands for the Shift key * <LI> <code>M</code> stands for the Meta key * </UL> * Every modifier before the hyphen must be pressed. * <em>identifier</EM> can be any text constant from {@link KeyEvent} but * without the leading <code>VK_</code> characters. So {@link KeyEvent#VK_ENTER} is described as * <code>ENTER</code>. * * @param s the string with the description of the key * @return key description object, or <code>null</code> if the string does not represent any valid key */ public static KeyStroke stringToKey (String s) { StringTokenizer st = new StringTokenizer (s.toUpperCase (), "-", true); // NOI18N int needed = 0; HashMap names = initNameAndValues ()[0]; int lastModif = -1; try { for (;;) { String el = st.nextToken (); // required key if (el.equals ("-")) { // NOI18N if (lastModif != -1) { needed |= lastModif; lastModif = -1; } continue; } // if there is more elements if (st.hasMoreElements ()) { // the text should describe modifiers lastModif = readModifiers (el); } else { // last text must be the key code Integer i = (Integer)names.get (el); if (i != null) { return KeyStroke.getKeyStroke (i.intValue (), needed); } else { return null; } } } } catch (NoSuchElementException ex) { return null; } } /** Convert a space-separated list of user-friendly key binding names to a list of Swing key strokes. * @param s the string with keys * @return array of key strokes, or <code>null</code> if the string description is not valid * @see #stringToKey */ public static KeyStroke[] stringToKeys (String s) { StringTokenizer st = new StringTokenizer (s.toUpperCase (), " "); // NOI18N ArrayList arr = new ArrayList (); while (st.hasMoreElements ()) { s = st.nextToken (); KeyStroke k = stringToKey (s); if (k == null) return null; arr.add (k); } return (KeyStroke[])arr.toArray (new KeyStroke[arr.size ()]); } /** Adds characters for modifiers to the buffer. * @param buf buffer to add to * @param modif modifiers to add (KeyEvent.XXX_MASK) * @return true if something has been added */ private static boolean addModifiers (StringBuffer buf, int modif) { boolean b = false; if ((modif & KeyEvent.CTRL_MASK) != 0) { buf.append("C"); // NOI18N b = true; } if ((modif & KeyEvent.ALT_MASK) != 0) { buf.append("A"); // NOI18N b = true; } if ((modif & KeyEvent.SHIFT_MASK) != 0) { buf.append("S"); // NOI18N b = true; } if ((modif & KeyEvent.META_MASK) != 0) { buf.append("M"); // NOI18N b = true; } return b; } /** Reads for modifiers and creates integer with required mask. * @param s string with modifiers * @return integer with mask * @exception NoSuchElementException if some letter is not modifier */ private static int readModifiers (String s) throws NoSuchElementException { int m = 0; for (int i = 0; i < s.length (); i++) { switch (s.charAt (i)) { case 'C': m |= KeyEvent.CTRL_MASK; break; case 'A': m |= KeyEvent.ALT_MASK; break; case 'M': m |= KeyEvent.META_MASK; break; case 'S': m |= KeyEvent.SHIFT_MASK; break; default: throw new NoSuchElementException (); } } return m; } /** * Finds out the monitor where the user currently has the input focus. * This method is usually used to help the client code to figure out on * which monitor it should place newly created windows/frames/dialogs. * * @return the GraphicsConfiguration of the monitor which currently has the * input focus */ private static GraphicsConfiguration getCurrentGraphicsConfiguration() { Frame[] frames = Frame.getFrames(); for (int i = 0; i < frames.length; i++) { if (javax.swing.SwingUtilities.findFocusOwner(frames[i]) != null) { return frames[i].getGraphicsConfiguration(); } } return GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration(); } /** * Returns the usable area of the screen where applications can place its * windows. The method subtracts from the screen the area of taskbars, * system menus and the like. The screen this method applies to is the one * which is considered current, ussually the one where the current input * focus is. * * @return the rectangle of the screen where one can place windows * * @since 2.5 */ public static Rectangle getUsableScreenBounds() { return getUsableScreenBounds(getCurrentGraphicsConfiguration()); } /** * Returns the usable area of the screen where applications can place its * windows. The method subtracts from the screen the area of taskbars, * system menus and the like. * * @param gconf the GraphicsConfiguration of the monitor * @return the rectangle of the screen where one can place windows * * @since 2.5 */ public static Rectangle getUsableScreenBounds(GraphicsConfiguration gconf) { if (gconf == null) gconf = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration(); Rectangle bounds = new Rectangle(gconf.getBounds()); String str; str = System.getProperty("netbeans.screen.insets"); // NOI18N if (str != null) { StringTokenizer st = new StringTokenizer(str, ", "); // NOI18N if (st.countTokens() == 4) { try { bounds.y = Integer.parseInt(st.nextToken()); bounds.x = Integer.parseInt(st.nextToken()); bounds.height -= bounds.y + Integer.parseInt(st.nextToken()); bounds.width -= bounds.x + Integer.parseInt(st.nextToken()); } catch (NumberFormatException ex) { ErrorManager.getDefault().notify(ErrorManager.WARNING, ex); } } return bounds; } str = System.getProperty("netbeans.taskbar.height"); // NOI18N if (str != null) { bounds.height -= Integer.getInteger(str, 0).intValue(); return bounds; } // if JDK 1.4 or later if (Dependency.JAVA_SPEC.compareTo(new SpecificationVersion("1.4")) >= 0) { // NOI18N try { Toolkit toolkit = Toolkit.getDefaultToolkit(); Method m = Toolkit.class.getMethod("getScreenInsets", // NOI18N new Class[] { GraphicsConfiguration.class }); if (m == null) return bounds; Insets insets = (Insets) m.invoke(toolkit, new Object[] { gconf }); bounds.y += insets.top; bounds.x += insets.left; bounds.height -= insets.top + insets.bottom; bounds.width -= insets.left + insets.right; } catch (Exception ex) { ErrorManager.getDefault().notify(ErrorManager.WARNING, ex); } return bounds; } if (Utilities.isWindows ()) { bounds.height -= Utilities.TYPICAL_WINDOWS_TASKBAR_HEIGHT; return bounds; } if ((getOperatingSystem() & OS_MAC) != 0) { bounds.height -= TYPICAL_MACOSX_MENU_HEIGHT; bounds.y += TYPICAL_MACOSX_MENU_HEIGHT; return bounds; } return bounds; } /** * Helps client code place components on the center of the screen. It * handles multiple monitor configuration correctly * * @param componentSize the size of the component * @return bounds of the centered component * * @since 2.5 */ public static Rectangle findCenterBounds(Dimension componentSize) { return findCenterBounds(getCurrentGraphicsConfiguration(), componentSize); } /** * Helps client code place components on the center of the screen. It * handles multiple monitor configuration correctly * * @param gconf the GraphicsConfiguration of the monitor * @param componentSize the size of the component * @return bounds of the centered component */ private static Rectangle findCenterBounds(GraphicsConfiguration gconf, Dimension componentSize) { if (gconf == null) gconf = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration(); Rectangle bounds = gconf.getBounds(); return new Rectangle(bounds.x + (bounds.width - componentSize.width) / 2, bounds.y + (bounds.height - componentSize.height) / 2, componentSize.width, componentSize.height); } /** @return size of the screen. The size is modified for Windows OS * - some pointes are subtracted to reflect a presence of the taskbar * * @deprecated this method is almost useless in multiple monitor configuration * * @see #getUsableScreenBounds() * @see #findCenterBounds(Dimension) */ public static final Dimension getScreenSize() { Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); if (isWindows() && !Boolean.getBoolean ("netbeans.no.taskbar")) { screenSize.height -= TYPICAL_WINDOWS_TASKBAR_HEIGHT; } else if ((getOperatingSystem() & OS_MAC) != 0) screenSize.height -= TYPICAL_MACOSX_MENU_HEIGHT; return screenSize; } /** Utility method for avoiding of memory leak in JDK 1.3 / JFileChooser.showDialog(...) * @param parent * @param approveButtonText */ public static final int showJFileChooser(javax.swing.JFileChooser chooser, java.awt.Component parent, java.lang.String approveButtonText) { if(approveButtonText != null) { chooser.setApproveButtonText(approveButtonText); chooser.setDialogType(javax.swing.JFileChooser.CUSTOM_DIALOG); } Frame frame = null; Dialog parentDlg = null; if (parent instanceof Dialog) parentDlg = (Dialog) parent; else frame = parent instanceof java.awt.Frame ? (Frame) parent : (Frame)javax.swing.SwingUtilities.getAncestorOfClass(Frame.class, parent); String title = chooser.getDialogTitle(); if (title == null) { title = chooser.getUI().getDialogTitle(chooser); } final javax.swing.JDialog dialog; if (parentDlg != null) dialog = new javax.swing.JDialog(parentDlg, title, true); else dialog = new javax.swing.JDialog(frame, title, true); dialog.setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); Container contentPane = dialog.getContentPane(); contentPane.setLayout(new BorderLayout()); contentPane.add(chooser, BorderLayout.CENTER); dialog.pack(); dialog.setBounds(findCenterBounds(parent.getGraphicsConfiguration(), dialog.getSize())); chooser.rescanCurrentDirectory(); final int[] retValue = new int[] { javax.swing.JFileChooser.CANCEL_OPTION }; java.awt.event.ActionListener l = new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent ev) { if (ev.getActionCommand() == javax.swing.JFileChooser.APPROVE_SELECTION) { retValue[0] = javax.swing.JFileChooser.APPROVE_OPTION; } dialog.setVisible(false); dialog.dispose(); } }; chooser.addActionListener(l); dialog.show(); return retValue[0]; } /** Exception indicating that a given list could not be partially-ordered. * @see #partialSort * @deprecated Used only by the deprecated {@link #partialSort} */ public static class UnorderableException extends RuntimeException { private Collection unorderable; private Map deps; static final long serialVersionUID =6749951134051806661L; /** Create a new unorderable-list exception with no detail message. * @param unorderable a collection of list elements which could not be ordered * (because there was some sort of cycle) * @param deps dependencies associated with the list; a map from list elements * to sets of list elements which that element must appear after */ public UnorderableException (Collection unorderable, Map deps) { super (/* "Cannot be ordered: " + unorderable */); // NOI18N this.unorderable = unorderable; this.deps = deps; } /** Create a new unorderable-list exception with a specified detail message. * @param message the detail message * @param unorderable a collection of list elements which could not be ordered * (because there was some sort of cycle) * @param deps dependencies associated with the list; a map from list elements * to sets of list elements which that element must appear after */ public UnorderableException (String message, Collection unorderable, Map deps) { super (message); this.unorderable = unorderable; this.deps = deps; } /** Get the unorderable elements. * @return the elements * @see Utilities.UnorderableException#Utilities.UnorderableException(Collection,Map) */ public Collection getUnorderable () { return unorderable; } /** Get the dependencies. * @return the dependencies * @see Utilities.UnorderableException#Utilities.UnorderableException(Collection,Map) */ public Map getDeps () { return deps; } } /** Sort a list according to a specified partial order. * Note that in the current implementation, the comparator will be called * exactly once for each distinct pair of list elements, ignoring order, * so caching its results is a waste of time. * @param l the list to sort (will not be modified) * @param c a comparator to impose the partial order; "equal" means that the elements * are not ordered with respect to one another, i.e. may be only a partial order * @param stable whether to attempt a stable sort, meaning that the position of elements * will be disturbed as little as possible; might be slightly slower * @return the partially-sorted list * @throws UnorderableException if the specified partial order is inconsistent on this list * @deprecated Deprecated in favor of the potentially much faster (and possibly more correct) {@link #topologicalSort}. */ public static List partialSort (List l, Comparator c, boolean stable) throws UnorderableException { // map from objects in the list to null or sets of objects they are greater than // (i.e. must appear after): Map deps = new HashMap (); // Map<Object,Set<Object>> int size = l.size (); // Create a table of dependencies. for (int i = 0; i < size; i++) { for (int j = i + 1; j < size; j++) { int cmp = c.compare (l.get (i), l.get (j)); if (cmp != 0) { Object earlier = l.get (cmp < 0 ? i : j); Object later = l.get (cmp > 0 ? i : j); Set s = (Set) deps.get (later); if (s == null) deps.put (later, s = new HashSet ()); s.add (earlier); } } } // Lists of items to process, and items sorted. List left = new LinkedList (l); List sorted = new ArrayList (size); while (left.size () > 0) { boolean stillGoing = false; Iterator it = left.iterator (); while (it.hasNext ()) { Object elt = it.next (); Set eltDeps = (Set) deps.get (elt); if (eltDeps == null || eltDeps.size () == 0) { // This one is OK to add to the result now. it.remove (); stillGoing = true; sorted.add (elt); // Mark other elements that should be later // than this as having their dep satisfied. Iterator it2 = left.iterator (); while (it2.hasNext ()) { Object elt2 = it2.next (); Set eltDeps2 = (Set) deps.get (elt2); if (eltDeps2 != null) eltDeps2.remove (elt); } if (stable) break; } } if (! stillGoing) { // Clean up deps to only include "interesting" problems. it = deps.entrySet().iterator(); while (it.hasNext()) { Map.Entry me = (Map.Entry)it.next(); if (!left.contains(me.getKey())) { it.remove(); } else { Set s = (Set)me.getValue(); Iterator it2 = s.iterator(); while (it2.hasNext()) { if (!left.contains(it2.next())) { it2.remove(); } } if (s.isEmpty()) { it.remove(); } } } throw new UnorderableException (left, deps); } } return sorted; } /** * Topologically sort some objects. * <p>There may not be any nulls among the objects, nor duplicates * (as per hash/equals), nor duplicates among the edge lists. * The edge map need not contain an entry for every object, only if it * has some outgoing edges (empty but not null map values are permitted). * The edge map may contain neither keys nor value entries for objects not * in the collection to be sorted. * <p>The incoming parameters will not be modified; they must not be changed * during the call and possible calls to TopologicalSortException methods. * The returned list will support modifications. * <p>There is a <em>weak</em> stability guarantee: if there are no edges * which contradict the incoming order, the resulting list will be in the same * order as the incoming elements. However if some elements need to be rearranged, * it is <em>not</em> guaranteed that others will not also be rearranged, even * if they did not strictly speaking need to be. * @param c a collection of objects to be topologically sorted * @param edges constraints among those objects, of type <code>Map<Object,Collection></code>; * if an object is a key in this map, the resulting order will * have that object before any objects listed in the value * @return a partial ordering of the objects in the collection, * @exception TopologicalSortException if the sort cannot succeed due to cycles in the graph, the * exception contains additional information to describe and possibly recover from the error * @since 3.30 * @see "#27286" */ public static List topologicalSort(Collection c, Map edges) throws TopologicalSortException { Map finished = new HashMap(); List r = new ArrayList(Math.max(c.size(), 1)); List cRev = new ArrayList(c); Collections.reverse(cRev); Iterator it = cRev.iterator(); while (it.hasNext()) { List cycle = visit(it.next(), edges, finished, r); if (cycle != null) { throw new TopologicalSortException (cRev, edges); } } Collections.reverse(r); return r; } /** * Visit one node in the DAG. * @param node node to visit * @param edges edges in the DAG * @param finished which nodes are finished; a node has no entry if it has not yet * been visited, else it is set to false while recurring and true * when it has finished * @param r the order in progress * @return list with detected cycle */ static List visit(Object node, Map edges, Map finished, List r) { Boolean b = (Boolean)finished.get(node); //System.err.println("node=" + node + " color=" + b); if (b != null) { if (b.booleanValue ()) { return null; } ArrayList cycle = new ArrayList (); cycle.add (node); finished.put (node, null); return cycle; } Collection e = (Collection)edges.get(node); if (e != null) { finished.put(node, Boolean.FALSE); Iterator it = e.iterator(); while (it.hasNext()) { List cycle = visit(it.next(), edges, finished, r); if (cycle != null) { if (cycle instanceof ArrayList) { // if cycle instanceof ArrayList we are still in the // cycle and we want to collect new members if (Boolean.FALSE == finished.get (node)) { // another member in the cycle cycle.add (node); } else { // we have reached the head of the cycle // do not add additional cycles anymore Collections.reverse(cycle); // changing cycle to not be ArrayList cycle = Collections.unmodifiableList(cycle); } } // mark this node as tested finished.put (node, Boolean.TRUE); // and report an error return cycle; } } } finished.put(node, Boolean.TRUE); r.add(node); return null; } // Package retranslation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ private static final String TRANS_LOCK = "TRANS_LOCK"; /** last used classloader or if run in test mode the TRANS_LOCK */ private static Object transLoader; /** regular expression to with all changes */ private static RE transExp; /** Provides support for parts of the system that deal with classnames * (use <code>Class.forName</code>, <code>NbObjectInputStream</code>, etc.). * <P> * Often class names (especially package names) changes during lifecycle * of a module. When some piece of the system stores the name of a class * in certain point of a time and wants to find the correct <code>Class</code> * later it needs to count with the possibility of rename. * <P> * For such purposes this method has been created. It allows modules to * register their classes that changed names and other parts of system that * deal with class names to find the correct names. * <P> * To register a mapping from old class names to new ones create a file * <code>META-INF/netbeans/translate.names</code> in your module and fill it * with your mapping: * <PRE> * # * # Mapping of legacy classes to new ones * # * * org.oldpackage.MyClass=org.newpackage.MyClass # rename of package for one class * org.mypackage.OldClass=org.mypackage.NewClass # rename of class in a package * * # rename of class and package * org.oldpackage.OldClass=org.newpackage.NewClass * * # rename of whole package * org.someoldpackage=org.my.new.package.structure * * </PRE> * Btw. one can use spaces instead of <code>=</code> sign. * For a real world example * check the * <a href="http://www.netbeans.org/source/browse/xml/text-edit/compat/src/META-INF/netbeans/"> * xml module</a>. * * <P> * For purposes of <link>org.openide.util.io.NbObjectInputStream</link> there is * a following special convention: * If the * className is not listed as one that is to be renamed, the returned * string == className, if the className is registered to be renamed * than the className != returned value, even in a case when className.equals (retValue) * * @param className fully qualified name of a class to translate * @return new name of the class according to renaming rules. */ public static String translate(final String className) { checkMapping (); RE exp; synchronized (TRANS_LOCK) { exp = transExp; } if (exp == null) { // no transition table found return className; } synchronized (exp) { // refusing convertions as fast as possible return exp.convert (className); } } /** Loads all resources that contain renaming information. * @param l classloader to load packages from */ private static void checkMapping () { // test if we run in test mode if (transLoader == TRANS_LOCK) { // no check return; } ClassLoader current = (ClassLoader)Lookup.getDefault ().lookup (ClassLoader.class); if (current == null) { current = ClassLoader.getSystemClassLoader(); } if (transLoader == current) { // no change, no rescan return; } initForLoader (current, current); } /* Initializes the content of transition table from a classloader. * @param loader loader to read data from * @param set loader to set as the transLoader or null if we run in test mode */ static void initForLoader (ClassLoader current, Object set) { if (set == null) { set = TRANS_LOCK; } Enumeration en; try { en = current.getResources("META-INF/netbeans/translate.names"); } catch (IOException ex) { ErrorManager.getDefault ().notify (ErrorManager.INFORMATIONAL, ex); en = null; } if (en == null || !en.hasMoreElements()) { synchronized (TRANS_LOCK) { transLoader = set; transExp = null; } return; } // format of line in the meta files // // # comments are allowed // a.name.in.a.Package=another.Name # with comment is allowed // for.compatibility.one.can.use.Space instead.of.Equal // RE re = null; // [pnejedly:perf] commented out. The RegExp based translation was way slower // than the hand-written RE13 // if (Dependency.JAVA_SPEC.compareTo(new SpecificationVersion("1.4")) >= 0) { // NOI18N // try { // re = (RE)Class.forName ("org.openide.util.RE14").newInstance (); // } catch (ThreadDeath t) { // throw t; // } catch (Throwable t) { // } // } if (re == null) { re = new RE13 (); } TreeSet list = new TreeSet (new Comparator () { public int compare (Object o1, Object o2) { String s1 = ((String[])o1)[0]; String s2 = ((String[])o2)[0]; int i1 = s1.length (); int i2 = s2.length (); if (i1 != i2) return i2 - i1; return s2.compareTo (s1); } }); while (en.hasMoreElements ()) { URL u = (URL)en.nextElement(); try { BufferedReader reader = new BufferedReader( new InputStreamReader (u.openStream(), "UTF8") // use explicit encoding //NOI18N ); loadTranslationFile (re, reader, list); reader.close (); } catch (IOException ex) { ErrorManager.getDefault ().annotate(ex, ErrorManager.UNKNOWN, "Problematic file: " + u, null, null, null); ErrorManager.getDefault ().notify (ErrorManager.INFORMATIONAL, ex); } } // construct a regular expression of following form. Let "1", "2", "3", "4" // be the keys: // "^ // thus if 4 is matched five groups will be created String[] arr = new String[list.size ()]; String[] pattern = new String[arr.length]; int i = 0; Iterator it = list.iterator (); while (it.hasNext ()) { String[] pair = (String[])it.next (); arr[i] = pair[1].intern (); // name of the track pattern[i] = pair[0]; // original object i++; } synchronized (TRANS_LOCK) { // last check if (arr.length == 0) { transExp = null; } else { transExp = re; transExp.init (pattern, arr); } transLoader = set; } } /** * Load single translation file. * @param resource URL identifiing transaction table * @param results will be filled with String[2] */ private static void loadTranslationFile( RE re, BufferedReader reader, Set results ) throws IOException { for (;;) { String line = reader.readLine (); if (line == null) { break; } if (line.length () == 0 || line.startsWith ("#")) { // NOI18N continue; } String[] pair = re.readPair (line); if (pair == null) { throw new java.io.InvalidObjectException ("Line is invalid: " + line); } results.add (pair); } } /** This method merges two images into the new one. The second image is drawn * over the first one with its top-left corner at x, y. Images mustn't be of the same size. * New image will have a size of max(second image size + top-left corner, first image size). * Method is used mostly when second image contains transparent pixels (e.g. for badging). * If both images are <code>null</code>, it makes default transparent 16x16 image. * @param image1 underlying image * @param image2 second image * @param x x position of top-left corner * @param y y position of top-left corner * @return new merged image */ public static final Image mergeImages (Image image1, Image image2, int x, int y) { return IconManager.mergeImages(image1, image2, x, y); } /** * Loads an image from the specified resource ID. The image is loaded using the "system" classloader registered in * Lookup. * @param resourceID resource path of the icon (no initial slash) * @return icon's Image, or null, if the icon cannot be loaded. */ public static final Image loadImage (String resourceID) { return IconManager.getIcon(resourceID); } // // Support for work with actions // /** type of Class or of an Exception thrown */ private static Object actionClassForPopupMenu; /** Builds a popup menu from actions for provided context specified by * <code>Lookup</code>. * Takes list of actions and for actions whic are instances of * <code>ContextAwareAction</code> creates and uses the context aware instance. * Then gets the action presenter or simple menu item for the action to the * popup menu for each action (or separator for each 'lonely' null array member). * * @param actions array of actions to build menu for. Can contain null * elements, they will be replaced by separators * @param context the context for which the popup is build * @return the constructed popup menu * @see ContextAwareAction * @since 3.29 */ public static javax.swing.JPopupMenu actionsToPopup ( Action[] actions, Lookup context ) { javax.swing.JPopupMenu menu = null; try { if (actionClassForPopupMenu == null) { actionClassForPopupMenu = Class.forName ("org.openide.awt.JPopupMenuPlus"); } if (actionClassForPopupMenu instanceof Class) { menu = (javax.swing.JPopupMenu)((Class)actionClassForPopupMenu).newInstance (); } } catch (IllegalAccessException ex) { actionClassForPopupMenu = new Object (); } catch (InstantiationException ex) { actionClassForPopupMenu = new Object (); } catch (ClassNotFoundException ex) { actionClassForPopupMenu = new Object (); } if (menu == null) { menu = new javax.swing.JPopupMenu (); } // keeps actions for which was menu item created already HashSet counted = new HashSet (); boolean canSep = false; for (int i = 0; i < actions.length; i++) { boolean addSep = true; Action action = actions[i]; if (action != null) { // if this action has menu item already, skip to next iteration if (counted.contains (action)) continue; counted.add (action); // switch to replacement action if there is some if(action instanceof ContextAwareAction) { action = ((ContextAwareAction)action).createContextAwareInstance(context); } addSep = false; canSep = true; javax.swing.JMenuItem item; if (action instanceof org.openide.util.actions.Presenter.Popup) { item = ((org.openide.util.actions.Presenter.Popup)action).getPopupPresenter (); if (item == null) { NullPointerException npe = new NullPointerException( "findContextMenuImpl, getPopupPresenter returning null for " + action); // NOI18N ErrorManager.getDefault ().notify (ErrorManager.INFORMATIONAL, npe); } menu.add (item); } else { menu.add (action); } } if (addSep && canSep) { menu.addSeparator (); canSep = false; } } return menu; } /** Builds a popup menu for provided component. It retrieves context * (lookkup) from provided component instance or one of its parent * (it searches up to the hierarchy for <code>Lookup.Provider</code> instance). * If none of the components is <code>Lookup.Provider</code> instance, then * it is created context which is fed with composite ActionMap which delegates * to all components up to hierarchy started from the specified one. * Then it is called method {@link #actionsToPopup(Action[], Lookup)} whith * the found <code>Lookup</code> instance, which actualy creates a popup menu. * * @param actions array of actions to build menu for. Can contain null * elements, they will be replaced by separators * @param component a component in which to search for a context * @return the constructed popup menu * @see Lookup.Provider * @see #actionsToPopup(Action[], Lookup) * @since 3.29 */ public static javax.swing.JPopupMenu actionsToPopup ( Action[] actions, java.awt.Component component ) { Lookup lookup = null; for (Component c = component; c != null; c = c.getParent()) { if (c instanceof Lookup.Provider) { lookup = ((Lookup.Provider)c).getLookup (); if (lookup != null) { break; } } } if(lookup == null) { // Fallback to composite action map, even it is questionable, // whether we should support component which is not (nor // none of its parents) lookup provider. UtilitiesCompositeActionMap map = new UtilitiesCompositeActionMap (component); lookup = org.openide.util.lookup.Lookups.singleton(map); } return actionsToPopup (actions, lookup); } // // end of actions stuff // /** * Loads an image based on resource path. * Exactly like {@link #loadImage(String)} but may do a localized search. * For example, requesting <samp>org/netbeans/modules/foo/resources/foo.gif</samp> * might actually find <samp>org/netbeans/modules/foo/resources/foo_ja.gif</samp> * or <samp>org/netbeans/modules/foo/resources/foo_mybranding.gif</samp>. * @since 3.24 */ public static final Image loadImage(String resource, boolean localized) { if (localized) { String base, ext; int idx = resource.lastIndexOf('.'); if (idx != -1 && idx > resource.lastIndexOf('/')) { base = resource.substring(0, idx); ext = resource.substring(idx); } else { base = resource; ext = ""; // NOI18N } // #31008. [PENDING] remove in case package cache is precomputed Image baseVariant = loadImage(base + ext); Iterator it = NbBundle.getLocalizingSuffixes(); while (it.hasNext()) { String suffix = (String)it.next(); Image i; if (suffix.length() == 0) { i = baseVariant; } else { i = loadImage(base + suffix + ext); } if (i != null) { return i; } } return null; } else { return loadImage(resource); } } /** * Returns a cursor with an arrow and an hourglass (or stop watch) badge, * to be used when a component is busy but the UI is still responding to the user. * * Similar to the predefined {@link Cursor#WAIT_CURSOR}, but has an arrow to indicate * a still-responsive UI. * * <p>Typically you will set the cursor only temporarily: * * <pre> * <font class="comment">// code is running in other then event dispatch thread</font> * currentComponent.setCursor(Utilities.createProgressCursor(currentComponent)); * <font class="keyword">try</font> { * <font class="comment">// perform some work in other than event dispatch thread * // (do not block UI)</font> * } <font class="keyword">finally</font> { * currentComponent.setCursor(<font class="constant">null</font>); * } * </pre> * * <p>This implementation provides one cursor for all Mac systems, one for all * Unix systems (regardless of window manager), and one for all other systems * including Windows. * * @param component the non-null component that will use the progress cursor * @return a progress cursor (Unix, Windows or Mac) * * @since 3.23 */ public static final Cursor createProgressCursor(Component component) { // refuse null component if (component == null) { throw new NullPointerException("Given component is null"); //NOI18N } Image image = null; // First check for Mac because its part of the Unix_Mask if (getOperatingSystem() == OS_MAC) { image = loadImage("org/openide/resources/progress-cursor-mac.gif"); //NOI18N } else if (isUnix()) { image = loadImage("org/openide/resources/progress-cursor-motif.gif"); //NOI18N } // All other OS, including Windows, use Windows cursor else { image = loadImage("org/openide/resources/progress-cursor-win.gif"); //NOI18N } return createCustomCursor (component, image, "PROGRESS_CURSOR"); //NOI18N } // added to fix issue #30665 (bad size on linux) private static Cursor createCustomCursor(Component component, Image icon, String name) { Toolkit t = component.getToolkit(); Dimension d = t.getBestCursorSize(16, 16); Image i = icon; if (d.width != icon.getWidth(null)) { if ((d.width) == 0 && (d.height == 0)) { // system doesn't support custom cursors, falling back return Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR); } // need to resize the icon Image empty = IconManager.createBufferedImage(d.width, d.height); i = Utilities.mergeImages(icon, empty, 0, 0); } return t.createCustomCursor(i, new Point(1,1), name); } /** Attaches asynchronous init job to given component. * {@link AsyncGUIJob#construct()} will be called after first * paint, when paint event arrives. Later, {@link AsyncGUIJob#finished()} * will be called according to the rules of the <code>AsyncGUIJob</code> interface. * * Useful for components that have slower initialization phase, component * can benefit from more responsive behaviour during init. * * @param comp4Init Regular component in its pre-inited state, state in which * component will be shown between first paint and init completion. * @param initJob Initialization job to be called asynchronously. Job can * optionally implement {@link Cancellable} * interface for proper cancel logic. Cancel method will be called * when component stops to be showing during job's progress. * See {@link java.awt.Component#isShowing} * * @since 3.36 */ public static final void attachInitJob (Component comp4Init, AsyncGUIJob initJob) { new AsyncInitSupport(comp4Init, initJob); } /** Interfaces for communication between Utilities.translate and regular * expression impl. * * Order of methods is: * readPair few times * init once * convert many times */ static interface RE { public void init (String[] original, String[] newversion); public String convert (String pattern); /** Parses line of text to two parts: the key and the rest */ public String[] readPair (String line); } /** Implementation of the active queue. */ private static final class ActiveQueue extends ReferenceQueue implements Runnable { private boolean running; private boolean deprecated; public ActiveQueue (boolean deprecated) { this.deprecated = deprecated; Thread t = new Thread (this, "Active Reference Queue Daemon"); // NOI18N t.setPriority(Thread.MIN_PRIORITY); t.setDaemon(true); // to not prevent exit of VM t.start (); } public Reference poll() { throw new java.lang.UnsupportedOperationException (); } public Reference remove(long timeout) throws IllegalArgumentException, InterruptedException { throw new java.lang.InterruptedException (); } public Reference remove() throws InterruptedException { throw new java.lang.InterruptedException (); } /** Called either from Thread.run or RequestProcessor.post. In first case * calls scanTheQueue (only once) in the second and nexts calls cleanTheQueue */ public void run () { synchronized (this) { if (running) { return; } running = true; } for (;;) { try { Reference ref = super.remove (0); if (! (ref instanceof Runnable)) { ErrorManager.getDefault().log ( ErrorManager.ERROR, "A reference not implementing runnable has been added to the Utilities.activeReferenceQueue (): " + ref.getClass () // NOI18N ); continue; } if (deprecated) { ErrorManager.getDefault().log ( ErrorManager.WARNING, "Utilities.ACTIVE_REFERENCE_QUEUE has been deprecated for " + ref.getClass () + " use Utilities.activeReferenceQueue" // NOI18N ); } // do the cleanup try { ((Runnable)ref).run (); } catch (ThreadDeath td) { throw td; } catch (Throwable t) { // Should not happen. // If it happens, it is a bug in client code, notify! ErrorManager.getDefault().notify(t); } } catch (InterruptedException ex) { ErrorManager.getDefault ().notify (ErrorManager.INFORMATIONAL, ex); } } } } /** * A convertor between files and URLs. */ interface FileURLConvertor { /** @see Utilities#toURL */ URL toURL(File f) throws MalformedURLException; /** @see Utilities#toFile */ File toFile(URL u); } /** * A convertor between files and URLs that (sort of) works under JDK 1.3. */ private static class FileURLConvertor13 implements FileURLConvertor { FileURLConvertor13() {} public URL toURL(File f) throws MalformedURLException { URL u = f.toURL(); String u2 = u.toExternalForm(); if (u2.indexOf('#') != -1) { // #27330: installation in a dir containing hash marks int i; while ((i = u2.indexOf('#')) != -1) { u2 = u2.substring(0, i) + "%23" + u2.substring(i + 1); // NOI18N } u = new URL(u2); } return u; } public File toFile(URL u) { if (!"file".equals(u.getProtocol())) { // NOI18N return null; } String path = u.getPath(); int i; while ((i = path.indexOf("%23")) != -1) { // NOI18N path = path.substring(0, i) + '#' + path.substring(i + 3); } return new File(path.replace('/', File.separatorChar)); } } /** currently available File ↔ URL convertor */ private static FileURLConvertor convertor = null; /** * Convert a file to a matching <code>file:</code> URL. * Under JDK 1.4, the result should be properly escaped as a URL; * under JDK 1.3, it may not be, and so filenames containing * strange characters may not work well, though hash marks are * supported as a special case. * <p>The resulting URL should be openable as a means of accessing the * file contents, except for some unusual characters under JDK 1.3. * @param f a file (absolute only) * @return a URL using the <code>file</code> protocol * @throws MalformedURLException for no good reason * @see File#toURI * @see java.net.URI#toURL * @see File#toURL * @see URL#openConnection * @see #toFile * @see "#29711" * @since 3.26 */ public static URL toURL(File f) throws MalformedURLException { if (f == null) throw new NullPointerException(); if (!f.isAbsolute()) throw new IllegalArgumentException("Relative path: " + f); // NOI18N if (convertor == null) { // No access to Dependency.JAVA_SPEC from here, so just always try to load it. try { Class clazz = Class.forName("org.openide.util.FileURLConvertor14"); // NOI18N FileURLConvertor c = (FileURLConvertor)clazz.newInstance(); try { URL u = c.toURL(f); convertor = c; return u; } catch (MalformedURLException mfue) { convertor = c; throw mfue; } } catch (Exception e) { // ClassNotFoundException, etc. } catch (LinkageError e) { // NoSuchMethodError, etc. } // Fallback - JDK 1.3, or problem under 1.4. convertor = new FileURLConvertor13(); } return convertor.toURL(f); } /** * Convert a <code>file:</code> URL to a matching file. * Under JDK 1.4, this should be the exact inverse of * converting the file to a URL. Under JDK 1.3, the * conversion is sloppier and may fail for files named * strangely (including unusual characters), though as a * special case the hash mark (<samp>#</samp>) is supported. * <p>You may not use a URL generated from a file on a different * platform, as file name conventions may make the result meaningless * or even unparsable. * @param u a URL with the <code>file</code> protocol * @return an absolute file it points to, or <code>null</code> if the URL * does not seem to point to a file at all * @see java.net.URI#URI(String) * @see File#File(java.net.URI) * @see #toURL * @see "#29711" * @since 3.26 */ public static File toFile(URL u) { if (u == null) throw new NullPointerException(); if (convertor == null) { try { Class clazz = Class.forName("org.openide.util.FileURLConvertor14"); // NOI18N FileURLConvertor c = (FileURLConvertor)clazz.newInstance(); File f = c.toFile(u); // Only set it after it has successfully gone through the whole code // path. Some VMs (e.g. -Xverify:none) will not resolve all methods // until they are actually run. Making the instance is not enough // to guarantee that all of its references have been resolved. convertor = c; return f; } catch (Exception e) { // ClassNotFoundException, etc. } catch (LinkageError e) { // NoSuchMethodError, etc. } // Fallback - JDK 1.3, or problem under 1.4. convertor = new FileURLConvertor13(); } return convertor.toFile(u); } }