0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * The Original Software is NetBeans. The Initial Developer of the Original
0027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
0028: * Microsystems, Inc. All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: */
0041:
0042: package org.openide.util;
0043:
0044: import java.awt.BorderLayout;
0045: import java.awt.Component;
0046: import java.awt.Container;
0047: import java.awt.Cursor;
0048: import java.awt.Dialog;
0049: import java.awt.Dimension;
0050: import java.awt.Frame;
0051: import java.awt.Graphics;
0052: import java.awt.GraphicsConfiguration;
0053: import java.awt.GraphicsEnvironment;
0054: import java.awt.Image;
0055: import java.awt.Insets;
0056: import java.awt.KeyboardFocusManager;
0057: import java.awt.Point;
0058: import java.awt.Rectangle;
0059: import java.awt.Toolkit;
0060: import java.awt.Window;
0061: import java.awt.event.ActionEvent;
0062: import java.awt.event.ActionListener;
0063: import java.awt.event.KeyEvent;
0064: import java.awt.image.BufferedImage;
0065: import java.io.BufferedReader;
0066: import java.io.File;
0067: import java.io.IOException;
0068: import java.io.InputStreamReader;
0069: import java.lang.ref.Reference;
0070: import java.lang.ref.ReferenceQueue;
0071: import java.lang.ref.SoftReference;
0072: import java.lang.reflect.Field;
0073: import java.lang.reflect.Method;
0074: import java.lang.reflect.Modifier;
0075: import java.net.MalformedURLException;
0076: import java.net.URI;
0077: import java.net.URISyntaxException;
0078: import java.net.URL;
0079: import java.text.BreakIterator;
0080: import java.util.ArrayList;
0081: import java.util.Arrays;
0082: import java.util.Collection;
0083: import java.util.Collections;
0084: import java.util.Comparator;
0085: import java.util.Enumeration;
0086: import java.util.HashMap;
0087: import java.util.HashSet;
0088: import java.util.Iterator;
0089: import java.util.LinkedList;
0090: import java.util.List;
0091: import java.util.Locale;
0092: import java.util.Map;
0093: import java.util.NoSuchElementException;
0094: import java.util.Set;
0095: import java.util.StringTokenizer;
0096: import java.util.TreeSet;
0097: import java.util.Vector;
0098: import java.util.logging.Level;
0099: import java.util.logging.Logger;
0100: import javax.swing.Action;
0101: import javax.swing.Icon;
0102: import javax.swing.ImageIcon;
0103: import javax.swing.JLabel;
0104: import javax.swing.JMenuItem;
0105: import javax.swing.JPopupMenu;
0106: import javax.swing.JSeparator;
0107: import javax.swing.KeyStroke;
0108: import javax.swing.SwingUtilities;
0109: import javax.swing.Timer;
0110: import org.netbeans.modules.openide.util.AWTBridge;
0111: import org.openide.util.actions.Presenter;
0112:
0113: /** Otherwise uncategorized useful static methods.
0114: *
0115: * @author Jan Palka, Ian Formanek, Jaroslav Tulach
0116: */
0117: public final class Utilities {
0118:
0119: private static final Logger LOG = Logger.getLogger(Utilities.class
0120: .getName());
0121:
0122: /** Operating system is Windows NT. */
0123: public static final int OS_WINNT = 1 << 0;
0124:
0125: /** Operating system is Windows 95. */
0126: public static final int OS_WIN95 = OS_WINNT << 1;
0127:
0128: /** Operating system is Windows 98. */
0129: public static final int OS_WIN98 = OS_WIN95 << 1;
0130:
0131: /** Operating system is Solaris. */
0132: public static final int OS_SOLARIS = OS_WIN98 << 1;
0133:
0134: /** Operating system is Linux. */
0135: public static final int OS_LINUX = OS_SOLARIS << 1;
0136:
0137: /** Operating system is HP-UX. */
0138: public static final int OS_HP = OS_LINUX << 1;
0139:
0140: /** Operating system is IBM AIX. */
0141: public static final int OS_AIX = OS_HP << 1;
0142:
0143: /** Operating system is SGI IRIX. */
0144: public static final int OS_IRIX = OS_AIX << 1;
0145:
0146: /** Operating system is Sun OS. */
0147: public static final int OS_SUNOS = OS_IRIX << 1;
0148:
0149: /** Operating system is Compaq TRU64 Unix */
0150: public static final int OS_TRU64 = OS_SUNOS << 1;
0151:
0152: /** @deprecated please use OS_TRU64 instead */
0153: @Deprecated
0154: public static final int OS_DEC = OS_TRU64 << 1;
0155:
0156: /** Operating system is OS/2. */
0157: public static final int OS_OS2 = OS_DEC << 1;
0158:
0159: /** Operating system is Mac. */
0160: public static final int OS_MAC = OS_OS2 << 1;
0161:
0162: /** Operating system is Windows 2000. */
0163: public static final int OS_WIN2000 = OS_MAC << 1;
0164:
0165: /** Operating system is Compaq OpenVMS */
0166: public static final int OS_VMS = OS_WIN2000 << 1;
0167:
0168: /**
0169: *Operating system is one of the Windows variants but we don't know which
0170: *one it is
0171: */
0172: public static final int OS_WIN_OTHER = OS_VMS << 1;
0173:
0174: /** Operating system is unknown. */
0175: public static final int OS_OTHER = OS_WIN_OTHER << 1;
0176:
0177: /** Operating system is FreeBSD
0178: * @since 4.50
0179: */
0180: public static final int OS_FREEBSD = OS_OTHER << 1;
0181:
0182: /** A mask for Windows platforms. */
0183: public static final int OS_WINDOWS_MASK = OS_WINNT | OS_WIN95
0184: | OS_WIN98 | OS_WIN2000 | OS_WIN_OTHER;
0185:
0186: /** A mask for Unix platforms. */
0187: public static final int OS_UNIX_MASK = OS_SOLARIS | OS_LINUX
0188: | OS_HP | OS_AIX | OS_IRIX | OS_SUNOS | OS_TRU64 | OS_MAC
0189: | OS_FREEBSD;
0190:
0191: /** A height of the windows's taskbar */
0192: public static final int TYPICAL_WINDOWS_TASKBAR_HEIGHT = 27;
0193:
0194: /** A height of the Mac OS X's menu */
0195: private static final int TYPICAL_MACOSX_MENU_HEIGHT = 24;
0196:
0197: private static ActiveQueue activeReferenceQueue;
0198:
0199: /** reference to map that maps allowed key names to their values (String, Integer)
0200: and reference to map for mapping of values to their names */
0201: private static Reference<Object> namesAndValues;
0202:
0203: /** The operating system on which NetBeans runs*/
0204: private static int operatingSystem = -1;
0205: private static final String[] keywords = new String[] {
0206:
0207: //If adding to this, insert in alphabetical order!
0208: "abstract", "assert", "boolean", "break", "byte",
0209: "case", //NOI18N
0210: "catch", "char", "class", "const", "continue",
0211: "default", //NOI18N
0212: "do", "double", "else", "enum", "extends", "false",
0213: "final", //NOI18N
0214: "finally", "float", "for", "goto", "if", "implements", //NOI18N
0215: "import", "instanceof", "int", "interface", "long", //NOI18N
0216: "native", "new", "null", "package", "private", //NOI18N
0217: "protected", "public", "return", "short", "static", //NOI18N
0218: "strictfp", "super", "switch", "synchronized", "this", //NOI18N
0219: "throw", "throws", "transient", "true", "try", "void", //NOI18N
0220: "volatile", "while" //NOI18N
0221: };
0222: private static Timer clearIntrospector;
0223: private static ActionListener doClear;
0224: private static final int CTRL_WILDCARD_MASK = 32768;
0225: private static final int ALT_WILDCARD_MASK = CTRL_WILDCARD_MASK * 2;
0226:
0227: // Package retranslation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0228: private static final String TRANS_LOCK = "TRANS_LOCK";
0229:
0230: /** last used classloader or if run in test mode the TRANS_LOCK */
0231: private static Object transLoader;
0232:
0233: /** regular expression to with all changes */
0234: private static RE transExp;
0235:
0236: //
0237: // Support for work with actions
0238: //
0239:
0240: /** type of Class or of an Exception thrown */
0241: private static Object actionClassForPopupMenu;
0242:
0243: /** the found actionsGlobalContext */
0244: private static Lookup global;
0245:
0246: private Utilities() {
0247: }
0248:
0249: /**
0250: * Useful queue for all parts of system that use <code>java.lang.ref.Reference</code>s
0251: * together with some <code>ReferenceQueue</code> and need to do some clean up
0252: * when the reference is enqueued. Usually, in order to be notified about that, one
0253: * needs to either create a dedicated thread that blocks on the queue and is
0254: * <code>Object.notify</code>-ed, which is the right approach but consumes
0255: * valuable system resources (threads) or one can periodically check the content
0256: * of the queue by <code>RequestProcessor.Task.schedule</code> which is
0257: * completely wrong, because it wakes up the system every (say) 15 seconds.
0258: * In order to provide useful support for this problem, this queue has been
0259: * provided.
0260: * <P>
0261: * If you have a reference that needs cleanup, make it implement <link>Runnable</link>
0262: * and register it with the queue:
0263: * <PRE>
0264: * class MyReference extends WeakReference<Thing> implements Runnable {
0265: * private final OtherInfo dataToCleanUp;
0266: * public MyReference(Thing ref, OtherInfo data) {
0267: * super(ref, Utilities.activeReferenceQueue());
0268: * dataToCleanUp = data;
0269: * }
0270: * public void run() {
0271: * dataToCleanUp.releaseOrWhateverYouNeed();
0272: * }
0273: * }
0274: * </PRE>
0275: * When the <code>ref</code> object is garbage collected, your run method
0276: * will be invoked by calling
0277: * <code>((Runnable) reference).run()</code>
0278: * and you can perform whatever cleanup is necessary. Be sure not to block
0279: * in such cleanup for a long time as this prevents other waiting references
0280: * from cleaning themselves up.
0281: * <P>
0282: * Do not call any <code>ReferenceQueue</code> methods. They
0283: * will throw exceptions. You may only enqueue a reference.
0284: * <p>
0285: * Be sure to call this method anew for each reference.
0286: * Do not attempt to cache the return value.
0287: * @since 3.11
0288: */
0289: public static synchronized ReferenceQueue<Object> activeReferenceQueue() {
0290: if (activeReferenceQueue == null) {
0291: activeReferenceQueue = new ActiveQueue(false);
0292: }
0293:
0294: activeReferenceQueue.ping();
0295:
0296: return activeReferenceQueue;
0297: }
0298:
0299: /** Get the operating system on which NetBeans is running.
0300: * @return one of the <code>OS_*</code> constants (such as {@link #OS_WINNT})
0301: */
0302: public static final int getOperatingSystem() {
0303: if (operatingSystem == -1) {
0304: String osName = System.getProperty("os.name");
0305:
0306: if ("Windows NT".equals(osName)) { // NOI18N
0307: operatingSystem = OS_WINNT;
0308: } else if ("Windows 95".equals(osName)) { // NOI18N
0309: operatingSystem = OS_WIN95;
0310: } else if ("Windows 98".equals(osName)) { // NOI18N
0311: operatingSystem = OS_WIN98;
0312: } else if ("Windows 2000".equals(osName)) { // NOI18N
0313: operatingSystem = OS_WIN2000;
0314: } else if (osName.startsWith("Windows ")) { // NOI18N
0315: operatingSystem = OS_WIN_OTHER;
0316: } else if ("Solaris".equals(osName)) { // NOI18N
0317: operatingSystem = OS_SOLARIS;
0318: } else if (osName.startsWith("SunOS")) { // NOI18N
0319: operatingSystem = OS_SOLARIS;
0320: }
0321: // JDK 1.4 b2 defines os.name for me as "Redhat Linux" -jglick
0322: else if (osName.endsWith("Linux")) { // NOI18N
0323: operatingSystem = OS_LINUX;
0324: } else if ("HP-UX".equals(osName)) { // NOI18N
0325: operatingSystem = OS_HP;
0326: } else if ("AIX".equals(osName)) { // NOI18N
0327: operatingSystem = OS_AIX;
0328: } else if ("Irix".equals(osName)) { // NOI18N
0329: operatingSystem = OS_IRIX;
0330: } else if ("SunOS".equals(osName)) { // NOI18N
0331: operatingSystem = OS_SUNOS;
0332: } else if ("Digital UNIX".equals(osName)) { // NOI18N
0333: operatingSystem = OS_TRU64;
0334: } else if ("OS/2".equals(osName)) { // NOI18N
0335: operatingSystem = OS_OS2;
0336: } else if ("OpenVMS".equals(osName)) { // NOI18N
0337: operatingSystem = OS_VMS;
0338: } else if (osName.equals("Mac OS X")) { // NOI18N
0339: operatingSystem = OS_MAC;
0340: } else if (osName.startsWith("Darwin")) { // NOI18N
0341: operatingSystem = OS_MAC;
0342: } else if (osName.toLowerCase(Locale.US).startsWith(
0343: "freebsd")) { // NOI18N
0344: operatingSystem = OS_FREEBSD;
0345: } else {
0346: operatingSystem = OS_OTHER;
0347: }
0348: }
0349:
0350: return operatingSystem;
0351: }
0352:
0353: /** Test whether NetBeans is running on some variant of Windows.
0354: * @return <code>true</code> if Windows, <code>false</code> if some other manner of operating system
0355: */
0356: public static final boolean isWindows() {
0357: return (getOperatingSystem() & OS_WINDOWS_MASK) != 0;
0358: }
0359:
0360: /** Test whether NetBeans is running on MacOS.
0361: * @since 7.7
0362: * @return <code>true</code> if Mac, <code>false</code> if some other manner of operating system
0363: */
0364: public static final boolean isMac() {
0365: return (getOperatingSystem() & OS_MAC) != 0;
0366: }
0367:
0368: /** Test whether NetBeans is running on some variant of Unix.
0369: * Linux is included as well as the commercial vendors.
0370: * @return <code>true</code> some sort of Unix, <code>false</code> if some other manner of operating system
0371: */
0372: public static final boolean isUnix() {
0373: return (getOperatingSystem() & OS_UNIX_MASK) != 0;
0374: }
0375:
0376: // only for UtilitiesTest purposes
0377: final static void resetOperatingSystem() {
0378: operatingSystem = -1;
0379: }
0380:
0381: /** Test whether a given string is a valid Java identifier.
0382: * @param id string which should be checked
0383: * @return <code>true</code> if a valid identifier
0384: */
0385: public static final boolean isJavaIdentifier(String id) {
0386: if (id == null) {
0387: return false;
0388: }
0389:
0390: if (id.equals("")) {
0391: return false;
0392: }
0393:
0394: if (!(java.lang.Character.isJavaIdentifierStart(id.charAt(0)))) {
0395: return false;
0396: }
0397:
0398: for (int i = 1; i < id.length(); i++) {
0399: if (!(java.lang.Character
0400: .isJavaIdentifierPart(id.charAt(i)))) {
0401: return false;
0402: }
0403: }
0404:
0405: return Arrays.binarySearch(keywords, id) < 0;
0406: }
0407:
0408: /** Central method for obtaining <code>BeanInfo</code> for potential JavaBean classes.
0409: * @param clazz class of the bean to provide the <code>BeanInfo</code> for
0410: * @return the bean info
0411: * @throws java.beans.IntrospectionException for the usual reasons
0412: * @see java.beans.Introspector#getBeanInfo(Class)
0413: */
0414: public static java.beans.BeanInfo getBeanInfo(Class clazz)
0415: throws java.beans.IntrospectionException {
0416: java.beans.BeanInfo bi;
0417:
0418: try {
0419: bi = java.beans.Introspector.getBeanInfo(clazz);
0420: } catch (java.beans.IntrospectionException ie) {
0421: Exceptions.attachMessage(ie,
0422: "Encountered while introspecting "
0423: + clazz.getName()); // NOI18N
0424: throw ie;
0425: } catch (Error e) {
0426: // Could be a bug in Introspector triggered by NB code.
0427: Exceptions.attachMessage(e,
0428: "Encountered while introspecting "
0429: + clazz.getName()); // NOI18N
0430: throw e;
0431: }
0432:
0433: if (java.awt.Component.class.isAssignableFrom(clazz)) {
0434: java.beans.PropertyDescriptor[] pds = bi
0435: .getPropertyDescriptors();
0436:
0437: for (int i = 0; i < pds.length; i++) {
0438: if (pds[i].getName().equals("cursor")) { // NOI18N
0439:
0440: try {
0441: Method getter = Component.class
0442: .getDeclaredMethod("getCursor",
0443: new Class[0]); // NOI18N
0444: Method setter = Component.class
0445: .getDeclaredMethod("setCursor",
0446: new Class[] { Cursor.class }); // NOI18N
0447: pds[i] = new java.beans.PropertyDescriptor(
0448: "cursor", getter, setter); // NOI18N
0449: } catch (NoSuchMethodException e) {
0450: e.printStackTrace();
0451: }
0452:
0453: break;
0454: }
0455: }
0456: }
0457:
0458: // clears about 1000 instances of Method
0459: if (bi != null) {
0460: if (clearIntrospector == null) {
0461: doClear = new ActionListener() {
0462: public void actionPerformed(ActionEvent ev) {
0463: java.beans.Introspector.flushCaches();
0464: }
0465: };
0466: clearIntrospector = new Timer(15000, doClear);
0467: clearIntrospector.setRepeats(false);
0468: }
0469:
0470: clearIntrospector.restart();
0471: }
0472:
0473: return bi;
0474: }
0475:
0476: /** Central method for obtaining <code>BeanInfo</code> for potential JavaBean classes, with a stop class.
0477: * @param clazz class of the bean to provide the <code>BeanInfo</code> for
0478: * @param stopClass the stop class
0479: * @return the bean info
0480: * @throws java.beans.IntrospectionException for the usual reasons
0481: * @see java.beans.Introspector#getBeanInfo(Class, Class)
0482: */
0483: public static java.beans.BeanInfo getBeanInfo(Class clazz,
0484: Class stopClass) throws java.beans.IntrospectionException {
0485: return java.beans.Introspector.getBeanInfo(clazz, stopClass);
0486: }
0487:
0488: /** Wrap multi-line strings (and get the individual lines).
0489: * @param original the original string to wrap
0490: * @param width the maximum width of lines
0491: * @param wrapWords if <code>true</code>, the lines are wrapped on word boundaries (if possible);
0492: * if <code>false</code>, character boundaries are used
0493: * @param removeNewLines if <code>true</code>, any newlines in the original string are ignored
0494: * @return the lines after wrapping
0495: * @deprecated use {@link #wrapStringToArray(String, int, BreakIterator, boolean)} since it is better for I18N
0496: */
0497: @Deprecated
0498: public static String[] wrapStringToArray(String original,
0499: int width, boolean wrapWords, boolean removeNewLines) {
0500: BreakIterator bi = (wrapWords ? BreakIterator.getWordInstance()
0501: : BreakIterator.getCharacterInstance());
0502:
0503: return wrapStringToArray(original, width, bi, removeNewLines);
0504: }
0505:
0506: /** Wrap multi-line strings (and get the individual lines).
0507: * @param original the original string to wrap
0508: * @param width the maximum width of lines
0509: * @param breakIterator breaks original to chars, words, sentences, depending on what instance you provide.
0510: * @param removeNewLines if <code>true</code>, any newlines in the original string are ignored
0511: * @return the lines after wrapping
0512: */
0513: public static String[] wrapStringToArray(String original,
0514: int width, BreakIterator breakIterator,
0515: boolean removeNewLines) {
0516: if (original.length() == 0) {
0517: return new String[] { original };
0518: }
0519:
0520: String[] workingSet;
0521:
0522: // substitute original newlines with spaces,
0523: // remove newlines from head and tail
0524: if (removeNewLines) {
0525: original = trimString(original);
0526: original = original.replace('\n', ' ');
0527: workingSet = new String[] { original };
0528: } else {
0529: StringTokenizer tokens = new StringTokenizer(original, "\n"); // NOI18N
0530: int len = tokens.countTokens();
0531: workingSet = new String[len];
0532:
0533: for (int i = 0; i < len; i++) {
0534: workingSet[i] = tokens.nextToken();
0535: }
0536: }
0537:
0538: if (width < 1) {
0539: width = 1;
0540: }
0541:
0542: if (original.length() <= width) {
0543: return workingSet;
0544: }
0545:
0546: widthcheck: {
0547: boolean ok = true;
0548:
0549: for (int i = 0; i < workingSet.length; i++) {
0550: ok = ok && (workingSet[i].length() < width);
0551:
0552: if (!ok) {
0553: break widthcheck;
0554: }
0555: }
0556:
0557: return workingSet;
0558: }
0559:
0560: java.util.ArrayList<String> lines = new java.util.ArrayList<String>();
0561:
0562: int lineStart = 0; // the position of start of currently processed line in the original string
0563:
0564: for (int i = 0; i < workingSet.length; i++) {
0565: if (workingSet[i].length() < width) {
0566: lines.add(workingSet[i]);
0567: } else {
0568: breakIterator.setText(workingSet[i]);
0569:
0570: int nextStart = breakIterator.next();
0571: int prevStart = 0;
0572:
0573: do {
0574: while (((nextStart - lineStart) < width)
0575: && (nextStart != BreakIterator.DONE)) {
0576: prevStart = nextStart;
0577: nextStart = breakIterator.next();
0578: }
0579:
0580: if (nextStart == BreakIterator.DONE) {
0581: nextStart = prevStart = workingSet[i].length();
0582: }
0583:
0584: if (prevStart == 0) {
0585: prevStart = nextStart;
0586: }
0587:
0588: lines.add(workingSet[i].substring(lineStart,
0589: prevStart));
0590:
0591: lineStart = prevStart;
0592: prevStart = 0;
0593: } while (lineStart < workingSet[i].length());
0594:
0595: lineStart = 0;
0596: }
0597: }
0598:
0599: String[] s = new String[lines.size()];
0600:
0601: return lines.toArray(s);
0602: }
0603:
0604: /** trims String
0605: * @param s a String to trim
0606: * @return trimmed String
0607: */
0608: private static String trimString(String s) {
0609: int idx = 0;
0610: char c;
0611: final int slen = s.length();
0612:
0613: if (slen == 0) {
0614: return s;
0615: }
0616:
0617: do {
0618: c = s.charAt(idx++);
0619: } while (((c == '\n') || (c == '\r')) && (idx < slen));
0620:
0621: s = s.substring(--idx);
0622: idx = s.length() - 1;
0623:
0624: if (idx < 0) {
0625: return s;
0626: }
0627:
0628: do {
0629: c = s.charAt(idx--);
0630: } while (((c == '\n') || (c == '\r')) && (idx >= 0));
0631:
0632: return s.substring(0, idx + 2);
0633: }
0634:
0635: /** Wrap multi-line strings.
0636: * @param original the original string to wrap
0637: * @param width the maximum width of lines
0638: * @param breakIterator algorithm for breaking lines
0639: * @param removeNewLines if <code>true</code>, any newlines in the original string are ignored
0640: * @return the whole string with embedded newlines
0641: */
0642: public static String wrapString(String original, int width,
0643: BreakIterator breakIterator, boolean removeNewLines) {
0644: String[] sarray = wrapStringToArray(original, width,
0645: breakIterator, removeNewLines);
0646: StringBuffer retBuf = new StringBuffer();
0647:
0648: for (int i = 0; i < sarray.length; i++) {
0649: retBuf.append(sarray[i]);
0650: retBuf.append('\n');
0651: }
0652:
0653: return retBuf.toString();
0654: }
0655:
0656: /** Wrap multi-line strings.
0657: * @param original the original string to wrap
0658: * @param width the maximum width of lines
0659: * @param wrapWords if <code>true</code>, the lines are wrapped on word boundaries (if possible);
0660: * if <code>false</code>, character boundaries are used
0661: * @param removeNewLines if <code>true</code>, any newlines in the original string are ignored
0662: * @return the whole string with embedded newlines
0663: * @deprecated Use {@link #wrapString (String, int, BreakIterator, boolean)} as it is friendlier to I18N.
0664: */
0665: @Deprecated
0666: public static String wrapString(String original, int width,
0667: boolean wrapWords, boolean removeNewLines) {
0668: // substitute original newlines with spaces,
0669: // remove newlines from head and tail
0670: if (removeNewLines) {
0671: while (original.startsWith("\n"))
0672: // NOI18N
0673:
0674: original = original.substring(1);
0675:
0676: while (original.endsWith("\n"))
0677: // NOI18N
0678:
0679: original = original.substring(0, original.length() - 1);
0680:
0681: original = original.replace('\n', ' ');
0682: }
0683:
0684: if (width < 1) {
0685: width = 1;
0686: }
0687:
0688: if (original.length() <= width) {
0689: return original;
0690: }
0691:
0692: java.util.Vector<String> lines = new java.util.Vector<String>();
0693: int lineStart = 0; // the position of start of currently processed line in the original string
0694: int lastSpacePos = -1;
0695:
0696: for (int i = 0; i < original.length(); i++) {
0697: if (lineStart >= (original.length() - 1)) {
0698: break;
0699: }
0700:
0701: // newline in the original string
0702: if (original.charAt(i) == '\n') {
0703: lines.addElement(original.substring(lineStart, i));
0704: lineStart = i + 1;
0705: lastSpacePos = -1;
0706:
0707: continue;
0708: }
0709:
0710: // remember last space position
0711: if (Character.isSpaceChar(original.charAt(i))) {
0712: lastSpacePos = i;
0713: }
0714:
0715: // last position in the original string
0716: if (i == (original.length() - 1)) {
0717: lines.addElement(original.substring(lineStart));
0718:
0719: break;
0720: }
0721:
0722: // reached width
0723: if ((i - lineStart) == width) {
0724: if (wrapWords && (lastSpacePos != -1)) {
0725: lines.addElement(original.substring(lineStart,
0726: lastSpacePos));
0727: lineStart = lastSpacePos + 1; // the space is consumed for the newline
0728: lastSpacePos = -1;
0729: } else {
0730: lines.addElement(original.substring(lineStart, i));
0731: lineStart = i;
0732: lastSpacePos = -1;
0733: }
0734: }
0735: }
0736:
0737: StringBuffer retBuf = new StringBuffer();
0738:
0739: for (java.util.Enumeration e = lines.elements(); e
0740: .hasMoreElements();) {
0741: retBuf.append((String) e.nextElement());
0742: retBuf.append('\n');
0743: }
0744:
0745: return retBuf.toString();
0746: }
0747:
0748: /** Search-and-replace fixed string matches within a string.
0749: * @param original the original string
0750: * @param replaceFrom the substring to be find
0751: * @param replaceTo the substring to replace it with
0752: * @return a new string with all occurrences replaced
0753: */
0754: public static String replaceString(String original,
0755: String replaceFrom, String replaceTo) {
0756: int index = 0;
0757:
0758: if ("".equals(replaceFrom)) {
0759: return original; // NOI18N
0760: }
0761:
0762: StringBuffer buf = new StringBuffer();
0763:
0764: while (true) {
0765: int pos = original.indexOf(replaceFrom, index);
0766:
0767: if (pos == -1) {
0768: buf.append(original.substring(index));
0769:
0770: return buf.toString();
0771: }
0772:
0773: buf.append(original.substring(index, pos));
0774: buf.append(replaceTo);
0775: index = pos + replaceFrom.length();
0776:
0777: if (index == original.length()) {
0778: return buf.toString();
0779: }
0780: }
0781: }
0782:
0783: /** Turn full name of an inner class into its pure form.
0784: * @param fullName e.g. <code>some.pkg.SomeClass$Inner</code>
0785: * @return e.g. <code>Inner</code>
0786: */
0787: public static final String pureClassName(final String fullName) {
0788: final int index = fullName.indexOf('$');
0789:
0790: if ((index >= 0) && (index < fullName.length())) {
0791: return fullName.substring(index + 1, fullName.length());
0792: }
0793:
0794: return fullName;
0795: }
0796:
0797: /** Test whether the operating system supports icons on frames (windows).
0798: * @return <code>true</code> if it does <em>not</em>
0799: * @deprecated Obsolete, useless method, no replacement.
0800: */
0801: @Deprecated
0802: public static final boolean isLargeFrameIcons() {
0803: return (getOperatingSystem() == OS_SOLARIS)
0804: || (getOperatingSystem() == OS_HP);
0805: }
0806:
0807: /** Compute hash code of array.
0808: * Asks all elements for their own code and composes the
0809: * values.
0810: * @param arr array of objects, can contain <code>null</code>s
0811: * @return the hash code
0812: * @see Object#hashCode
0813: */
0814: public static int arrayHashCode(Object[] arr) {
0815: int c = 0;
0816: int len = arr.length;
0817:
0818: for (int i = 0; i < len; i++) {
0819: Object o = arr[i];
0820: int v = (o == null) ? 1 : o.hashCode();
0821: c += (v ^ i);
0822: }
0823:
0824: return c;
0825: }
0826:
0827: /** Safe equality check.
0828: * The supplied objects are equal if: <UL>
0829: * <LI> both are <code>null</code>
0830: * <LI> both are arrays with same length and equal items (if the items are arrays,
0831: * they are <em>not</em> checked the same way again)
0832: * <LI> the two objects are {@link Object#equals}
0833: * </UL>
0834: * This method is <code>null</code>-safe, so if one of the parameters is true and the second not,
0835: * it returns <code>false</code>.
0836: * @param o1 the first object to compare
0837: * @param o2 the second object to compare
0838: * @return <code>true</code> if the objects are equal
0839: */
0840: public static boolean compareObjects(Object o1, Object o2) {
0841: return compareObjectsImpl(o1, o2, 1);
0842: }
0843:
0844: /** Safe equality check with array recursion.
0845: * @param o1 the first object to compare
0846: * @param o2 the second object to compare
0847: * @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.)
0848: * @return <code>true</code> if the objects are equal
0849: * @see #compareObjects(Object, Object)
0850: */
0851: public static boolean compareObjectsImpl(Object o1, Object o2,
0852: int checkArraysDepth) {
0853: // handle null values
0854: if (o1 == null) {
0855: return (o2 == null);
0856: } else if (o2 == null) {
0857: return false;
0858: }
0859:
0860: // handle arrays
0861: if (checkArraysDepth > 0) {
0862: if ((o1 instanceof Object[]) && (o2 instanceof Object[])) {
0863: // Note: also handles multidimensional arrays of primitive types correctly.
0864: // I.e. new int[0][] instanceof Object[]
0865: Object[] o1a = (Object[]) o1;
0866: Object[] o2a = (Object[]) o2;
0867: int l1 = o1a.length;
0868: int l2 = o2a.length;
0869:
0870: if (l1 != l2) {
0871: return false;
0872: }
0873:
0874: for (int i = 0; i < l1; i++) {
0875: if (!compareObjectsImpl(o1a[i], o2a[i],
0876: checkArraysDepth - 1)) {
0877: return false;
0878: }
0879: }
0880:
0881: return true;
0882: } else if ((o1 instanceof byte[]) && (o2 instanceof byte[])) {
0883: byte[] o1a = (byte[]) o1;
0884: byte[] o2a = (byte[]) o2;
0885: int l1 = o1a.length;
0886: int l2 = o2a.length;
0887:
0888: if (l1 != l2) {
0889: return false;
0890: }
0891:
0892: for (int i = 0; i < l1; i++)
0893: if (o1a[i] != o2a[i]) {
0894: return false;
0895: }
0896:
0897: return true;
0898: } else if ((o1 instanceof short[])
0899: && (o2 instanceof short[])) {
0900: short[] o1a = (short[]) o1;
0901: short[] o2a = (short[]) o2;
0902: int l1 = o1a.length;
0903: int l2 = o2a.length;
0904:
0905: if (l1 != l2) {
0906: return false;
0907: }
0908:
0909: for (int i = 0; i < l1; i++)
0910: if (o1a[i] != o2a[i]) {
0911: return false;
0912: }
0913:
0914: return true;
0915: } else if ((o1 instanceof int[]) && (o2 instanceof int[])) {
0916: int[] o1a = (int[]) o1;
0917: int[] o2a = (int[]) o2;
0918: int l1 = o1a.length;
0919: int l2 = o2a.length;
0920:
0921: if (l1 != l2) {
0922: return false;
0923: }
0924:
0925: for (int i = 0; i < l1; i++)
0926: if (o1a[i] != o2a[i]) {
0927: return false;
0928: }
0929:
0930: return true;
0931: } else if ((o1 instanceof long[]) && (o2 instanceof long[])) {
0932: long[] o1a = (long[]) o1;
0933: long[] o2a = (long[]) o2;
0934: int l1 = o1a.length;
0935: int l2 = o2a.length;
0936:
0937: if (l1 != l2) {
0938: return false;
0939: }
0940:
0941: for (int i = 0; i < l1; i++)
0942: if (o1a[i] != o2a[i]) {
0943: return false;
0944: }
0945:
0946: return true;
0947: } else if ((o1 instanceof float[])
0948: && (o2 instanceof float[])) {
0949: float[] o1a = (float[]) o1;
0950: float[] o2a = (float[]) o2;
0951: int l1 = o1a.length;
0952: int l2 = o2a.length;
0953:
0954: if (l1 != l2) {
0955: return false;
0956: }
0957:
0958: for (int i = 0; i < l1; i++)
0959: if (o1a[i] != o2a[i]) {
0960: return false;
0961: }
0962:
0963: return true;
0964: } else if ((o1 instanceof double[])
0965: && (o2 instanceof double[])) {
0966: double[] o1a = (double[]) o1;
0967: double[] o2a = (double[]) o2;
0968: int l1 = o1a.length;
0969: int l2 = o2a.length;
0970:
0971: if (l1 != l2) {
0972: return false;
0973: }
0974:
0975: for (int i = 0; i < l1; i++)
0976: if (o1a[i] != o2a[i]) {
0977: return false;
0978: }
0979:
0980: return true;
0981: } else if ((o1 instanceof char[]) && (o2 instanceof char[])) {
0982: char[] o1a = (char[]) o1;
0983: char[] o2a = (char[]) o2;
0984: int l1 = o1a.length;
0985: int l2 = o2a.length;
0986:
0987: if (l1 != l2) {
0988: return false;
0989: }
0990:
0991: for (int i = 0; i < l1; i++)
0992: if (o1a[i] != o2a[i]) {
0993: return false;
0994: }
0995:
0996: return true;
0997: } else if ((o1 instanceof boolean[])
0998: && (o2 instanceof boolean[])) {
0999: boolean[] o1a = (boolean[]) o1;
1000: boolean[] o2a = (boolean[]) o2;
1001: int l1 = o1a.length;
1002: int l2 = o2a.length;
1003:
1004: if (l1 != l2) {
1005: return false;
1006: }
1007:
1008: for (int i = 0; i < l1; i++)
1009: if (o1a[i] != o2a[i]) {
1010: return false;
1011: }
1012:
1013: return true;
1014: }
1015:
1016: // else not array type
1017: }
1018:
1019: // handle common objects--non-arrays, or arrays when depth == 0
1020: return o1.equals(o2);
1021: }
1022:
1023: /** Assemble a human-presentable class name for a specified class.
1024: * Arrays are represented as e.g. <code>java.lang.String[]</code>.
1025: * @param clazz the class to name
1026: * @return the human-presentable name
1027: */
1028: public static String getClassName(Class clazz) {
1029: // if it is an array, get short name of element type and append []
1030: if (clazz.isArray()) {
1031: return getClassName(clazz.getComponentType()) + "[]"; // NOI18N
1032: } else {
1033: return clazz.getName();
1034: }
1035: }
1036:
1037: /** Assemble a human-presentable class name for a specified class (omitting the package).
1038: * Arrays are represented as e.g. <code>String[]</code>.
1039: * @param clazz the class to name
1040: * @return the human-presentable name
1041: */
1042: public static String getShortClassName(Class clazz) {
1043: // if it is an array, get short name of element type and append []
1044: if (clazz.isArray()) {
1045: return getShortClassName(clazz.getComponentType()) + "[]"; // NOI18N
1046: }
1047:
1048: String name = clazz.getName().replace('$', '.');
1049:
1050: return name.substring(name.lastIndexOf(".") + 1, name.length()); // NOI18N
1051: }
1052:
1053: /**
1054: * Convert an array of objects to an array of primitive types.
1055: * E.g. an <code>Integer[]</code> would be changed to an <code>int[]</code>.
1056: * @param array the wrapper array
1057: * @return a primitive array
1058: * @throws IllegalArgumentException if the array element type is not a primitive wrapper
1059: */
1060: public static Object toPrimitiveArray(Object[] array) {
1061: if (array instanceof Integer[]) {
1062: int[] r = new int[array.length];
1063: int i;
1064: int k = array.length;
1065:
1066: for (i = 0; i < k; i++)
1067: r[i] = (((Integer) array[i]) == null) ? 0
1068: : ((Integer) array[i]).intValue();
1069:
1070: return r;
1071: }
1072:
1073: if (array instanceof Boolean[]) {
1074: boolean[] r = new boolean[array.length];
1075: int i;
1076: int k = array.length;
1077:
1078: for (i = 0; i < k; i++)
1079: r[i] = (((Boolean) array[i]) == null) ? false
1080: : ((Boolean) array[i]).booleanValue();
1081:
1082: return r;
1083: }
1084:
1085: if (array instanceof Byte[]) {
1086: byte[] r = new byte[array.length];
1087: int i;
1088: int k = array.length;
1089:
1090: for (i = 0; i < k; i++)
1091: r[i] = (((Byte) array[i]) == null) ? 0
1092: : ((Byte) array[i]).byteValue();
1093:
1094: return r;
1095: }
1096:
1097: if (array instanceof Character[]) {
1098: char[] r = new char[array.length];
1099: int i;
1100: int k = array.length;
1101:
1102: for (i = 0; i < k; i++)
1103: r[i] = (((Character) array[i]) == null) ? 0
1104: : ((Character) array[i]).charValue();
1105:
1106: return r;
1107: }
1108:
1109: if (array instanceof Double[]) {
1110: double[] r = new double[array.length];
1111: int i;
1112: int k = array.length;
1113:
1114: for (i = 0; i < k; i++)
1115: r[i] = (((Double) array[i]) == null) ? 0
1116: : ((Double) array[i]).doubleValue();
1117:
1118: return r;
1119: }
1120:
1121: if (array instanceof Float[]) {
1122: float[] r = new float[array.length];
1123: int i;
1124: int k = array.length;
1125:
1126: for (i = 0; i < k; i++)
1127: r[i] = (((Float) array[i]) == null) ? 0
1128: : ((Float) array[i]).floatValue();
1129:
1130: return r;
1131: }
1132:
1133: if (array instanceof Long[]) {
1134: long[] r = new long[array.length];
1135: int i;
1136: int k = array.length;
1137:
1138: for (i = 0; i < k; i++)
1139: r[i] = (((Long) array[i]) == null) ? 0
1140: : ((Long) array[i]).longValue();
1141:
1142: return r;
1143: }
1144:
1145: if (array instanceof Short[]) {
1146: short[] r = new short[array.length];
1147: int i;
1148: int k = array.length;
1149:
1150: for (i = 0; i < k; i++)
1151: r[i] = (((Short) array[i]) == null) ? 0
1152: : ((Short) array[i]).shortValue();
1153:
1154: return r;
1155: }
1156:
1157: throw new IllegalArgumentException();
1158: }
1159:
1160: /**
1161: * Convert an array of primitive types to an array of objects.
1162: * E.g. an <code>int[]</code> would be turned into an <code>Integer[]</code>.
1163: * @param array the primitive array
1164: * @return a wrapper array
1165: * @throws IllegalArgumentException if the array element type is not primitive
1166: */
1167: public static Object[] toObjectArray(Object array) {
1168: if (array instanceof Object[]) {
1169: return (Object[]) array;
1170: }
1171:
1172: if (array instanceof int[]) {
1173: int i;
1174: int k = ((int[]) array).length;
1175: Integer[] r = new Integer[k];
1176:
1177: for (i = 0; i < k; i++)
1178: r[i] = new Integer(((int[]) array)[i]);
1179:
1180: return r;
1181: }
1182:
1183: if (array instanceof boolean[]) {
1184: int i;
1185: int k = ((boolean[]) array).length;
1186: Boolean[] r = new Boolean[k];
1187:
1188: for (i = 0; i < k; i++)
1189: r[i] = ((boolean[]) array)[i] ? Boolean.TRUE
1190: : Boolean.FALSE;
1191:
1192: return r;
1193: }
1194:
1195: if (array instanceof byte[]) {
1196: int i;
1197: int k = ((byte[]) array).length;
1198: Byte[] r = new Byte[k];
1199:
1200: for (i = 0; i < k; i++)
1201: r[i] = new Byte(((byte[]) array)[i]);
1202:
1203: return r;
1204: }
1205:
1206: if (array instanceof char[]) {
1207: int i;
1208: int k = ((char[]) array).length;
1209: Character[] r = new Character[k];
1210:
1211: for (i = 0; i < k; i++)
1212: r[i] = new Character(((char[]) array)[i]);
1213:
1214: return r;
1215: }
1216:
1217: if (array instanceof double[]) {
1218: int i;
1219: int k = ((double[]) array).length;
1220: Double[] r = new Double[k];
1221:
1222: for (i = 0; i < k; i++)
1223: r[i] = new Double(((double[]) array)[i]);
1224:
1225: return r;
1226: }
1227:
1228: if (array instanceof float[]) {
1229: int i;
1230: int k = ((float[]) array).length;
1231: Float[] r = new Float[k];
1232:
1233: for (i = 0; i < k; i++)
1234: r[i] = new Float(((float[]) array)[i]);
1235:
1236: return r;
1237: }
1238:
1239: if (array instanceof long[]) {
1240: int i;
1241: int k = ((long[]) array).length;
1242: Long[] r = new Long[k];
1243:
1244: for (i = 0; i < k; i++)
1245: r[i] = new Long(((long[]) array)[i]);
1246:
1247: return r;
1248: }
1249:
1250: if (array instanceof short[]) {
1251: int i;
1252: int k = ((short[]) array).length;
1253: Short[] r = new Short[k];
1254:
1255: for (i = 0; i < k; i++)
1256: r[i] = new Short(((short[]) array)[i]);
1257:
1258: return r;
1259: }
1260:
1261: throw new IllegalArgumentException();
1262: }
1263:
1264: /**
1265: * Get the object type for given primitive type.
1266: *
1267: * @param c primitive type (e.g. <code>int</code>)
1268: * @return object type (e.g. <code>Integer</code>)
1269: */
1270: public static Class getObjectType(Class c) {
1271: if (!c.isPrimitive()) {
1272: return c;
1273: }
1274:
1275: if (c == Integer.TYPE) {
1276: return Integer.class;
1277: }
1278:
1279: if (c == Boolean.TYPE) {
1280: return Boolean.class;
1281: }
1282:
1283: if (c == Byte.TYPE) {
1284: return Byte.class;
1285: }
1286:
1287: if (c == Character.TYPE) {
1288: return Character.class;
1289: }
1290:
1291: if (c == Double.TYPE) {
1292: return Double.class;
1293: }
1294:
1295: if (c == Float.TYPE) {
1296: return Float.class;
1297: }
1298:
1299: if (c == Long.TYPE) {
1300: return Long.class;
1301: }
1302:
1303: if (c == Short.TYPE) {
1304: return Short.class;
1305: }
1306:
1307: throw new IllegalArgumentException();
1308: }
1309:
1310: /**
1311: * Get the primitive type for given object type.
1312: *
1313: * @param c object type (e.g. <code>Integer</code>)
1314: * @return primitive type (e.g. <code>int</code>)
1315: */
1316: public static Class getPrimitiveType(Class c) {
1317: if (!c.isPrimitive()) {
1318: return c;
1319: }
1320:
1321: if (c == Integer.class) {
1322: return Integer.TYPE;
1323: }
1324:
1325: if (c == Boolean.class) {
1326: return Boolean.TYPE;
1327: }
1328:
1329: if (c == Byte.class) {
1330: return Byte.TYPE;
1331: }
1332:
1333: if (c == Character.class) {
1334: return Character.TYPE;
1335: }
1336:
1337: if (c == Double.class) {
1338: return Double.TYPE;
1339: }
1340:
1341: if (c == Float.class) {
1342: return Float.TYPE;
1343: }
1344:
1345: if (c == Long.class) {
1346: return Long.TYPE;
1347: }
1348:
1349: if (c == Short.class) {
1350: return Short.TYPE;
1351: }
1352:
1353: throw new IllegalArgumentException();
1354: }
1355:
1356: /** Find a focus-traverable component.
1357: * @param c the component to look in
1358: * @return the same component if traversable, else a child component if present, else <code>null</code>
1359: * @see Component#isFocusTraversable
1360: */
1361: public static Component getFocusTraversableComponent(Component c) {
1362: if (c.isFocusable()) {
1363: return c;
1364: }
1365:
1366: if (!(c instanceof Container)) {
1367: return null;
1368: }
1369:
1370: int i;
1371: int k = ((Container) c).getComponentCount();
1372:
1373: for (i = 0; i < k; i++) {
1374: Component v = ((Container) c).getComponent(i);
1375:
1376: if (v != null) {
1377: return v;
1378: }
1379: }
1380:
1381: return null;
1382: }
1383:
1384: /** Parses parameters from a given string in shell-like manner.
1385: * Users of the Bourne shell (e.g. on Unix) will already be familiar with the behavior.
1386: * For example, when using <code>org.openide.execution.NbProcessDescriptor</code> (Execution API)
1387: * you should be able to:
1388: * <ul>
1389: * <li>Include command names with embedded spaces, such as <code>c:\Program Files\jdk\bin\javac</code>.
1390: * <li>Include extra command arguments, such as <code>-Dname=value</code>.
1391: * <li>Do anything else which might require unusual characters or processing. For example:
1392: * <p><code><pre>
1393: * "c:\program files\jdk\bin\java" -Dmessage="Hello /\\/\\ there!" -Xmx128m
1394: * </pre></code>
1395: * <p>This example would create the following executable name and arguments:
1396: * <ol>
1397: * <li> <code>c:\program files\jdk\bin\java</code>
1398: * <li> <code>-Dmessage=Hello /\/\ there!</code>
1399: * <li> <code>-Xmx128m</code>
1400: * </ol>
1401: * Note that the command string does not escape its backslashes--under the assumption
1402: * that Windows users will not think to do this, meaningless escapes are just left
1403: * as backslashes plus following character.
1404: * </ul>
1405: * <em>Caveat</em>: even after parsing, Windows programs (such as the Java launcher)
1406: * may not fully honor certain
1407: * characters, such as quotes, in command names or arguments. This is because programs
1408: * under Windows frequently perform their own parsing and unescaping (since the shell
1409: * cannot be relied on to do this). On Unix, this problem should not occur.
1410: * @param s a string to parse
1411: * @return an array of parameters
1412: */
1413: public static String[] parseParameters(String s) {
1414: int NULL = 0x0; // STICK + whitespace or NULL + non_"
1415: int INPARAM = 0x1; // NULL + " or STICK + " or INPARAMPENDING + "\ // NOI18N
1416: int INPARAMPENDING = 0x2; // INPARAM + \
1417: int STICK = 0x4; // INPARAM + " or STICK + non_" // NOI18N
1418: int STICKPENDING = 0x8; // STICK + \
1419: Vector<String> params = new Vector<String>(5, 5);
1420: char c;
1421:
1422: int state = NULL;
1423: StringBuffer buff = new StringBuffer(20);
1424: int slength = s.length();
1425:
1426: for (int i = 0; i < slength; i++) {
1427: c = s.charAt(i);
1428:
1429: if (Character.isWhitespace(c)) {
1430: if (state == NULL) {
1431: if (buff.length() > 0) {
1432: params.addElement(buff.toString());
1433: buff.setLength(0);
1434: }
1435: } else if (state == STICK) {
1436: params.addElement(buff.toString());
1437: buff.setLength(0);
1438: state = NULL;
1439: } else if (state == STICKPENDING) {
1440: buff.append('\\');
1441: params.addElement(buff.toString());
1442: buff.setLength(0);
1443: state = NULL;
1444: } else if (state == INPARAMPENDING) {
1445: state = INPARAM;
1446: buff.append('\\');
1447: buff.append(c);
1448: } else { // INPARAM
1449: buff.append(c);
1450: }
1451:
1452: continue;
1453: }
1454:
1455: if (c == '\\') {
1456: if (state == NULL) {
1457: ++i;
1458:
1459: if (i < slength) {
1460: char cc = s.charAt(i);
1461:
1462: if ((cc == '"') || (cc == '\\')) {
1463: buff.append(cc);
1464: } else if (Character.isWhitespace(cc)) {
1465: buff.append(c);
1466: --i;
1467: } else {
1468: buff.append(c);
1469: buff.append(cc);
1470: }
1471: } else {
1472: buff.append('\\');
1473:
1474: break;
1475: }
1476:
1477: continue;
1478: } else if (state == INPARAM) {
1479: state = INPARAMPENDING;
1480: } else if (state == INPARAMPENDING) {
1481: buff.append('\\');
1482: state = INPARAM;
1483: } else if (state == STICK) {
1484: state = STICKPENDING;
1485: } else if (state == STICKPENDING) {
1486: buff.append('\\');
1487: state = STICK;
1488: }
1489:
1490: continue;
1491: }
1492:
1493: if (c == '"') {
1494: if (state == NULL) {
1495: state = INPARAM;
1496: } else if (state == INPARAM) {
1497: state = STICK;
1498: } else if (state == STICK) {
1499: state = INPARAM;
1500: } else if (state == STICKPENDING) {
1501: buff.append('"');
1502: state = STICK;
1503: } else { // INPARAMPENDING
1504: buff.append('"');
1505: state = INPARAM;
1506: }
1507:
1508: continue;
1509: }
1510:
1511: if (state == INPARAMPENDING) {
1512: buff.append('\\');
1513: state = INPARAM;
1514: } else if (state == STICKPENDING) {
1515: buff.append('\\');
1516: state = STICK;
1517: }
1518:
1519: buff.append(c);
1520: }
1521:
1522: // collect
1523: if (state == INPARAM) {
1524: params.addElement(buff.toString());
1525: } else if ((state & (INPARAMPENDING | STICKPENDING)) != 0) {
1526: buff.append('\\');
1527: params.addElement(buff.toString());
1528: } else { // NULL or STICK
1529:
1530: if (buff.length() != 0) {
1531: params.addElement(buff.toString());
1532: }
1533: }
1534:
1535: String[] ret = new String[params.size()];
1536: params.copyInto(ret);
1537:
1538: return ret;
1539: }
1540:
1541: /** Complementary method to parseParameters
1542: * @see #parseParameters
1543: */
1544: public static String escapeParameters(String[] params) {
1545: StringBuffer sb = new StringBuffer();
1546:
1547: for (int i = 0; i < params.length; i++) {
1548: escapeString(params[i], sb);
1549: sb.append(' ');
1550: }
1551:
1552: final int len = sb.length();
1553:
1554: if (len > 0) {
1555: sb.setLength(len - 1);
1556: }
1557:
1558: return sb.toString().trim();
1559: }
1560:
1561: /** Escapes one string
1562: * @see #escapeParameters
1563: */
1564: private static void escapeString(String s, StringBuffer sb) {
1565: if (s.length() == 0) {
1566: sb.append("\"\"");
1567:
1568: return;
1569: }
1570:
1571: boolean hasSpace = false;
1572: final int sz = sb.length();
1573: final int slen = s.length();
1574: char c;
1575:
1576: for (int i = 0; i < slen; i++) {
1577: c = s.charAt(i);
1578:
1579: if (Character.isWhitespace(c)) {
1580: hasSpace = true;
1581: sb.append(c);
1582:
1583: continue;
1584: }
1585:
1586: if (c == '\\') {
1587: sb.append('\\').append('\\');
1588:
1589: continue;
1590: }
1591:
1592: if (c == '"') {
1593: sb.append('\\').append('"');
1594:
1595: continue;
1596: }
1597:
1598: sb.append(c);
1599: }
1600:
1601: if (hasSpace) {
1602: sb.insert(sz, '"');
1603: sb.append('"');
1604: }
1605: }
1606:
1607: //
1608: // Key conversions
1609: //
1610:
1611: /** Initialization of the names and values
1612: * @return array of two hashmaps first maps
1613: * allowed key names to their values (String, Integer)
1614: * and second
1615: * hashtable for mapping of values to their names (Integer, String)
1616: */
1617: private static synchronized HashMap[] initNameAndValues() {
1618: if (namesAndValues != null) {
1619: HashMap[] arr = (HashMap[]) namesAndValues.get();
1620:
1621: if (arr != null) {
1622: return arr;
1623: }
1624: }
1625:
1626: Field[] fields = KeyEvent.class.getDeclaredFields();
1627:
1628: HashMap<String, Integer> names = new HashMap<String, Integer>(
1629: ((fields.length * 4) / 3) + 5, 0.75f);
1630: HashMap<Integer, String> values = new HashMap<Integer, String>(
1631: ((fields.length * 4) / 3) + 5, 0.75f);
1632:
1633: for (int i = 0; i < fields.length; i++) {
1634: if (Modifier.isStatic(fields[i].getModifiers())) {
1635: String name = fields[i].getName();
1636:
1637: if (name.startsWith("VK_")) { // NOI18N
1638:
1639: // exclude VK
1640: name = name.substring(3);
1641:
1642: try {
1643: int numb = fields[i].getInt(null);
1644: Integer value = new Integer(numb);
1645: names.put(name, value);
1646: values.put(value, name);
1647: } catch (IllegalArgumentException ex) {
1648: } catch (IllegalAccessException ex) {
1649: }
1650: }
1651: }
1652: }
1653:
1654: if (names.get("CONTEXT_MENU") == null) { // NOI18N
1655:
1656: Integer n = new Integer(0x20C);
1657: names.put("CONTEXT_MENU", n); // NOI18N
1658: values.put(n, "CONTEXT_MENU"); // NOI18N
1659:
1660: n = new Integer(0x20D);
1661: names.put("WINDOWS", n); // NOI18N
1662: values.put(n, "WINDOWS"); // NOI18N
1663: }
1664:
1665: HashMap[] arr = { names, values };
1666:
1667: namesAndValues = new SoftReference<Object>(arr);
1668:
1669: return arr;
1670: }
1671:
1672: /** Converts a Swing key stroke descriptor to a familiar Emacs-like name.
1673: * @param stroke key description
1674: * @return name of the key (e.g. <code>CS-F1</code> for control-shift-function key one)
1675: * @see #stringToKey
1676: */
1677: public static String keyToString(KeyStroke stroke) {
1678: StringBuilder sb = new StringBuilder();
1679:
1680: // add modifiers that must be pressed
1681: if (addModifiers(sb, stroke.getModifiers())) {
1682: sb.append('-');
1683: }
1684:
1685: HashMap[] namesAndValues = initNameAndValues();
1686:
1687: String c = (String) namesAndValues[1].get(Integer
1688: .valueOf(stroke.getKeyCode()));
1689:
1690: if (c == null) {
1691: sb.append(stroke.getKeyChar());
1692: } else {
1693: sb.append(c);
1694: }
1695:
1696: return sb.toString();
1697: }
1698:
1699: /** Construct a new key description from a given universal string
1700: * description.
1701: * Provides mapping between Emacs-like textual key descriptions and the
1702: * <code>KeyStroke</code> object used in Swing.
1703: * <P>
1704: * This format has following form:
1705: * <P><code>[C][A][S][M]-<em>identifier</em></code>
1706: * <p>Where:
1707: * <UL>
1708: * <LI> <code>C</code> stands for the Control key
1709: * <LI> <code>A</code> stands for the Alt key
1710: * <LI> <code>S</code> stands for the Shift key
1711: * <LI> <code>M</code> stands for the Meta key
1712: * </UL>
1713: * The format also supports two wildcard codes, to support differences in
1714: * platforms. These are the preferred choices for registering keystrokes,
1715: * since platform conflicts will automatically be handled:
1716: * <UL>
1717: * <LI> <code>D</code> stands for the default menu accelerator - the Control
1718: * key on most platforms, the Command (meta) key on Macintosh</LI>
1719: * <LI> <code>O</code> stands for the alternate accelerator - the Alt key on
1720: * most platforms, the Ctrl key on Macintosh (Macintosh uses Alt as a
1721: * secondary shift key for composing international characters - if you bind
1722: * Alt-8 to an action, a mac user with a French keyboard will not be able
1723: * to type the <code>[</code> character, which is a significant handicap</LI>
1724: * </UL>
1725: * If you use the wildcard characters, and specify a key which will conflict
1726: * with keys the operating system consumes, it will be mapped to whichever
1727: * choice can work - for example, on Macintosh, Command-Q is always consumed
1728: * by the operating system, so <code>D-Q</code> will always map to Control-Q.
1729: * <p>
1730: * Every modifier before the hyphen must be pressed.
1731: * <em>identifier</EM> can be any text constant from {@link KeyEvent} but
1732: * without the leading <code>VK_</code> characters. So {@link KeyEvent#VK_ENTER} is described as
1733: * <code>ENTER</code>.
1734: *
1735: * @param s the string with the description of the key
1736: * @return key description object, or <code>null</code> if the string does not represent any valid key
1737: */
1738: public static KeyStroke stringToKey(String s) {
1739: StringTokenizer st = new StringTokenizer(s
1740: .toUpperCase(Locale.ENGLISH), "-", true); // NOI18N
1741:
1742: int needed = 0;
1743:
1744: HashMap names = initNameAndValues()[0];
1745:
1746: int lastModif = -1;
1747:
1748: try {
1749: for (;;) {
1750: String el = st.nextToken();
1751:
1752: // required key
1753: if (el.equals("-")) { // NOI18N
1754:
1755: if (lastModif != -1) {
1756: needed |= lastModif;
1757: lastModif = -1;
1758: }
1759:
1760: continue;
1761: }
1762:
1763: // if there is more elements
1764: if (st.hasMoreElements()) {
1765: // the text should describe modifiers
1766: lastModif = readModifiers(el);
1767: } else {
1768: // last text must be the key code
1769: Integer i = (Integer) names.get(el);
1770: boolean wildcard = (needed & CTRL_WILDCARD_MASK) != 0;
1771:
1772: //Strip out the explicit mask - KeyStroke won't know
1773: //what to do with it
1774: needed = needed & ~CTRL_WILDCARD_MASK;
1775:
1776: boolean macAlt = (needed & ALT_WILDCARD_MASK) != 0;
1777: needed = needed & ~ALT_WILDCARD_MASK;
1778:
1779: if (i != null) {
1780: //#26854 - Default accelerator should be Command on mac
1781: if (wildcard) {
1782: needed |= Toolkit.getDefaultToolkit()
1783: .getMenuShortcutKeyMask();
1784:
1785: if (isMac()) {
1786: if (!usableKeyOnMac(i.intValue(),
1787: needed)) {
1788: needed &= ~Toolkit
1789: .getDefaultToolkit()
1790: .getMenuShortcutKeyMask();
1791: needed |= KeyEvent.CTRL_MASK;
1792: }
1793: }
1794: }
1795:
1796: if (macAlt) {
1797: if (getOperatingSystem() == OS_MAC) {
1798: needed |= KeyEvent.CTRL_MASK;
1799: } else {
1800: needed |= KeyEvent.ALT_MASK;
1801: }
1802: }
1803:
1804: return KeyStroke.getKeyStroke(i.intValue(),
1805: needed);
1806: } else {
1807: return null;
1808: }
1809: }
1810: }
1811: } catch (NoSuchElementException ex) {
1812: return null;
1813: }
1814: }
1815:
1816: private static final boolean usableKeyOnMac(int key, int mask) {
1817: //All permutations fail for Q except ctrl
1818: if (key == KeyEvent.VK_Q) {
1819: return false;
1820: }
1821:
1822: boolean isMeta = ((mask & KeyEvent.META_MASK) != 0)
1823: || ((mask & KeyEvent.CTRL_DOWN_MASK) != 0);
1824:
1825: boolean isAlt = ((mask & KeyEvent.ALT_MASK) != 0)
1826: || ((mask & KeyEvent.ALT_DOWN_MASK) != 0);
1827:
1828: boolean isOnlyMeta = isMeta
1829: && ((mask & ~(KeyEvent.META_DOWN_MASK | KeyEvent.META_MASK)) == 0);
1830:
1831: //Mac OS consumes keys Command+ these keys - the app will never see
1832: //them, so CTRL should not be remapped for these
1833: if (isOnlyMeta) {
1834: return (key != KeyEvent.VK_H) && (key != KeyEvent.VK_SPACE)
1835: && (key != KeyEvent.VK_TAB);
1836: } else if ((key == KeyEvent.VK_D) && isMeta && isAlt) {
1837: return false;
1838: } else {
1839: return true;
1840: }
1841: }
1842:
1843: /** Convert a space-separated list of user-friendly key binding names to a list of Swing key strokes.
1844: * @param s the string with keys
1845: * @return array of key strokes, or <code>null</code> if the string description is not valid
1846: * @see #stringToKey
1847: */
1848: public static KeyStroke[] stringToKeys(String s) {
1849: StringTokenizer st = new StringTokenizer(s
1850: .toUpperCase(Locale.ENGLISH), " "); // NOI18N
1851: ArrayList<KeyStroke> arr = new ArrayList<KeyStroke>();
1852:
1853: while (st.hasMoreElements()) {
1854: s = st.nextToken();
1855:
1856: KeyStroke k = stringToKey(s);
1857:
1858: if (k == null) {
1859: return null;
1860: }
1861:
1862: arr.add(k);
1863: }
1864:
1865: return arr.toArray(new KeyStroke[arr.size()]);
1866: }
1867:
1868: /** Adds characters for modifiers to the buffer.
1869: * @param buf buffer to add to
1870: * @param modif modifiers to add (KeyEvent.XXX_MASK)
1871: * @return true if something has been added
1872: */
1873: private static boolean addModifiers(StringBuilder buf, int modif) {
1874: boolean b = false;
1875:
1876: if ((modif & KeyEvent.CTRL_MASK) != 0) {
1877: buf.append("C"); // NOI18N
1878: b = true;
1879: }
1880:
1881: if ((modif & KeyEvent.ALT_MASK) != 0) {
1882: buf.append("A"); // NOI18N
1883: b = true;
1884: }
1885:
1886: if ((modif & KeyEvent.SHIFT_MASK) != 0) {
1887: buf.append("S"); // NOI18N
1888: b = true;
1889: }
1890:
1891: if ((modif & KeyEvent.META_MASK) != 0) {
1892: buf.append("M"); // NOI18N
1893: b = true;
1894: }
1895:
1896: if ((modif & CTRL_WILDCARD_MASK) != 0) {
1897: buf.append("D");
1898: b = true;
1899: }
1900:
1901: if ((modif & ALT_WILDCARD_MASK) != 0) {
1902: buf.append("O");
1903: b = true;
1904: }
1905:
1906: return b;
1907: }
1908:
1909: /** Reads for modifiers and creates integer with required mask.
1910: * @param s string with modifiers
1911: * @return integer with mask
1912: * @exception NoSuchElementException if some letter is not modifier
1913: */
1914: private static int readModifiers(String s)
1915: throws NoSuchElementException {
1916: int m = 0;
1917:
1918: for (int i = 0; i < s.length(); i++) {
1919: switch (s.charAt(i)) {
1920: case 'C':
1921: m |= KeyEvent.CTRL_MASK;
1922:
1923: break;
1924:
1925: case 'A':
1926: m |= KeyEvent.ALT_MASK;
1927:
1928: break;
1929:
1930: case 'M':
1931: m |= KeyEvent.META_MASK;
1932:
1933: break;
1934:
1935: case 'S':
1936: m |= KeyEvent.SHIFT_MASK;
1937:
1938: break;
1939:
1940: case 'D':
1941: m |= CTRL_WILDCARD_MASK;
1942:
1943: break;
1944:
1945: case 'O':
1946: m |= ALT_WILDCARD_MASK;
1947:
1948: break;
1949:
1950: default:
1951: throw new NoSuchElementException(s);
1952: }
1953: }
1954:
1955: return m;
1956: }
1957:
1958: /**
1959: * Finds out the monitor where the user currently has the input focus.
1960: * This method is usually used to help the client code to figure out on
1961: * which monitor it should place newly created windows/frames/dialogs.
1962: *
1963: * @return the GraphicsConfiguration of the monitor which currently has the
1964: * input focus
1965: */
1966: private static GraphicsConfiguration getCurrentGraphicsConfiguration() {
1967: Component focusOwner = KeyboardFocusManager
1968: .getCurrentKeyboardFocusManager().getFocusOwner();
1969: if (focusOwner != null) {
1970: Window w = SwingUtilities.getWindowAncestor(focusOwner);
1971: if (w != null) {
1972: return w.getGraphicsConfiguration();
1973: }
1974: }
1975:
1976: return GraphicsEnvironment.getLocalGraphicsEnvironment()
1977: .getDefaultScreenDevice().getDefaultConfiguration();
1978: }
1979:
1980: /**
1981: * Returns the usable area of the screen where applications can place its
1982: * windows. The method subtracts from the screen the area of taskbars,
1983: * system menus and the like. The screen this method applies to is the one
1984: * which is considered current, ussually the one where the current input
1985: * focus is.
1986: *
1987: * @return the rectangle of the screen where one can place windows
1988: *
1989: * @since 2.5
1990: */
1991: public static Rectangle getUsableScreenBounds() {
1992: return getUsableScreenBounds(getCurrentGraphicsConfiguration());
1993: }
1994:
1995: /**
1996: * Returns the usable area of the screen where applications can place its
1997: * windows. The method subtracts from the screen the area of taskbars,
1998: * system menus and the like.
1999: *
2000: * @param gconf the GraphicsConfiguration of the monitor
2001: * @return the rectangle of the screen where one can place windows
2002: *
2003: * @since 2.5
2004: */
2005: public static Rectangle getUsableScreenBounds(
2006: GraphicsConfiguration gconf) {
2007: if (gconf == null) {
2008: gconf = GraphicsEnvironment.getLocalGraphicsEnvironment()
2009: .getDefaultScreenDevice().getDefaultConfiguration();
2010: }
2011:
2012: Rectangle bounds = new Rectangle(gconf.getBounds());
2013:
2014: String str;
2015:
2016: str = System.getProperty("netbeans.screen.insets"); // NOI18N
2017:
2018: if (str != null) {
2019: StringTokenizer st = new StringTokenizer(str, ", "); // NOI18N
2020:
2021: if (st.countTokens() == 4) {
2022: try {
2023: bounds.y = Integer.parseInt(st.nextToken());
2024: bounds.x = Integer.parseInt(st.nextToken());
2025: bounds.height -= (bounds.y + Integer.parseInt(st
2026: .nextToken()));
2027: bounds.width -= (bounds.x + Integer.parseInt(st
2028: .nextToken()));
2029: } catch (NumberFormatException ex) {
2030: LOG.log(Level.WARNING, null, ex);
2031: }
2032: }
2033:
2034: return bounds;
2035: }
2036:
2037: str = System.getProperty("netbeans.taskbar.height"); // NOI18N
2038:
2039: if (str != null) {
2040: bounds.height -= Integer.getInteger(str, 0).intValue();
2041:
2042: return bounds;
2043: }
2044:
2045: try {
2046: Toolkit toolkit = Toolkit.getDefaultToolkit();
2047: Insets insets = toolkit.getScreenInsets(gconf);
2048: bounds.y += insets.top;
2049: bounds.x += insets.left;
2050: bounds.height -= (insets.top + insets.bottom);
2051: bounds.width -= (insets.left + insets.right);
2052: } catch (Exception ex) {
2053: LOG.log(Level.WARNING, null, ex);
2054: }
2055:
2056: return bounds;
2057: }
2058:
2059: /**
2060: * Helps client code place components on the center of the screen. It
2061: * handles multiple monitor configuration correctly
2062: *
2063: * @param componentSize the size of the component
2064: * @return bounds of the centered component
2065: *
2066: * @since 2.5
2067: */
2068: public static Rectangle findCenterBounds(Dimension componentSize) {
2069: return findCenterBounds(getCurrentGraphicsConfiguration(),
2070: componentSize);
2071: }
2072:
2073: /**
2074: * Helps client code place components on the center of the screen. It
2075: * handles multiple monitor configuration correctly
2076: *
2077: * @param gconf the GraphicsConfiguration of the monitor
2078: * @param componentSize the size of the component
2079: * @return bounds of the centered component
2080: */
2081: private static Rectangle findCenterBounds(
2082: GraphicsConfiguration gconf, Dimension componentSize) {
2083: if (gconf == null) {
2084: gconf = GraphicsEnvironment.getLocalGraphicsEnvironment()
2085: .getDefaultScreenDevice().getDefaultConfiguration();
2086: }
2087:
2088: Rectangle bounds = gconf.getBounds();
2089:
2090: return new Rectangle(bounds.x
2091: + ((bounds.width - componentSize.width) / 2), bounds.y
2092: + ((bounds.height - componentSize.height) / 2),
2093: componentSize.width, componentSize.height);
2094: }
2095:
2096: /** @return size of the screen. The size is modified for Windows OS
2097: * - some points are subtracted to reflect a presence of the taskbar
2098: *
2099: * @deprecated this method is almost useless in multiple monitor configuration
2100: *
2101: * @see #getUsableScreenBounds()
2102: * @see #findCenterBounds(Dimension)
2103: */
2104: @Deprecated
2105: public static final Dimension getScreenSize() {
2106: Dimension screenSize = Toolkit.getDefaultToolkit()
2107: .getScreenSize();
2108:
2109: if (isWindows() && !Boolean.getBoolean("netbeans.no.taskbar")) {
2110: screenSize.height -= TYPICAL_WINDOWS_TASKBAR_HEIGHT;
2111: } else if (isMac()) {
2112: screenSize.height -= TYPICAL_MACOSX_MENU_HEIGHT;
2113: }
2114:
2115: return screenSize;
2116: }
2117:
2118: /** Utility method for avoiding of memory leak in JDK 1.3 / JFileChooser.showDialog(...)
2119: * @param parent
2120: * @param approveButtonText
2121: * @deprecated Not needed in JDK 1.4.
2122: */
2123: @Deprecated
2124: public static final int showJFileChooser(
2125: javax.swing.JFileChooser chooser,
2126: java.awt.Component parent,
2127: java.lang.String approveButtonText) {
2128: if (approveButtonText != null) {
2129: chooser.setApproveButtonText(approveButtonText);
2130: chooser
2131: .setDialogType(javax.swing.JFileChooser.CUSTOM_DIALOG);
2132: }
2133:
2134: Frame frame = null;
2135: Dialog parentDlg = null;
2136:
2137: if (parent instanceof Dialog) {
2138: parentDlg = (Dialog) parent;
2139: } else {
2140: frame = (parent instanceof java.awt.Frame) ? (Frame) parent
2141: : (Frame) javax.swing.SwingUtilities
2142: .getAncestorOfClass(Frame.class, parent);
2143: }
2144:
2145: String title = chooser.getDialogTitle();
2146:
2147: if (title == null) {
2148: title = chooser.getUI().getDialogTitle(chooser);
2149: }
2150:
2151: final javax.swing.JDialog dialog;
2152:
2153: if (parentDlg != null) {
2154: dialog = new javax.swing.JDialog(parentDlg, title, true);
2155: } else {
2156: dialog = new javax.swing.JDialog(frame, title, true);
2157: }
2158:
2159: dialog
2160: .setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
2161:
2162: Container contentPane = dialog.getContentPane();
2163: contentPane.setLayout(new BorderLayout());
2164: contentPane.add(chooser, BorderLayout.CENTER);
2165:
2166: dialog.pack();
2167: dialog.setBounds(findCenterBounds(parent
2168: .getGraphicsConfiguration(), dialog.getSize()));
2169:
2170: chooser.rescanCurrentDirectory();
2171:
2172: final int[] retValue = new int[] { javax.swing.JFileChooser.CANCEL_OPTION };
2173:
2174: java.awt.event.ActionListener l = new java.awt.event.ActionListener() {
2175: public void actionPerformed(java.awt.event.ActionEvent ev) {
2176: if (ev.getActionCommand() == javax.swing.JFileChooser.APPROVE_SELECTION) {
2177: retValue[0] = javax.swing.JFileChooser.APPROVE_OPTION;
2178: }
2179:
2180: dialog.setVisible(false);
2181: dialog.dispose();
2182: }
2183: };
2184:
2185: chooser.addActionListener(l);
2186:
2187: dialog.show();
2188:
2189: return retValue[0];
2190: }
2191:
2192: /** Sort a list according to a specified partial order.
2193: * Note that in the current implementation, the comparator will be called
2194: * exactly once for each distinct pair of list elements, ignoring order,
2195: * so caching its results is a waste of time.
2196: * @param l the list to sort (will not be modified)
2197: * @param c a comparator to impose the partial order; "equal" means that the elements
2198: * are not ordered with respect to one another, i.e. may be only a partial order
2199: * @param stable whether to attempt a stable sort, meaning that the position of elements
2200: * will be disturbed as little as possible; might be slightly slower
2201: * @return the partially-sorted list
2202: * @throws UnorderableException if the specified partial order is inconsistent on this list
2203: * @deprecated Deprecated in favor of the potentially much faster (and possibly more correct) {@link #topologicalSort}.
2204: */
2205: @SuppressWarnings("unchecked")
2206: // do not bother, it is deprecated anyway
2207: @Deprecated
2208: public static List partialSort(List l, Comparator c, boolean stable)
2209: throws UnorderableException {
2210: // map from objects in the list to null or sets of objects they are greater than
2211: // (i.e. must appear after):
2212: Map deps = new HashMap(); // Map<Object,Set<Object>>
2213: int size = l.size();
2214:
2215: // Create a table of dependencies.
2216: for (int i = 0; i < size; i++) {
2217: for (int j = i + 1; j < size; j++) {
2218: int cmp = c.compare(l.get(i), l.get(j));
2219:
2220: if (cmp != 0) {
2221: Object earlier = l.get((cmp < 0) ? i : j);
2222: Object later = l.get((cmp > 0) ? i : j);
2223: Set s = (Set) deps.get(later);
2224:
2225: if (s == null) {
2226: deps.put(later, s = new HashSet());
2227: }
2228:
2229: s.add(earlier);
2230: }
2231: }
2232: }
2233:
2234: // Lists of items to process, and items sorted.
2235: List left = new LinkedList(l);
2236: List sorted = new ArrayList(size);
2237:
2238: while (left.size() > 0) {
2239: boolean stillGoing = false;
2240: Iterator it = left.iterator();
2241:
2242: while (it.hasNext()) {
2243: Object elt = it.next();
2244: Set eltDeps = (Set) deps.get(elt);
2245:
2246: if ((eltDeps == null) || (eltDeps.size() == 0)) {
2247: // This one is OK to add to the result now.
2248: it.remove();
2249: stillGoing = true;
2250: sorted.add(elt);
2251:
2252: // Mark other elements that should be later
2253: // than this as having their dep satisfied.
2254: Iterator it2 = left.iterator();
2255:
2256: while (it2.hasNext()) {
2257: Object elt2 = it2.next();
2258: Set eltDeps2 = (Set) deps.get(elt2);
2259:
2260: if (eltDeps2 != null) {
2261: eltDeps2.remove(elt);
2262: }
2263: }
2264:
2265: if (stable) {
2266: break;
2267: }
2268: }
2269: }
2270:
2271: if (!stillGoing) {
2272: // Clean up deps to only include "interesting" problems.
2273: it = deps.entrySet().iterator();
2274:
2275: while (it.hasNext()) {
2276: Map.Entry me = (Map.Entry) it.next();
2277:
2278: if (!left.contains(me.getKey())) {
2279: it.remove();
2280: } else {
2281: Set s = (Set) me.getValue();
2282: Iterator it2 = s.iterator();
2283:
2284: while (it2.hasNext()) {
2285: if (!left.contains(it2.next())) {
2286: it2.remove();
2287: }
2288: }
2289:
2290: if (s.isEmpty()) {
2291: it.remove();
2292: }
2293: }
2294: }
2295:
2296: throw new UnorderableException(left, deps);
2297: }
2298: }
2299:
2300: return sorted;
2301: }
2302:
2303: /**
2304: * Topologically sort some objects.
2305: * <p>There may not be any nulls among the objects, nor duplicates
2306: * (as per hash/equals), nor duplicates among the edge lists.
2307: * The edge map need not contain an entry for every object, only if it
2308: * has some outgoing edges (empty but not null map values are permitted).
2309: * The edge map shall not contain neither keys nor value entries for objects not
2310: * in the collection to be sorted, if that happens they will be ignored (since version 7.9).
2311: * <p>The incoming parameters will not be modified; they must not be changed
2312: * during the call and possible calls to TopologicalSortException methods.
2313: * The returned list will support modifications.
2314: * <p>There is a <em>weak</em> stability guarantee: if there are no edges
2315: * which contradict the incoming order, the resulting list will be in the same
2316: * order as the incoming elements. However if some elements need to be rearranged,
2317: * it is <em>not</em> guaranteed that others will not also be rearranged, even
2318: * if they did not strictly speaking need to be.
2319: * @param c a collection of objects to be topologically sorted
2320: * @param edges constraints among those objects, of type <code>Map<Object,Collection></code>;
2321: * if an object is a key in this map, the resulting order will
2322: * have that object before any objects listed in the value
2323: * @return a partial ordering of the objects in the collection,
2324: * @exception TopologicalSortException if the sort cannot succeed due to cycles in the graph, the
2325: * exception contains additional information to describe and possibly recover from the error
2326: * @since 3.30
2327: * @see <a href="http://www.netbeans.org/issues/show_bug.cgi?id=27286">Issue #27286</a>
2328: */
2329: public static <T> List<T> topologicalSort(Collection<T> c,
2330: Map<? super T, ? extends Collection<? extends T>> edges)
2331: throws TopologicalSortException {
2332: Map<T, Boolean> finished = new HashMap<T, Boolean>();
2333: List<T> r = new ArrayList<T>(Math.max(c.size(), 1));
2334: List<T> cRev = new ArrayList<T>(c);
2335: Collections.reverse(cRev);
2336:
2337: Iterator<T> it = cRev.iterator();
2338:
2339: while (it.hasNext()) {
2340: List<T> cycle = visit(it.next(), edges, finished, r);
2341:
2342: if (cycle != null) {
2343: throw new TopologicalSortException(cRev, edges);
2344: }
2345: }
2346:
2347: Collections.reverse(r);
2348: if (r.size() != c.size()) {
2349: r.retainAll(c);
2350: }
2351:
2352: return r;
2353: }
2354:
2355: /**
2356: * Visit one node in the DAG.
2357: * @param node node to visit
2358: * @param edges edges in the DAG
2359: * @param finished which nodes are finished; a node has no entry if it has not yet
2360: * been visited, else it is set to false while recurring and true
2361: * when it has finished
2362: * @param r the order in progress
2363: * @return list with detected cycle
2364: */
2365: static <T> List<T> visit(T node,
2366: Map<? super T, ? extends Collection<? extends T>> edges,
2367: Map<T, Boolean> finished, List<T> r) {
2368: Boolean b = finished.get(node);
2369:
2370: //System.err.println("node=" + node + " color=" + b);
2371: if (b != null) {
2372: if (b.booleanValue()) {
2373: return null;
2374: }
2375:
2376: ArrayList<T> cycle = new ArrayList<T>();
2377: cycle.add(node);
2378: finished.put(node, null);
2379:
2380: return cycle;
2381: }
2382:
2383: Collection<? extends T> e = edges.get(node);
2384:
2385: if (e != null) {
2386: finished.put(node, Boolean.FALSE);
2387:
2388: Iterator<? extends T> it = e.iterator();
2389:
2390: while (it.hasNext()) {
2391: List<T> cycle = visit(it.next(), edges, finished, r);
2392:
2393: if (cycle != null) {
2394: if (cycle instanceof ArrayList) {
2395: // if cycle instanceof ArrayList we are still in the
2396: // cycle and we want to collect new members
2397: if (Boolean.FALSE == finished.get(node)) {
2398: // another member in the cycle
2399: cycle.add(node);
2400: } else {
2401: // we have reached the head of the cycle
2402: // do not add additional cycles anymore
2403: Collections.reverse(cycle);
2404:
2405: // changing cycle to not be ArrayList
2406: cycle = Collections.unmodifiableList(cycle);
2407: }
2408: }
2409:
2410: // mark this node as tested
2411: finished.put(node, Boolean.TRUE);
2412:
2413: // and report an error
2414: return cycle;
2415: }
2416: }
2417: }
2418:
2419: finished.put(node, Boolean.TRUE);
2420: r.add(node);
2421:
2422: return null;
2423: }
2424:
2425: /** Provides support for parts of the system that deal with classnames
2426: * (use <code>Class.forName</code>, <code>NbObjectInputStream</code>, etc.).
2427: * <P>
2428: * Often class names (especially package names) changes during lifecycle
2429: * of a module. When some piece of the system stores the name of a class
2430: * in certain point of a time and wants to find the correct <code>Class</code>
2431: * later it needs to count with the possibility of rename.
2432: * <P>
2433: * For such purposes this method has been created. It allows modules to
2434: * register their classes that changed names and other parts of system that
2435: * deal with class names to find the correct names.
2436: * <P>
2437: * To register a mapping from old class names to new ones create a file
2438: * <code>META-INF/netbeans/translate.names</code> in your module and fill it
2439: * with your mapping:
2440: * <PRE>
2441: * #
2442: * # Mapping of legacy classes to new ones
2443: * #
2444: *
2445: * org.oldpackage.MyClass=org.newpackage.MyClass # rename of package for one class
2446: * org.mypackage.OldClass=org.mypackage.NewClass # rename of class in a package
2447: *
2448: * # rename of class and package
2449: * org.oldpackage.OldClass=org.newpackage.NewClass
2450: *
2451: * # rename of whole package
2452: * org.someoldpackage=org.my.new.package.structure
2453: *
2454: * </PRE>
2455: * Btw. one can use spaces instead of <code>=</code> sign.
2456: * For a real world example
2457: * check the
2458: * <a href="http://www.netbeans.org/source/browse/xml/text-edit/compat/src/META-INF/netbeans/">
2459: * xml module</a>.
2460: *
2461: * <P>
2462: * For purposes of <link>org.openide.util.io.NbObjectInputStream</link> there is
2463: * a following special convention:
2464: * If the
2465: * className is not listed as one that is to be renamed, the returned
2466: * string == className, if the className is registered to be renamed
2467: * than the className != returned value, even in a case when className.equals (retValue)
2468: *
2469: * @param className fully qualified name of a class to translate
2470: * @return new name of the class according to renaming rules.
2471: */
2472: public static String translate(final String className) {
2473: checkMapping();
2474:
2475: RE exp;
2476:
2477: synchronized (TRANS_LOCK) {
2478: exp = transExp;
2479: }
2480:
2481: if (exp == null) {
2482: // no transition table found
2483: return className;
2484: }
2485:
2486: synchronized (exp) {
2487: // refusing convertions as fast as possible
2488: return exp.convert(className);
2489: }
2490: }
2491:
2492: /** Loads all resources that contain renaming information.
2493: * @param l classloader to load packages from
2494: */
2495: private static void checkMapping() {
2496: // test if we run in test mode
2497: if (transLoader == TRANS_LOCK) {
2498: // no check
2499: return;
2500: }
2501:
2502: ClassLoader current = Lookup.getDefault().lookup(
2503: ClassLoader.class);
2504:
2505: if (current == null) {
2506: current = ClassLoader.getSystemClassLoader();
2507: }
2508:
2509: if (transLoader == current) {
2510: // no change, no rescan
2511: return;
2512: }
2513:
2514: initForLoader(current, current);
2515: }
2516:
2517: /* Initializes the content of transition table from a classloader.
2518: * @param loader loader to read data from
2519: * @param set loader to set as the transLoader or null if we run in test mode
2520: */
2521: static void initForLoader(ClassLoader current, Object set) {
2522: if (set == null) {
2523: set = TRANS_LOCK;
2524: }
2525:
2526: Enumeration en;
2527:
2528: try {
2529: en = current
2530: .getResources("META-INF/netbeans/translate.names");
2531: } catch (IOException ex) {
2532: LOG.log(Level.WARNING, null, ex);
2533: en = null;
2534: }
2535:
2536: if ((en == null) || !en.hasMoreElements()) {
2537: synchronized (TRANS_LOCK) {
2538: transLoader = set;
2539: transExp = null;
2540: }
2541:
2542: return;
2543: }
2544:
2545: // format of line in the meta files
2546: //
2547: // # comments are allowed
2548: // a.name.in.a.Package=another.Name # with comment is allowed
2549: // for.compatibility.one.can.use.Space instead.of.Equal
2550: //
2551: RE re = null;
2552:
2553: // [pnejedly:perf] commented out. The RegExp based translation was way slower
2554: // than the hand-written RE13
2555: // if (Dependency.JAVA_SPEC.compareTo(new SpecificationVersion("1.4")) >= 0) { // NOI18N
2556: // try {
2557: // re = (RE)Class.forName ("org.openide.util.RE14").newInstance ();
2558: // } catch (ThreadDeath t) {
2559: // throw t;
2560: // } catch (Throwable t) {
2561: // }
2562: // }
2563: // if (re == null) {
2564: re = new RE13();
2565:
2566: // }
2567: TreeSet<String[]> list = new TreeSet<String[]>(
2568: new Comparator<String[]>() {
2569: public int compare(String[] o1, String[] o2) {
2570: String s1 = o1[0];
2571: String s2 = o2[0];
2572:
2573: int i1 = s1.length();
2574: int i2 = s2.length();
2575:
2576: if (i1 != i2) {
2577: return i2 - i1;
2578: }
2579:
2580: return s2.compareTo(s1);
2581: }
2582: });
2583:
2584: while (en.hasMoreElements()) {
2585: URL u = (URL) en.nextElement();
2586:
2587: try {
2588: BufferedReader reader = new BufferedReader(
2589: new InputStreamReader(u.openStream(), "UTF8") // use explicit encoding //NOI18N
2590: );
2591: loadTranslationFile(re, reader, list);
2592: reader.close();
2593: } catch (IOException ex) {
2594: LOG.log(Level.WARNING, "Problematic file: " + u);
2595: LOG.log(Level.WARNING, null, ex);
2596: }
2597: }
2598:
2599: // construct a regular expression of following form. Let "1", "2", "3", "4"
2600: // be the keys:
2601: // "^
2602: // thus if 4 is matched five groups will be created
2603: String[] arr = new String[list.size()];
2604: String[] pattern = new String[arr.length];
2605:
2606: int i = 0;
2607: Iterator it = list.iterator();
2608:
2609: while (it.hasNext()) {
2610: String[] pair = (String[]) it.next();
2611: arr[i] = pair[1].intern(); // name of the track
2612: pattern[i] = pair[0]; // original object
2613: i++;
2614: }
2615:
2616: synchronized (TRANS_LOCK) {
2617: // last check
2618: if (arr.length == 0) {
2619: transExp = null;
2620: } else {
2621: transExp = re;
2622: transExp.init(pattern, arr);
2623: }
2624:
2625: transLoader = set;
2626: }
2627: }
2628:
2629: /**
2630: * Load single translation file.
2631: * @param resource URL identifiing transaction table
2632: * @param results will be filled with String[2]
2633: */
2634: private static void loadTranslationFile(RE re,
2635: BufferedReader reader, Set<String[]> results)
2636: throws IOException {
2637: for (;;) {
2638: String line = reader.readLine();
2639:
2640: if (line == null) {
2641: break;
2642: }
2643:
2644: if ((line.length() == 0) || line.startsWith("#")) { // NOI18N
2645:
2646: continue;
2647: }
2648:
2649: String[] pair = re.readPair(line);
2650:
2651: if (pair == null) {
2652: throw new java.io.InvalidObjectException(
2653: "Line is invalid: " + line);
2654: }
2655:
2656: results.add(pair);
2657: }
2658: }
2659:
2660: /** This method merges two images into the new one. The second image is drawn
2661: * over the first one with its top-left corner at x, y. Images need not be of the same size.
2662: * New image will have a size of max(second image size + top-left corner, first image size).
2663: * Method is used mostly when second image contains transparent pixels (e.g. for badging).
2664: * If both images are <code>null</code>, it makes default transparent 16x16 image.
2665: * @param image1 underlying image
2666: * @param image2 second image
2667: * @param x x position of top-left corner
2668: * @param y y position of top-left corner
2669: * @return new merged image
2670: */
2671: public static final Image mergeImages(Image image1, Image image2,
2672: int x, int y) {
2673: if (image1 == null) {
2674: throw new NullPointerException();
2675: }
2676:
2677: if (image2 == null) {
2678: throw new NullPointerException();
2679: }
2680:
2681: return IconManager.mergeImages(image1, image2, x, y);
2682: }
2683:
2684: /**
2685: * Loads an image from the specified resource ID. The image is loaded using the "system" classloader registered in
2686: * Lookup.
2687: * @param resourceID resource path of the icon (no initial slash)
2688: * @return icon's Image, or null, if the icon cannot be loaded.
2689: */
2690: public static final Image loadImage(String resourceID) {
2691: return IconManager.getIcon(resourceID, false);
2692: }
2693:
2694: /**
2695: * Converts given icon to a {@link java.awt.Image}.
2696: *
2697: * @param icon {@link javax.swing.Icon} to be converted.
2698: * @since 7.3
2699: */
2700: public static final Image icon2Image(Icon icon) {
2701: if (icon instanceof ImageIcon) {
2702: return ((ImageIcon) icon).getImage();
2703: } else {
2704: BufferedImage bImage = new BufferedImage(icon
2705: .getIconWidth(), icon.getIconHeight(),
2706: BufferedImage.TYPE_INT_ARGB);
2707: Graphics g = bImage.getGraphics();
2708: icon.paintIcon(new JLabel(), g, 0, 0);
2709: g.dispose();
2710: return bImage;
2711: }
2712: }
2713:
2714: /** Builds a popup menu from actions for provided context specified by
2715: * <code>Lookup</code>.
2716: * Takes list of actions and for actions whic are instances of
2717: * <code>ContextAwareAction</code> creates and uses the context aware instance.
2718: * Then gets the action presenter or simple menu item for the action to the
2719: * popup menu for each action (or separator for each 'lonely' null array member).
2720: *
2721: * @param actions array of actions to build menu for. Can contain null
2722: * elements, they will be replaced by separators
2723: * @param context the context for which the popup is build
2724: * @return the constructed popup menu
2725: * @see ContextAwareAction
2726: * @since 3.29
2727: */
2728: public static JPopupMenu actionsToPopup(Action[] actions,
2729: Lookup context) {
2730: // keeps actions for which was menu item created already (do not add them twice)
2731: Set<Action> counted = new HashSet<Action>();
2732: // components to be added (separators are null)
2733: List<Component> components = new ArrayList<Component>();
2734:
2735: for (Action action : actions) {
2736: if (action != null && counted.add(action)) {
2737: // switch to replacement action if there is some
2738: if (action instanceof ContextAwareAction) {
2739: Action contextAwareAction = ((ContextAwareAction) action)
2740: .createContextAwareInstance(context);
2741: if (contextAwareAction == null) {
2742: Logger
2743: .getLogger(Utilities.class.getName())
2744: .warning(
2745: "ContextAwareAction.createContextAwareInstance(context) returns null. That is illegal!" // NOI18N
2746: + " action="
2747: + action
2748: + ", context="
2749: + context); // NOI18N
2750: } else {
2751: action = contextAwareAction;
2752: }
2753: }
2754:
2755: JMenuItem item;
2756: if (action instanceof Presenter.Popup) {
2757: item = ((Presenter.Popup) action)
2758: .getPopupPresenter();
2759: if (item == null) {
2760: Logger.getLogger(Utilities.class.getName())
2761: .warning(
2762: "findContextMenuImpl, getPopupPresenter returning null for "
2763: + action); // NOI18N
2764: continue;
2765: }
2766: } else {
2767: // We need to correctly handle mnemonics with '&' etc.
2768: item = AWTBridge.getDefault().createPopupPresenter(
2769: action);
2770: }
2771:
2772: for (Component c : AWTBridge.getDefault()
2773: .convertComponents(item)) {
2774: if (c instanceof JSeparator) {
2775: components.add(null);
2776: } else {
2777: components.add(c);
2778: }
2779: }
2780: } else {
2781: components.add(null);
2782: }
2783: }
2784:
2785: // Now create actual menu. Strip adjacent, leading, and trailing separators.
2786: JPopupMenu menu = AWTBridge.getDefault().createEmptyPopup();
2787: boolean nonempty = false; // has anything been added yet?
2788: boolean pendingSep = false; // should there be a separator before any following item?
2789: for (Component c : components) {
2790: if (c == null) {
2791: pendingSep = nonempty;
2792: } else {
2793: nonempty = true;
2794: if (pendingSep) {
2795: pendingSep = false;
2796: menu.addSeparator();
2797: }
2798: menu.add(c);
2799: }
2800: }
2801: return menu;
2802: }
2803:
2804: /** Builds a popup menu for provided component. It retrieves context
2805: * (lookup) from provided component instance or one of its parent
2806: * (it searches up to the hierarchy for <code>Lookup.Provider</code> instance).
2807: * If none of the components is <code>Lookup.Provider</code> instance, then
2808: * it is created context which is fed with composite ActionMap which delegates
2809: * to all components up to hierarchy started from the specified one.
2810: * Then <code>actionsToPopup(Action[], Lookup)</code>} is called with
2811: * the found <code>Lookup</code> instance, which actually creates a popup menu.
2812: *
2813: * @param actions array of actions to build menu for. Can contain null
2814: * elements, they will be replaced by separators
2815: * @param component a component in which to search for a context
2816: * @return the constructed popup menu
2817: * @see Lookup.Provider
2818: * @see #actionsToPopup(Action[], Lookup)
2819: * @since 3.29
2820: */
2821: public static javax.swing.JPopupMenu actionsToPopup(
2822: Action[] actions, java.awt.Component component) {
2823: Lookup lookup = null;
2824:
2825: for (Component c = component; c != null; c = c.getParent()) {
2826: if (c instanceof Lookup.Provider) {
2827: lookup = ((Lookup.Provider) c).getLookup();
2828:
2829: if (lookup != null) {
2830: break;
2831: }
2832: }
2833: }
2834:
2835: if (lookup == null) {
2836: // Fallback to composite action map, even it is questionable,
2837: // whether we should support component which is not (nor
2838: // none of its parents) lookup provider.
2839: UtilitiesCompositeActionMap map = new UtilitiesCompositeActionMap(
2840: component);
2841: lookup = org.openide.util.lookup.Lookups.singleton(map);
2842: }
2843:
2844: return actionsToPopup(actions, lookup);
2845: }
2846:
2847: /**
2848: * Global context for actions. Toolbar, menu or any other "global"
2849: * action presenters shall operate in this context.
2850: * Presenters for context menu items should <em>not</em> use
2851: * this method; instead see {@link ContextAwareAction}.
2852: * @see ContextGlobalProvider
2853: * @return the context for actions
2854: * @since 4.10
2855: */
2856: public static Lookup actionsGlobalContext() {
2857: synchronized (ContextGlobalProvider.class) {
2858: if (global != null) {
2859: return global;
2860: }
2861: }
2862:
2863: ContextGlobalProvider p = Lookup.getDefault().lookup(
2864: ContextGlobalProvider.class);
2865: Lookup l = (p == null) ? Lookup.EMPTY : p.createGlobalContext();
2866:
2867: synchronized (ContextGlobalProvider.class) {
2868: if (global == null) {
2869: global = l;
2870: }
2871:
2872: return global;
2873: }
2874: }
2875:
2876: //
2877: // end of actions stuff
2878: //
2879:
2880: /**
2881: * Loads an image based on resource path.
2882: * Exactly like {@link #loadImage(String)} but may do a localized search.
2883: * For example, requesting <samp>org/netbeans/modules/foo/resources/foo.gif</samp>
2884: * might actually find <samp>org/netbeans/modules/foo/resources/foo_ja.gif</samp>
2885: * or <samp>org/netbeans/modules/foo/resources/foo_mybranding.gif</samp>.
2886: *
2887: * <p>Caching of loaded images can be used internally to improve performance.
2888: *
2889: * @since 3.24
2890: */
2891: public static final Image loadImage(String resource,
2892: boolean localized) {
2893: return IconManager.getIcon(resource, localized);
2894: }
2895:
2896: /**
2897: * Returns a cursor with an arrow and an hourglass (or stop watch) badge,
2898: * to be used when a component is busy but the UI is still responding to the user.
2899: *
2900: * Similar to the predefined {@link Cursor#WAIT_CURSOR}, but has an arrow to indicate
2901: * a still-responsive UI.
2902: *
2903: * <p>Typically you will set the cursor only temporarily:
2904: *
2905: * <pre>
2906: * <font class="comment">// code is running in other then event dispatch thread</font>
2907: * currentComponent.setCursor(Utilities.createProgressCursor(currentComponent));
2908: * <font class="keyword">try</font> {
2909: * <font class="comment">// perform some work in other than event dispatch thread
2910: * // (do not block UI)</font>
2911: * } <font class="keyword">finally</font> {
2912: * currentComponent.setCursor(<font class="constant">null</font>);
2913: * }
2914: * </pre>
2915: *
2916: * <p>This implementation provides one cursor for all Mac systems, one for all
2917: * Unix systems (regardless of window manager), and one for all other systems
2918: * including Windows.
2919: *
2920: * @param component the non-null component that will use the progress cursor
2921: * @return a progress cursor (Unix, Windows or Mac)
2922: *
2923: * @since 3.23
2924: */
2925: public static final Cursor createProgressCursor(Component component) {
2926: // refuse null component
2927: if (component == null) {
2928: throw new NullPointerException("Given component is null"); //NOI18N
2929: }
2930:
2931: Image image = null;
2932:
2933: // First check for Mac because its part of the Unix_Mask
2934: if (isMac()) {
2935: image = loadImage("org/openide/util/progress-cursor-mac.gif"); //NOI18N
2936: } else if (isUnix()) {
2937: image = loadImage("org/openide/util/progress-cursor-motif.gif"); //NOI18N
2938: }
2939: // All other OS, including Windows, use Windows cursor
2940: else {
2941: image = loadImage("org/openide/util/progress-cursor-win.gif"); //NOI18N
2942: }
2943:
2944: return createCustomCursor(component, image, "PROGRESS_CURSOR"); //NOI18N
2945: }
2946:
2947: // added to fix issue #30665 (bad size on linux)
2948: public static Cursor createCustomCursor(Component component,
2949: Image icon, String name) {
2950: Toolkit t = component.getToolkit();
2951: Dimension d = t.getBestCursorSize(16, 16);
2952: Image i = icon;
2953:
2954: if (d.width != icon.getWidth(null)) {
2955: if (((d.width) == 0) && (d.height == 0)) {
2956: // system doesn't support custom cursors, falling back
2957: return Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR);
2958: }
2959:
2960: // need to resize the icon
2961: Image empty = IconManager.createBufferedImage(d.width,
2962: d.height);
2963: i = Utilities.mergeImages(icon, empty, 0, 0);
2964: }
2965:
2966: return t.createCustomCursor(i, new Point(1, 1), name);
2967: }
2968:
2969: /** Attaches asynchronous init job to given component.
2970: * {@link AsyncGUIJob#construct()} will be called after first
2971: * paint, when paint event arrives. Later, {@link AsyncGUIJob#finished()}
2972: * will be called according to the rules of the <code>AsyncGUIJob</code> interface.
2973: *
2974: * Useful for components that have slower initialization phase, component
2975: * can benefit from more responsive behaviour during init.
2976: *
2977: * @param comp4Init Regular component in its pre-inited state, state in which
2978: * component will be shown between first paint and init completion.
2979: * @param initJob Initialization job to be called asynchronously. Job can
2980: * optionally implement {@link Cancellable}
2981: * interface for proper cancel logic. Cancel method will be called
2982: * when component stops to be showing during job's progress.
2983: * See {@link java.awt.Component#isShowing}
2984: *
2985: * @since 3.36
2986: */
2987: public static final void attachInitJob(Component comp4Init,
2988: AsyncGUIJob initJob) {
2989: new AsyncInitSupport(comp4Init, initJob);
2990: }
2991:
2992: /**
2993: * Convert a file to a matching <code>file:</code> URL.
2994: * @param f a file (absolute only)
2995: * @return a URL using the <code>file</code> protocol
2996: * @throws MalformedURLException for no good reason
2997: * @see #toFile
2998: * @see <a href="http://www.netbeans.org/issues/show_bug.cgi?id=29711">Issue #29711</a>
2999: * @since 3.26
3000: * @deprecated Use {@link File#toURI} and {@link URI#toURL} instead under JDK 1.4.
3001: * ({@link File#toURL} is buggy in JDK 1.3 and the bugs are not fixed in JDK 1.4.)
3002: */
3003: @Deprecated
3004: public static URL toURL(File f) throws MalformedURLException {
3005: if (f == null) {
3006: throw new NullPointerException();
3007: }
3008:
3009: if (!f.isAbsolute()) {
3010: throw new IllegalArgumentException("Relative path: " + f); // NOI18N
3011: }
3012:
3013: URI uri = f.toURI();
3014:
3015: return uri.toURL();
3016: }
3017:
3018: /**
3019: * Convert a <code>file:</code> URL to a matching file.
3020: * <p>You may not use a URL generated from a file on a different
3021: * platform, as file name conventions may make the result meaningless
3022: * or even unparsable.
3023: * @param u a URL with the <code>file</code> protocol
3024: * @return an absolute file it points to, or <code>null</code> if the URL
3025: * does not seem to point to a file at all
3026: * @see #toURL
3027: * @see <a href="http://www.netbeans.org/issues/show_bug.cgi?id=29711">Issue #29711</a>
3028: * @since 3.26
3029: * @deprecated Use {@link URI#URI(String)} and {@link File#File(URI)} instead under JDK 1.4.
3030: * (There was no proper equivalent under JDK 1.3.)
3031: */
3032: @Deprecated
3033: public static File toFile(URL u) {
3034: if (u == null) {
3035: throw new NullPointerException();
3036: }
3037:
3038: try {
3039: URI uri = new URI(u.toExternalForm());
3040:
3041: return new File(uri);
3042: } catch (URISyntaxException use) {
3043: // malformed URL
3044: return null;
3045: } catch (IllegalArgumentException iae) {
3046: // not a file: URL
3047: return null;
3048: }
3049: }
3050:
3051: /** Interfaces for communication between Utilities.translate and regular
3052: * expression impl.
3053: *
3054: * Order of methods is:
3055: * readPair few times
3056: * init once
3057: * convert many times
3058: */
3059: static interface RE {
3060: public void init(String[] original, String[] newversion);
3061:
3062: public String convert(String pattern);
3063:
3064: /** Parses line of text to two parts: the key and the rest
3065: */
3066: public String[] readPair(String line);
3067: }
3068:
3069: /** Exception indicating that a given list could not be partially-ordered.
3070: * @see #partialSort
3071: * @deprecated Used only by the deprecated partialSort
3072: */
3073: @Deprecated
3074: public static class UnorderableException extends RuntimeException {
3075: static final long serialVersionUID = 6749951134051806661L;
3076: private Collection unorderable;
3077: private Map deps;
3078:
3079: /** Create a new unorderable-list exception with no detail message.
3080: * @param unorderable a collection of list elements which could not be ordered
3081: * (because there was some sort of cycle)
3082: * @param deps dependencies associated with the list; a map from list elements
3083: * to sets of list elements which that element must appear after
3084: */
3085: public UnorderableException(Collection unorderable, Map deps) {
3086: super ( /* "Cannot be ordered: " + unorderable */
3087: ); // NOI18N
3088: this .unorderable = unorderable;
3089: this .deps = deps;
3090: }
3091:
3092: /** Create a new unorderable-list exception with a specified detail message.
3093: * @param message the detail message
3094: * @param unorderable a collection of list elements which could not be ordered
3095: * (because there was some sort of cycle)
3096: * @param deps dependencies associated with the list; a map from list elements
3097: * to sets of list elements which that element must appear after
3098: */
3099: public UnorderableException(String message,
3100: Collection unorderable, Map deps) {
3101: super (message);
3102: this .unorderable = unorderable;
3103: this .deps = deps;
3104: }
3105:
3106: /** Get the unorderable elements.
3107: * @return the elements
3108: * @see Utilities.UnorderableException#Utilities.UnorderableException(Collection,Map)
3109: */
3110: public Collection getUnorderable() {
3111: return unorderable;
3112: }
3113:
3114: /** Get the dependencies.
3115: * @return the dependencies
3116: * @see Utilities.UnorderableException#Utilities.UnorderableException(Collection,Map)
3117: */
3118: public Map getDeps() {
3119: return deps;
3120: }
3121: }
3122:
3123: /** Implementation of the active queue.
3124: */
3125: private static final class ActiveQueue extends
3126: ReferenceQueue<Object> implements Runnable {
3127:
3128: private static final Logger LOGGER = Logger
3129: .getLogger(ActiveQueue.class.getName()
3130: .replace('$', '.'));
3131:
3132: /** number of known outstanding references */
3133: private int count;
3134: private boolean deprecated;
3135:
3136: public ActiveQueue(boolean deprecated) {
3137: this .deprecated = deprecated;
3138: }
3139:
3140: public Reference<Object> poll() {
3141: throw new UnsupportedOperationException();
3142: }
3143:
3144: public Reference<Object> remove(long timeout)
3145: throws IllegalArgumentException, InterruptedException {
3146: throw new InterruptedException();
3147: }
3148:
3149: public Reference<Object> remove() throws InterruptedException {
3150: throw new InterruptedException();
3151: }
3152:
3153: public void run() {
3154: while (true) {
3155: try {
3156: Reference<?> ref = super .remove(0);
3157: LOGGER.finer("dequeued reference");
3158:
3159: if (!(ref instanceof Runnable)) {
3160: LOGGER
3161: .warning("A reference not implementing runnable has been added to the Utilities.activeReferenceQueue(): "
3162: + ref.getClass() // NOI18N
3163: );
3164:
3165: continue;
3166: }
3167:
3168: if (deprecated) {
3169: LOGGER
3170: .warning("Utilities.ACTIVE_REFERENCE_QUEUE has been deprecated for "
3171: + ref.getClass()
3172: + " use Utilities.activeReferenceQueue" // NOI18N
3173: );
3174: }
3175:
3176: // do the cleanup
3177: try {
3178: ((Runnable) ref).run();
3179: } catch (ThreadDeath td) {
3180: throw td;
3181: } catch (Throwable t) {
3182: // Should not happen.
3183: // If it happens, it is a bug in client code, notify!
3184: LOGGER.log(Level.WARNING, null, t);
3185: } finally {
3186: // to allow GC
3187: ref = null;
3188: }
3189: } catch (InterruptedException ex) {
3190: LOGGER.log(Level.WARNING, null, ex);
3191: }
3192:
3193: synchronized (this ) {
3194: assert count > 0;
3195: count--;
3196: if (count == 0) {
3197: // We have processed all we have to process (for now at least).
3198: // Could be restarted later if ping() called again.
3199: // This could also happen in case someone called activeReferenceQueue() once and tried
3200: // to use it for several references; in that case run() might never be called on
3201: // the later ones to be collected. Can't really protect against that situation.
3202: // See issue #86625 for details.
3203: LOGGER.fine("stopping thread");
3204: break;
3205: }
3206: }
3207: }
3208: }
3209:
3210: synchronized void ping() {
3211: if (count == 0) {
3212: Thread t = new Thread(this ,
3213: "Active Reference Queue Daemon"); // NOI18N
3214: t.setPriority(Thread.MIN_PRIORITY);
3215: t.setDaemon(true); // to not prevent exit of VM
3216: t.start();
3217: // Note that this will not be printed during IDE startup because
3218: // it happens before logging is even initialized.
3219: LOGGER.fine("starting thread");
3220: } else {
3221: LOGGER.finer("enqueuing reference");
3222: }
3223: count++;
3224: }
3225:
3226: }
3227: }
|