All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openide.util.Utilities Maven / Gradle / Ivy

The newest version!
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
 *
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
 * Other names may be trademarks of their respective owners.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common
 * Development and Distribution License("CDDL") (collectively, the
 * "License"). You may not use this file except in compliance with the
 * License. You can obtain a copy of the License at
 * http://www.netbeans.org/cddl-gplv2.html
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
 * specific language governing permissions and limitations under the
 * License.  When distributing the software, include this License Header
 * Notice in each file and include the License file at
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the GPL Version 2 section of the License file that
 * accompanied this code. If applicable, add the following below the
 * License Header, with the fields enclosed by brackets [] replaced by
 * your own identifying information:
 * "Portions Copyrighted [year] [name of copyright owner]"
 *
 * Contributor(s):
 *
 * The Original Software is NetBeans. The Initial Developer of the Original
 * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
 * Microsystems, Inc. All Rights Reserved.
 *
 * If you wish your version of this file to be governed by only the CDDL
 * or only the GPL Version 2, indicate your decision by adding
 * "[Contributor] elects to include this software in this distribution
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
 * single choice of license, a recipient has the option to distribute
 * your version of this file under either the CDDL, the GPL Version 2 or
 * to extend the choice of license to its licensees as provided above.
 * However, if you add GPL Version 2 code and therefore, elected the GPL
 * Version 2 license, then the option applies only if the new code is
 * made subject to such option by the copyright holder.
 */

package org.openide.util;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Dialog;
import java.awt.Dimension;
import java.awt.Event;
import java.awt.Frame;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.awt.Image;
import java.awt.Insets;
import java.awt.KeyboardFocusManager;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.text.BreakIterator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TreeSet;
import java.util.Vector;
import java.util.WeakHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.lang.model.SourceVersion;
import javax.swing.Action;
import javax.swing.Icon;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JSeparator;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import org.openide.util.actions.Presenter;
import org.openide.util.lookup.Lookups;
import org.openide.util.lookup.implspi.ActiveQueue;
import org.openide.util.actions.ActionPresenterProvider;

/** Otherwise uncategorized useful static methods.
*
* @author Jan Palka, Ian Formanek, Jaroslav Tulach
*/
public final class Utilities {

    private static final Logger LOG = Logger.getLogger(Utilities.class.getName());

    /** Operating system is Windows NT. */
    public static final int OS_WINNT = 1/* << 0*/;

    /** Operating system is Windows 95. */
    public static final int OS_WIN95 = OS_WINNT << 1;

    /** Operating system is Windows 98. */
    public static final int OS_WIN98 = OS_WIN95 << 1;

    /** Operating system is Solaris. */
    public static final int OS_SOLARIS = OS_WIN98 << 1;

    /** Operating system is Linux. */
    public static final int OS_LINUX = OS_SOLARIS << 1;

    /** Operating system is HP-UX. */
    public static final int OS_HP = OS_LINUX << 1;

    /** Operating system is IBM AIX. */
    public static final int OS_AIX = OS_HP << 1;

    /** Operating system is SGI IRIX. */
    public static final int OS_IRIX = OS_AIX << 1;

    /** Operating system is Sun OS. */
    public static final int OS_SUNOS = OS_IRIX << 1;

    /** Operating system is Compaq TRU64 Unix */
    public static final int OS_TRU64 = OS_SUNOS << 1;

    /** @deprecated please use OS_TRU64 instead */
    @Deprecated
    public static final int OS_DEC = OS_TRU64 << 1;

    /** Operating system is OS/2. */
    public static final int OS_OS2 = OS_DEC << 1;

    /** Operating system is Mac. */
    public static final int OS_MAC = OS_OS2 << 1;

    /** Operating system is Windows 2000. */
    public static final int OS_WIN2000 = OS_MAC << 1;

    /** Operating system is Compaq OpenVMS */
    public static final int OS_VMS = OS_WIN2000 << 1;

    /**
     *Operating system is one of the Windows variants but we don't know which
     *one it is
     */
    public static final int OS_WIN_OTHER = OS_VMS << 1;

    /** Operating system is unknown. */
    public static final int OS_OTHER = OS_WIN_OTHER << 1;

    /** Operating system is FreeBSD
     * @since 4.50
     */
    public static final int OS_FREEBSD = OS_OTHER << 1;
    
    /** Operating system is Windows Vista.
     * @since 7.17
     */
    public static final int OS_WINVISTA = OS_FREEBSD << 1;

    /** Operating system is one of the Unix variants but we don't know which
     * one it is.
     * @since 7.18
     */
    public static final int OS_UNIX_OTHER = OS_WINVISTA << 1;

    /** Operating system is OpenBSD.
     * @since 7.18
     */
    public static final int OS_OPENBSD = OS_UNIX_OTHER << 1;

    /** A mask for Windows platforms.
     * @deprecated Use {@link #isWindows()} instead.
     */
    @Deprecated
    public static final int OS_WINDOWS_MASK = OS_WINNT | OS_WIN95 | OS_WIN98 | OS_WIN2000 | OS_WINVISTA | OS_WIN_OTHER;

    /** A mask for Unix platforms.
     * @deprecated Use {@link #isUnix()} instead.
     */
    @Deprecated
    public static final int OS_UNIX_MASK = OS_SOLARIS | OS_LINUX | OS_HP | OS_AIX | OS_IRIX | OS_SUNOS | OS_TRU64 |
        OS_MAC | OS_FREEBSD | OS_OPENBSD | OS_UNIX_OTHER;

    /** 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;

    /** The operating system on which NetBeans runs*/
    private static int operatingSystem = -1;
    private static Timer clearIntrospector;
    private static ActionListener doClear;
    private static final int CTRL_WILDCARD_MASK = 32768;
    private static final int ALT_WILDCARD_MASK = CTRL_WILDCARD_MASK * 2;

    // Package retranslation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    private static final Object TRANS_LOCK = /* FB complains about both "TRANS_LOCK" and new String("TRANS_LOCK") */new Object();

    /** 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;
    
    private static final Map> screenBoundsCache;

    static {
        final boolean cacheEnabled = !GraphicsEnvironment.isHeadless() && isUnix()
                && !isMac() && (System.getProperty( "netbeans.screen.insetsCache", "true" ).equalsIgnoreCase( "true" )); //NOI18N

        if( cacheEnabled ) {
            screenBoundsCache = new WeakHashMap>();
        } else {
            screenBoundsCache = null;
        }
    }

    //
    // Support for work with actions
    //

    /** the found actionsGlobalContext */
    private static Lookup global;

    private Utilities() {
    }

    /**
     * Useful queue for all parts of system that use java.lang.ref.References
     * together with some ReferenceQueue and need to do some clean up
     * when the reference is enqueued. Usually, in order to be notified about that, one
     * needs to either create a dedicated thread that blocks on the queue and is
     * Object.notify-ed, which is the right approach but consumes
     * valuable system resources (threads) or one can periodically check the content
     * of the queue by RequestProcessor.Task.schedule which is
     * completely 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.
     * 

* If you have a reference that needs cleanup, make it implement Runnable * and register it with the queue: *

     * class MyReference extends WeakReference implements Runnable {
     *     private final OtherInfo dataToCleanUp;
     *     public MyReference(Thing ref, OtherInfo data) {
     *         super(ref, Utilities.queue());
     *         dataToCleanUp = data;
     *     }
     *     public void run() {
     *         dataToCleanUp.releaseOrWhateverYouNeed();
     *     }
     * }
     * 
* When the ref object is garbage collected, your run method * will be invoked by calling * ((Runnable) reference).run() * and you can perform whatever cleanup is necessary. Be sure not to block * in such cleanup for a long time as this prevents other waiting references * from cleaning themselves up. *

* Do not call any ReferenceQueue methods. They * will throw exceptions. You may only enqueue a reference. *

* Be sure to call this method anew for each reference. * Do not attempt to cache the return value. * @since 3.11 */ public static ReferenceQueue activeReferenceQueue() { return ActiveQueue.queue(); } /** Get the operating system on which NetBeans is running. * @return one of the OS_* constants (such as {@link #OS_WINNT}) */ public static 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 ("Windows Vista".equals(osName)) { // NOI18N operatingSystem = OS_WINVISTA; } 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 if (osName.toLowerCase(Locale.US).startsWith("freebsd")) { // NOI18N operatingSystem = OS_FREEBSD; } else if ("OpenBSD".equals(osName)) { // NOI18N operatingSystem = OS_OPENBSD; } else if (File.pathSeparatorChar == ':') { // NOI18N operatingSystem = OS_UNIX_OTHER; } else { operatingSystem = OS_OTHER; } } return operatingSystem; } /** Test whether NetBeans is running on some variant of Windows. * @return true if Windows, false if some other manner of operating system */ public static boolean isWindows() { return (getOperatingSystem() & OS_WINDOWS_MASK) != 0; } /** Test whether NetBeans is running on Mac OS X. * @since 7.7 * @return true if Mac, false if some other manner of operating system */ public static boolean isMac() { return (getOperatingSystem() & OS_MAC) != 0; } /** Test whether NetBeans is running on some variant of Unix. * Linux is included as well as the commercial vendors and Mac OS X. * @return true some sort of Unix, false if some other manner of operating system */ public static boolean isUnix() { return (getOperatingSystem() & OS_UNIX_MASK) != 0; } // only for UtilitiesTest purposes static void resetOperatingSystem() { operatingSystem = -1; } /** Test whether a given string is a valid Java identifier. * @param id string which should be checked * @return true if a valid identifier * @see SourceVersion#isIdentifier * @see SourceVersion#isKeyword */ public static boolean isJavaIdentifier(String id) { if (id == null) { return false; } return SourceVersion.isIdentifier(id) && !SourceVersion.isKeyword(id); } /** Central method for obtaining BeanInfo for potential JavaBean classes. * @param clazz class of the bean to provide the BeanInfo 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) { Exceptions.attachMessage(ie, "Encountered while introspecting " + clazz.getName()); // NOI18N throw ie; } catch (Error e) { // Could be a bug in Introspector triggered by NB code. Exceptions.attachMessage(e, "Encountered while introspecting " + clazz.getName()); // 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"); // NOI18N Method setter = Component.class.getDeclaredMethod("setCursor", 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() { @Override public void actionPerformed(ActionEvent ev) { java.beans.Introspector.flushCaches(); } }; clearIntrospector = new Timer(15000, doClear); clearIntrospector.setRepeats(false); } clearIntrospector.restart(); } return bi; } /** Central method for obtaining BeanInfo for potential JavaBean classes, with a stop class. * @param clazz class of the bean to provide the BeanInfo 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 true, the lines are wrapped on word boundaries (if possible); * if false, character boundaries are used * @param removeNewLines if true, 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 */ @Deprecated 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 true, 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 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 true, 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); StringBuilder retBuf = new StringBuilder(); 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 true, the lines are wrapped on word boundaries (if possible); * if false, character boundaries are used * @param removeNewLines if true, 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. */ @Deprecated 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")) { original = original.substring(1); } while (original.endsWith("\n")) { 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; } } } StringBuilder retBuf = new StringBuilder(); for (java.util.Enumeration e = lines.elements(); e.hasMoreElements();) { retBuf.append(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 * @deprecated Use {@link String#replace(CharSequence,CharSequence)} instead */ @Deprecated public static String replaceString(String original, String replaceFrom, String replaceTo) { int index = 0; if ("".equals(replaceFrom)) { return original; // NOI18N } StringBuilder buf = new StringBuilder(); 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. some.pkg.SomeClass$Inner * @return e.g. Inner */ public static 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 true if it does not * @deprecated Obsolete, useless method, no replacement. */ @Deprecated public static 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 nulls * @return the hash code * @see Object#hashCode * @deprecated Use {@link Arrays#hashCode(Object[])} instead. */ @Deprecated 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:
    *
  • both are null *
  • both are arrays with same length and equal items (if the items are arrays, * they are not checked the same way again) *
  • the two objects are {@link Object#equals} *
* This method is null-safe, so if one of the parameters is true and the second not, * it returns false. *

Use {@code java.util.Objects.deepEquals} in JDK 7. * @param o1 the first object to compare * @param o2 the second object to compare * @return true if the objects are equal */ public static boolean compareObjects(Object o1, Object o2) { return compareObjectsImpl(o1, o2, 1); } /** Safe equality check with array recursion. *

Use {@code java.util.Objects.deepEquals} in JDK 7. * @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 true 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. java.lang.String[]. * @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. String[]. * @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 Integer[] would be changed to an int[]. * @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; int k = array.length; for (i = 0; i < k; i++) { r[i] = (((Integer) array[i]) == null) ? 0 : (Integer) array[i]; } return r; } if (array instanceof Boolean[]) { boolean[] r = new boolean[array.length]; int i; int k = array.length; for (i = 0; i < k; i++) { r[i] = (((Boolean) array[i]) == null) ? false : (Boolean) array[i]; } return r; } if (array instanceof Byte[]) { byte[] r = new byte[array.length]; int i; int k = array.length; for (i = 0; i < k; i++) { r[i] = (((Byte) array[i]) == null) ? 0 : (Byte) array[i]; } return r; } if (array instanceof Character[]) { char[] r = new char[array.length]; int i; int k = array.length; for (i = 0; i < k; i++) { r[i] = (((Character) array[i]) == null) ? 0 : (Character) array[i]; } return r; } if (array instanceof Double[]) { double[] r = new double[array.length]; int i; int k = array.length; for (i = 0; i < k; i++) { r[i] = (((Double) array[i]) == null) ? 0 : (Double) array[i]; } return r; } if (array instanceof Float[]) { float[] r = new float[array.length]; int i; int k = array.length; for (i = 0; i < k; i++) { r[i] = (((Float) array[i]) == null) ? 0 : (Float) array[i]; } return r; } if (array instanceof Long[]) { long[] r = new long[array.length]; int i; int k = array.length; for (i = 0; i < k; i++) { r[i] = (((Long) array[i]) == null) ? 0 : (Long) array[i]; } return r; } if (array instanceof Short[]) { short[] r = new short[array.length]; int i; int k = array.length; for (i = 0; i < k; i++) { r[i] = (((Short) array[i]) == null) ? 0 : (Short) array[i]; } return r; } throw new IllegalArgumentException(); } /** * Convert an array of primitive types to an array of objects. * E.g. an int[] would be turned into an Integer[]. * @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; int k = ((int[]) array).length; Integer[] r = new Integer[k]; for (i = 0; i < k; i++) { r[i] = ((int[]) array)[i]; } return r; } if (array instanceof boolean[]) { int i; int k = ((boolean[]) array).length; Boolean[] r = new Boolean[k]; for (i = 0; i < k; i++) { r[i] = ((boolean[]) array)[i]; } return r; } if (array instanceof byte[]) { int i; int k = ((byte[]) array).length; Byte[] r = new Byte[k]; for (i = 0; i < k; i++) { r[i] = ((byte[]) array)[i]; } return r; } if (array instanceof char[]) { int i; int k = ((char[]) array).length; Character[] r = new Character[k]; for (i = 0; i < k; i++) { r[i] = ((char[]) array)[i]; } return r; } if (array instanceof double[]) { int i; int k = ((double[]) array).length; Double[] r = new Double[k]; for (i = 0; i < k; i++) { r[i] = ((double[]) array)[i]; } return r; } if (array instanceof float[]) { int i; int k = ((float[]) array).length; Float[] r = new Float[k]; for (i = 0; i < k; i++) { r[i] = ((float[]) array)[i]; } return r; } if (array instanceof long[]) { int i; int k = ((long[]) array).length; Long[] r = new Long[k]; for (i = 0; i < k; i++) { r[i] = ((long[]) array)[i]; } return r; } if (array instanceof short[]) { int i; int k = ((short[]) array).length; Short[] r = new Short[k]; for (i = 0; i < k; i++) { r[i] = ((short[]) array)[i]; } return r; } throw new IllegalArgumentException(); } /** * Get the object type for given primitive type. * * @param c primitive type (e.g. int) * @return object type (e.g. Integer) */ 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. Integer) * @return primitive type (e.g. int) */ 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 null * @see Component#isFocusTraversable */ public static Component getFocusTraversableComponent(Component c) { if (c.isFocusable()) { return c; } if (!(c instanceof Container)) { return null; } int i; int 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 org.openide.execution.NbProcessDescriptor (Execution API) * you should be able to: *

    *
  • Include command names with embedded spaces, such as c:\Program Files\jdk\bin\javac. *
  • Include extra command arguments, such as -Dname=value. *
  • Do anything else which might require unusual characters or processing. For example: *

        * "c:\program files\jdk\bin\java" -Dmessage="Hello /\\/\\ there!" -Xmx128m
        * 
    *

    This example would create the following executable name and arguments: *

      *
    1. c:\program files\jdk\bin\java *
    2. -Dmessage=Hello /\/\ there! *
    3. -Xmx128m *
    * 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. *
* Caveat: 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; StringBuilder buff = new StringBuilder(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 // private static final class NamesAndValues { final Map keyToString; final Map stringToKey; NamesAndValues(Map keyToString, Map stringToKey) { this.keyToString = keyToString; this.stringToKey = stringToKey; } } private static Reference namesAndValues; private static synchronized NamesAndValues initNameAndValues() { if (namesAndValues != null) { NamesAndValues nav = namesAndValues.get(); if (nav != null) { return nav; } } Field[] fields = KeyEvent.class.getDeclaredFields(); Map names = new HashMap(fields.length * 4 / 3 + 5, 0.75f); Map values = new HashMap(fields.length * 4 / 3 + 5, 0.75f); for (Field f : fields) { if (Modifier.isStatic(f.getModifiers())) { String name = f.getName(); if (name.startsWith("VK_")) { // NOI18N // exclude VK name = name.substring(3); try { int numb = f.getInt(null); names.put(name, numb); values.put(numb, name); } catch (IllegalArgumentException ex) { } catch (IllegalAccessException ex) { } } } } if (names.get("CONTEXT_MENU") == null) { // NOI18N names.put("CONTEXT_MENU", 0x20C); // NOI18N values.put(0x20C, "CONTEXT_MENU"); // NOI18N names.put("WINDOWS", 0x20D); // NOI18N values.put(0x20D, "WINDOWS"); // NOI18N } names.put("MOUSE_WHEEL_UP", 0x290); names.put("MOUSE_WHEEL_DOWN", 0x291); values.put(0x290,"MOUSE_WHEEL_UP"); values.put(0x291,"MOUSE_WHEEL_DOWN"); NamesAndValues nav = new NamesAndValues(values, names); namesAndValues = new SoftReference(nav); return nav; } /** Converts a Swing key stroke descriptor to a familiar Emacs-like name. * @param stroke key description * @return name of the key (e.g. CS-F1 for control-shift-function key one) * @see #stringToKey */ public static String keyToString(KeyStroke stroke) { StringBuilder sb = new StringBuilder(); // add modifiers that must be pressed if (addModifiers(sb, stroke.getModifiers())) { sb.append('-'); } appendRest(sb, stroke); return sb.toString(); } private static void appendRest(StringBuilder sb, KeyStroke stroke) { String c = initNameAndValues().keyToString.get(Integer.valueOf(stroke.getKeyCode())); if (c == null) { sb.append(stroke.getKeyChar()); } else { sb.append(c); } } /** * Converts a Swing key stroke descriptor to a familiar Emacs-like name, * but in a portable way, ie. Meta-C on Mac => D-C * @param stroke key description * @return name of the key (e.g. CS-F1 for control-shift-function key one) * @see #stringToKey */ public static String keyToString(KeyStroke stroke, boolean portable) { if (portable) { StringBuilder sb = new StringBuilder(); // add modifiers that must be pressed if (addModifiersPortable(sb, stroke.getModifiers())) { sb.append('-'); } appendRest(sb, stroke); return sb.toString(); } return keyToString(stroke); } /** Construct a new key description from a given universal string * description. * Provides mapping between Emacs-like textual key descriptions and the * KeyStroke object used in Swing. *

* This format has following form: *

[C][A][S][M]-identifier *

Where: *

    *
  • C stands for the Control key *
  • A stands for the Alt key *
  • S stands for the Shift key *
  • M stands for the Meta key *
* The format also supports two wildcard codes, to support differences in * platforms. These are the preferred choices for registering keystrokes, * since platform conflicts will automatically be handled: *
    *
  • D stands for the default menu accelerator - the Control * key on most platforms, the Command (meta) key on Macintosh
  • *
  • O stands for the alternate accelerator - the Alt key on * most platforms, the Ctrl key on Macintosh (Macintosh uses Alt as a * secondary shift key for composing international characters - if you bind * Alt-8 to an action, a mac user with a French keyboard will not be able * to type the [ character, which is a significant handicap
  • *
* If you use the wildcard characters, and specify a key which will conflict * with keys the operating system consumes, it will be mapped to whichever * choice can work - for example, on Macintosh, Command-Q is always consumed * by the operating system, so D-Q will always map to Control-Q. *

* Every modifier before the hyphen must be pressed. * identifier can be any text constant from {@link KeyEvent} but * without the leading VK_ characters. So {@link KeyEvent#VK_ENTER} is described as * ENTER. * * @param s the string with the description of the key * @return key description object, or null if the string does not represent any valid key */ public static KeyStroke stringToKey(String s) { StringTokenizer st = new StringTokenizer(s.toUpperCase(Locale.ENGLISH), "-", true); // NOI18N int needed = 0; Map names = initNameAndValues().stringToKey; 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 = names.get(el); boolean wildcard = (needed & CTRL_WILDCARD_MASK) != 0; //Strip out the explicit mask - KeyStroke won't know //what to do with it needed = needed & ~CTRL_WILDCARD_MASK; boolean macAlt = (needed & ALT_WILDCARD_MASK) != 0; needed = needed & ~ALT_WILDCARD_MASK; if (i != null) { //#26854 - Default accelerator should be Command on mac if (wildcard) { needed |= getMenuShortcutKeyMask(); if (isMac()) { if (!usableKeyOnMac(i, macAlt ? needed | KeyEvent.CTRL_MASK : needed)) { needed &= ~getMenuShortcutKeyMask(); if (macAlt) { // CTRL will be added by the "if (macAlt) .." branch below needed |= KeyEvent.ALT_MASK; } else { needed |= KeyEvent.CTRL_MASK; } } } } if (macAlt) { if (getOperatingSystem() == OS_MAC) { needed |= KeyEvent.CTRL_MASK; } else { needed |= KeyEvent.ALT_MASK; } } return KeyStroke.getKeyStroke(i, needed); } else { return null; } } } } catch (NoSuchElementException ex) { return null; } } private static boolean usableKeyOnMac(int key, int mask) { //All permutations fail for Q except ctrl if (key == KeyEvent.VK_Q) { return false; } boolean isMeta = ((mask & KeyEvent.META_MASK) != 0) || ((mask & KeyEvent.CTRL_DOWN_MASK) != 0); boolean isAlt = ((mask & KeyEvent.ALT_MASK) != 0) || ((mask & KeyEvent.ALT_DOWN_MASK) != 0); boolean isOnlyMeta = isMeta && ((mask & ~(KeyEvent.META_DOWN_MASK | KeyEvent.META_MASK)) == 0); //Mac OS consumes keys Command+ these keys - the app will never see //them, so CTRL should not be remapped for these if (isOnlyMeta) { return (key != KeyEvent.VK_H) && (key != KeyEvent.VK_SPACE) && (key != KeyEvent.VK_TAB); } if ((key == KeyEvent.VK_D) && isMeta && isAlt) { return false; } if (key == KeyEvent.VK_SPACE && isMeta && ((mask & KeyEvent.CTRL_MASK) != 0)) { // http://lists.apple.com/archives/java-dev/2010/Aug/msg00002.html return false; } return true; } private static int getMenuShortcutKeyMask() { // #152050 - work in headless environment too try { if (!GraphicsEnvironment.isHeadless()) { return Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(); } } catch (Throwable ex) { // OK, just assume we are headless } return Event.CTRL_MASK; } /** 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 null if the string description is not valid * @see #stringToKey */ public static KeyStroke[] stringToKeys(String s) { StringTokenizer st = new StringTokenizer(s.toUpperCase(Locale.ENGLISH), " "); // NOI18N ArrayList arr = new ArrayList(); while (st.hasMoreElements()) { s = st.nextToken(); KeyStroke k = stringToKey(s); if (k == null) { return null; } arr.add(k); } return 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(StringBuilder 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; } if ((modif & CTRL_WILDCARD_MASK) != 0) { buf.append("D"); b = true; } if ((modif & ALT_WILDCARD_MASK) != 0) { buf.append("O"); b = true; } return b; } private static boolean addModifiersPortable(StringBuilder buf, int modifiers) { boolean b = false; if ((modifiers & KeyEvent.SHIFT_MASK) != 0) { buf.append('S'); b = true; } if (Utilities.isMac() && ((modifiers & KeyEvent.META_MASK) != 0) || !Utilities.isMac() && ((modifiers & KeyEvent.CTRL_MASK) != 0)) { buf.append('D'); b = true; } if (Utilities.isMac() && ((modifiers & KeyEvent.CTRL_MASK) != 0) || !Utilities.isMac() && ((modifiers & KeyEvent.ALT_MASK) != 0)) { buf.append('O'); b = true; } // mac alt fallback if (Utilities.isMac() && ((modifiers & KeyEvent.ALT_MASK) != 0)) { buf.append('A'); 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; case 'D': m |= CTRL_WILDCARD_MASK; break; case 'O': m |= ALT_WILDCARD_MASK; break; default: throw new NoSuchElementException(s); } } 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() { Component focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(); if (focusOwner != null) { Window w = SwingUtilities.getWindowAncestor(focusOwner); if (w != null) { return w.getGraphicsConfiguration(); } else { //#217737 - try to find the main window which could be placed in secondary screen for( Frame f : Frame.getFrames() ) { if( "NbMainWindow".equals(f.getName())) { //NOI18N return f.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. * On certain platforms this methods uses a cache to avoid performance degradation due to repeated calls. * This can be disabled by setting the property "-Dnetbeans.screen.insetsCache=false" * See issue http://netbeans.org/bugzilla/show_bug.cgi?id=219507 * * @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(); } if( screenBoundsCache == null ) { return calculateUsableScreenBounds( gconf ); } synchronized( screenBoundsCache ) { Map cacheEntry = screenBoundsCache.get( gconf ); if( cacheEntry != null ) { final long now = System.currentTimeMillis(); Entry entry = cacheEntry.entrySet().iterator().next(); if( entry.getValue() < now + 10000 ) { // cache hit, 10 seconds lifetime return new Rectangle( entry.getKey() ); // return copy } } final Rectangle screenBounds = calculateUsableScreenBounds( gconf ); cacheEntry = new HashMap( 1 ); cacheEntry.put( screenBounds, System.currentTimeMillis() ); if( screenBoundsCache.size() > 20 ) { //maximum entries screenBoundsCache.clear(); } screenBoundsCache.put( gconf, cacheEntry ); return new Rectangle( screenBounds ); } } /** * 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 */ private static Rectangle calculateUsableScreenBounds(GraphicsConfiguration gconf) { 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) { LOG.log(Level.WARNING, null, ex); } } return bounds; } str = System.getProperty("netbeans.taskbar.height"); // NOI18N if (str != null) { bounds.height -= Integer.getInteger(str, 0).intValue(); return bounds; } try { Toolkit toolkit = Toolkit.getDefaultToolkit(); Insets insets = toolkit.getScreenInsets(gconf); //#218895 - invalid screen insets in dual screen setup on Linux if( insets.left > bounds.x && bounds.x > 0 ) insets.left -= bounds.x; if( insets.top > bounds.y && bounds.y > 0 ) insets.top -= bounds.y; bounds.y += insets.top; bounds.x += insets.left; bounds.height -= (insets.top + insets.bottom); bounds.width -= (insets.left + insets.right); } catch (Exception ex) { LOG.log(Level.WARNING, null, ex); } if( bounds.width <= 0 || bounds.height <= 0 ) { bounds = new Rectangle(gconf.getBounds()); } 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 points are subtracted to reflect a presence of the taskbar * * @deprecated this method is almost useless in multiple monitor configuration * * @see #getUsableScreenBounds() * @see #findCenterBounds(Dimension) */ @Deprecated public static Dimension getScreenSize() { Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); if (isWindows() && !Boolean.getBoolean("netbeans.no.taskbar")) { screenSize.height -= TYPICAL_WINDOWS_TASKBAR_HEIGHT; } else if (isMac()) { 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 * @deprecated Not needed in JDK 1.4. * @see FileChooserBuilder */ @Deprecated public static 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 = {javax.swing.JFileChooser.CANCEL_OPTION}; java.awt.event.ActionListener l = new java.awt.event.ActionListener() { @Override public void actionPerformed(java.awt.event.ActionEvent ev) { if (javax.swing.JFileChooser.APPROVE_SELECTION.equals(ev.getActionCommand())) { retValue[0] = javax.swing.JFileChooser.APPROVE_OPTION; } dialog.setVisible(false); dialog.dispose(); } }; chooser.addActionListener(l); dialog.show(); return retValue[0]; } /** 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}. */ @SuppressWarnings({"unchecked", "rawtypes"}) // do not bother, it is deprecated anyway @Deprecated 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> 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.isEmpty())) { // 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. *

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 shall not contain neither keys nor value entries for objects not * in the collection to be sorted, if that happens they will be ignored (since version 7.9). *

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. *

There is a weak 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 not 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 Map<Object,Collection>; * 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 Issue #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); if (r.size() != c.size()) { r.retainAll(c); } 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( T node, Map> edges, Map finished, List r ) { Boolean b = 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 = 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; } /** Provides support for parts of the system that deal with classnames * (use Class.forName, NbObjectInputStream, etc.) or filenames * in layers. *

* 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 Class * later it needs to count with the possibility of rename. *

* 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. *

* To register a mapping from old class names to new ones create a file * META-INF/netbeans/translate.names in your module and fill it * with your mapping: *

     * #
     * # 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
     *
     * # class was removed without replacement
     * org.mypackage.OldClass=
     *
     * 
* Btw. one can use spaces instead of = sign. * For a real world example * check the * * xml module. * *

* For purposes of org.openide.util.io.NbObjectInputStream 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) *

* Similar behaviour applies to filenames provided by layers (system filesystem). Filenames * can be also translated to adapt to location changes e.g. in action registrations. Note that * no spaces or special characters are allowed in both translated filenames or translation * results. Filenames must conform to regexp {@code ^[/a-zA-Z0-9$_.+-]+$}. Keys and values are treated * as paths from fs root. * *

* Example of file path translation (action registration file has moved): *

     * # registration ID has changed
     * Actions/Refactoring/RefactoringWhereUsed.instance=Actions/Refactoring/org-netbeans-modules-refactoring-api-ui-WhereUsedAction.instance
     * 
* * @param className fully qualified name of a class, or file path 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 = 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) { LOG.log(Level.WARNING, null, 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() { @Override public int compare(String[] o1, String[] o2) { String s1 = o1[0]; String s2 = 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 = 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) { LOG.log(Level.WARNING, "Problematic file: {0}", u); LOG.log(Level.WARNING, null, 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; for (String[] pair : list) { 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 need not 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). * @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 * @deprecated Use {@link ImageUtilities#mergeImages}. */ @Deprecated public static Image mergeImages(Image image1, Image image2, int x, int y) { return ImageUtilities.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. * @deprecated Use {@link ImageUtilities#loadImage(java.lang.String)}. */ @Deprecated public static Image loadImage(String resourceID) { return ImageUtilities.loadImage(resourceID); } /** * Converts given icon to a {@link java.awt.Image}. * * @param icon {@link javax.swing.Icon} to be converted. * @since 7.3 * @deprecated Use {@link ImageUtilities#icon2Image}. */ @Deprecated public static Image icon2Image(Icon icon) { return ImageUtilities.icon2Image(icon); } /** Builds a popup menu from actions for provided context specified by * Lookup. * Takes list of actions and for actions whic are instances of * ContextAwareAction 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 JPopupMenu actionsToPopup(Action[] actions, Lookup context) { // keeps actions for which was menu item created already (do not add them twice) Set counted = new HashSet(); // components to be added (separators are null) List components = new ArrayList(); for (Action action : actions) { if (action != null && counted.add(action)) { // switch to replacement action if there is some if (action instanceof ContextAwareAction) { Action contextAwareAction = ((ContextAwareAction) action).createContextAwareInstance(context); if (contextAwareAction == null) { Logger.getLogger(Utilities.class.getName()).log(Level.WARNING,"ContextAwareAction.createContextAwareInstance(context) returns null. That is illegal!" + " action={0}, context={1}", new Object[] {action, context}); } else { action = contextAwareAction; } } JMenuItem item; if (action instanceof Presenter.Popup) { item = ((Presenter.Popup) action).getPopupPresenter(); if (item == null) { Logger.getLogger(Utilities.class.getName()).log(Level.WARNING, "findContextMenuImpl, getPopupPresenter returning null for {0}", action); continue; } } else { // We need to correctly handle mnemonics with '&' etc. item = ActionPresenterProvider.getDefault().createPopupPresenter(action); } for (Component c : ActionPresenterProvider.getDefault().convertComponents(item)) { if (c instanceof JSeparator) { components.add(null); } else { components.add(c); } } } else { components.add(null); } } // Now create actual menu. Strip adjacent, leading, and trailing separators. JPopupMenu menu = ActionPresenterProvider.getDefault().createEmptyPopup(); boolean nonempty = false; // has anything been added yet? boolean pendingSep = false; // should there be a separator before any following item? for (Component c : components) { try { if (c == null) { pendingSep = nonempty; } else { nonempty = true; if (pendingSep) { pendingSep = false; menu.addSeparator(); } menu.add(c); } } catch (RuntimeException ex) { Exceptions.attachMessage(ex, "Current component: " + c); // NOI18N Exceptions.attachMessage(ex, "List of components: " + components); // NOI18N Exceptions.attachMessage(ex, "List of actions: " + Arrays.asList(actions)); // NOI18N Exceptions.printStackTrace(ex); } } return menu; } /** Builds a popup menu for provided component. It retrieves context * (lookup) from provided component instance or one of its parent * (it searches up to the hierarchy for Lookup.Provider instance). * If none of the components is Lookup.Provider 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 actionsToPopup(Action[], Lookup)} is called with * the found Lookup instance, which actually 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); } /** * Load a menu sequence from a lookup path. * Any {@link Action} instances are returned as is; * any {@link JSeparator} instances are translated to nulls. * Warnings are logged for any other instances. * @param path a path as given to {@link Lookups#forPath}, generally a layer folder name * @return a list of actions interspersed with null separators * @since org.openide.util 7.14 */ public static List actionsForPath(String path) { List actions = new ArrayList(); for (Lookup.Item item : Lookups.forPath(path).lookupResult(Object.class).allItems()) { if (Action.class.isAssignableFrom(item.getType())) { Object instance = item.getInstance(); if (instance != null) { actions.add((Action) instance); } } else if (JSeparator.class.isAssignableFrom(item.getType())) { actions.add(null); } else { Logger.getLogger(Utilities.class.getName()).log(Level.WARNING, "Unrecognized object of {0} found in actions path {1}", new Object[] {item.getType(), path}); } } return actions; } /** * Global context for actions. Toolbar, menu or any other "global" * action presenters shall operate in this context. * Presenters for context menu items should not use * this method; instead see {@link ContextAwareAction}. * @see ContextGlobalProvider * @see ContextAwareAction * @see NetBeans FAQ * @return the context for actions * @since 4.10 */ public static Lookup actionsGlobalContext() { synchronized (ContextGlobalProvider.class) { if (global != null) { return global; } } ContextGlobalProvider p = Lookup.getDefault().lookup(ContextGlobalProvider.class); Lookup l = (p == null) ? Lookup.EMPTY : p.createGlobalContext(); synchronized (ContextGlobalProvider.class) { if (global == null) { global = l; } return global; } } // // 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 org/netbeans/modules/foo/resources/foo.gif * might actually find org/netbeans/modules/foo/resources/foo_ja.gif * or org/netbeans/modules/foo/resources/foo_mybranding.gif. * *

Caching of loaded images can be used internally to improve performance. * * @since 3.24 * @deprecated Use {@link ImageUtilities#loadImage(java.lang.String, boolean)}. */ @Deprecated public static Image loadImage(String resource, boolean localized) { return ImageUtilities.loadImage(resource, localized); } /** * 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. * *

Typically you will set the cursor only temporarily: * *

     *  // code is running in other then event dispatch thread
     *  currentComponent.setCursor(Utilities.createProgressCursor(currentComponent));
     *  try {
     *      // perform some work in other than event dispatch thread
     *      // (do not block UI)
     *  } finally {
     *      currentComponent.setCursor(null);
     *  }
     *  
* *

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. Note: The cursor does not have to look native in some * cases on some platforms! * * @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 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 (isMac()) { image = ImageUtilities.loadImage("org/openide/util/progress-cursor-mac.gif"); //NOI18N } else if (isUnix()) { image = ImageUtilities.loadImage("org/openide/util/progress-cursor-motif.gif"); //NOI18N } // All other OS, including Windows, use Windows cursor else { image = ImageUtilities.loadImage("org/openide/util/progress-cursor-win.gif"); //NOI18N } return createCustomCursor(component, image, "PROGRESS_CURSOR"); //NOI18N } // added to fix issue #30665 (bad size on linux) public 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 = ImageUtilities.createBufferedImage(d.width, d.height); i = ImageUtilities.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 AsyncGUIJob 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 */ @SuppressWarnings("ResultOfObjectAllocationIgnored") public static void attachInitJob(Component comp4Init, AsyncGUIJob initJob) { new AsyncInitSupport(comp4Init, initJob); } /** JDK 7 */ private static Method fileToPath, pathToUri, pathsGet, pathToFile; static { try { fileToPath = File.class.getMethod("toPath"); } catch (NoSuchMethodException x) { // fine, JDK 6 } if (fileToPath != null) { try { Class path = Class.forName("java.nio.file.Path"); pathToUri = path.getMethod("toUri"); pathsGet = Class.forName("java.nio.file.Paths").getMethod("get", URI.class); pathToFile = path.getMethod("toFile"); } catch (Exception x) { throw new ExceptionInInitializerError(x); } } } /** * Converts a file to a URI while being safe for UNC paths. * Unlike {@link File#toURI} the result works with {@link URI#normalize()} * and {@link URI#resolve(URI)}. * @param f a file * @return a {@code file}-protocol URI which may use the host field * @see java.nio.file.Path.toUri * @since 8.25 */ public static URI toURI(File f) { if (fileToPath != null) { try { URI u = (URI) pathToUri.invoke(fileToPath.invoke(f)); if (u.toString().startsWith("file:///")) { // #214131 workaround u = new URI(/* "file" */u.getScheme(), /* null */u.getUserInfo(), /* null (!) */u.getHost(), /* -1 */u.getPort(), /* "/..." */u.getPath(), /* null */u.getQuery(), /* null */u.getFragment()); } return u; } catch (Exception x) { LOG.log(Level.FINE, "could not convert " + f + " to URI", x); } } String path = f.getAbsolutePath(); if (path.startsWith("\\\\")) { // UNC if (!path.endsWith("\\") && f.isDirectory()) { path += "\\"; } try { return new URI("file", null, path.replace('\\', '/'), null); } catch (URISyntaxException x) { LOG.log(Level.FINE, "could not convert " + f + " to URI", x); } } return f.toURI(); } /** * Converts a URI to a file while being safe for UNC paths. * Unlike {@link File#File(URI)} UNC URIs with a host field are accepted. * @param u a {@code file}-protocol URI which may use the host field * @return a file * @see java.nio.file.Paths.get(java.net.URI) * @since 8.25 */ public static File toFile(URI u) throws IllegalArgumentException { if (pathsGet != null) { try { return (File) pathToFile.invoke(pathsGet.invoke(null, u)); } catch (Exception x) { LOG.log(Level.FINE, "could not convert " + u + " to File", x); } } String host = u.getHost(); if (host != null && !host.isEmpty() && "file".equals(u.getScheme())) { return new File("\\\\" + host + u.getPath().replace('/', '\\')); } return new File(u); } /** * Convert a file to a matching file: URL. * @param f a file (absolute only) * @return a URL using the file protocol * @throws MalformedURLException for no good reason * @see #toFile * @see Issue #29711 * @since 3.26 * @deprecated Use {@link #toURI} and {@link URI#toURL} instead under JDK 1.4. * ({@link File#toURL} is buggy in JDK 1.3 and the bugs are not fixed in JDK 1.4.) */ @Deprecated public static URL toURL(File f) throws MalformedURLException { if (f == null) { throw new NullPointerException(); } if (!f.isAbsolute()) { throw new IllegalArgumentException("Relative path: " + f); // NOI18N } URI uri = toURI(f); return uri.toURL(); } /** * Convert a file: URL to a matching file. *

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 file protocol * @return an absolute file it points to, or null if the URL * does not seem to point to a file at all * @see #toURL * @see Issue #29711 * @since 3.26 * @deprecated Use {@link URL#toURI} and {@link #toFile(URI)} instead under JDK 1.4. * (There was no proper equivalent under JDK 1.3.) */ @Deprecated public static File toFile(URL u) { if (u == null) { throw new NullPointerException(); } try { URI uri = u.toURI(); return toFile(uri); } catch (URISyntaxException use) { // malformed URL return null; } catch (IllegalArgumentException iae) { // not a file: URL return null; } } /** 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); } /** Exception indicating that a given list could not be partially-ordered. * @see #partialSort * @deprecated Used only by the deprecated partialSort */ @Deprecated @SuppressWarnings("rawtypes") public static class UnorderableException extends RuntimeException { static final long serialVersionUID = 6749951134051806661L; private Collection unorderable; private Map deps; /** 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; } } }