001    /*
002     * $Id: Utilities.java 3100 2008-10-14 22:33:10Z rah003 $
003     *
004     * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle,
005     * Santa Clara, California 95054, U.S.A. All rights reserved.
006     *
007     * This library is free software; you can redistribute it and/or
008     * modify it under the terms of the GNU Lesser General Public
009     * License as published by the Free Software Foundation; either
010     * version 2.1 of the License, or (at your option) any later version.
011     *
012     * This library is distributed in the hope that it will be useful,
013     * but WITHOUT ANY WARRANTY; without even the implied warranty of
014     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015     * Lesser General Public License for more details.
016     *
017     * You should have received a copy of the GNU Lesser General Public
018     * License along with this library; if not, write to the Free Software
019     * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
020     */
021    package org.jdesktop.swingx.util;
022    
023    
024    import java.awt.Component;
025    import java.awt.GraphicsConfiguration;
026    import java.awt.GraphicsEnvironment;
027    import java.awt.Insets;
028    import java.awt.KeyboardFocusManager;
029    import java.awt.Rectangle;
030    import java.awt.Toolkit;
031    import java.awt.Window;
032    import java.awt.event.KeyEvent;
033    import java.lang.ref.Reference;
034    import java.lang.ref.SoftReference;
035    import java.lang.reflect.Field;
036    import java.lang.reflect.Modifier;
037    import java.text.BreakIterator;
038    import java.util.ArrayList;
039    import java.util.HashMap;
040    import java.util.Locale;
041    import java.util.NoSuchElementException;
042    import java.util.StringTokenizer;
043    import java.util.logging.Level;
044    import java.util.logging.Logger;
045    
046    import javax.swing.KeyStroke;
047    import javax.swing.SwingUtilities;
048    
049    /**
050     * Contribution from NetBeans: Issue #319-swingx. <p>
051     * 
052     * PENDING: need to reconcile with OS, JVM... added as-is
053     * because needed the shortcut handling to fix #
054     *
055     * @author apple
056     */
057    public class Utilities {
058        private Utilities() {
059        }
060        
061        private static final int CTRL_WILDCARD_MASK = 32768;
062        private static final int ALT_WILDCARD_MASK = CTRL_WILDCARD_MASK * 2;
063        
064        /** Operating system is Windows NT. */
065        public static final int OS_WINNT = 1 << 0;
066    
067        /** Operating system is Windows 95. */
068        public static final int OS_WIN95 = OS_WINNT << 1;
069    
070        /** Operating system is Windows 98. */
071        public static final int OS_WIN98 = OS_WIN95 << 1;
072    
073        /** Operating system is Solaris. */
074        public static final int OS_SOLARIS = OS_WIN98 << 1;
075    
076        /** Operating system is Linux. */
077        public static final int OS_LINUX = OS_SOLARIS << 1;
078    
079        /** Operating system is HP-UX. */
080        public static final int OS_HP = OS_LINUX << 1;
081    
082        /** Operating system is IBM AIX. */
083        public static final int OS_AIX = OS_HP << 1;
084    
085        /** Operating system is SGI IRIX. */
086        public static final int OS_IRIX = OS_AIX << 1;
087    
088        /** Operating system is Sun OS. */
089        public static final int OS_SUNOS = OS_IRIX << 1;
090    
091        /** Operating system is Compaq TRU64 Unix */
092        public static final int OS_TRU64 = OS_SUNOS << 1;
093    
094        /** Operating system is OS/2. */
095        public static final int OS_OS2 = OS_TRU64 << 2;
096    
097        /** Operating system is Mac. */
098        public static final int OS_MAC = OS_OS2 << 1;
099    
100        /** Operating system is Windows 2000. */
101        public static final int OS_WIN2000 = OS_MAC << 1;
102    
103        /** Operating system is Compaq OpenVMS */
104        public static final int OS_VMS = OS_WIN2000 << 1;
105    
106        /**
107         *Operating system is one of the Windows variants but we don't know which
108         *one it is
109         */
110        public static final int OS_WIN_OTHER = OS_VMS << 1;
111    
112        /** Operating system is unknown. */
113        public static final int OS_OTHER = OS_WIN_OTHER << 1;
114    
115        /** Operating system is FreeBSD
116         * @since 4.50
117         */
118        public static final int OS_FREEBSD = OS_OTHER << 1;
119    
120        /** A mask for Windows platforms. */
121        public static final int OS_WINDOWS_MASK = OS_WINNT | OS_WIN95 | OS_WIN98 | OS_WIN2000 | OS_WIN_OTHER;
122    
123        /** A mask for Unix platforms. */
124        public static final int OS_UNIX_MASK = OS_SOLARIS | OS_LINUX | OS_HP | OS_AIX | OS_IRIX | OS_SUNOS | OS_TRU64 |
125            OS_MAC | OS_FREEBSD;
126    
127        /** A height of the windows's taskbar */
128        public static final int TYPICAL_WINDOWS_TASKBAR_HEIGHT = 27;
129    
130        /** A height of the Mac OS X's menu */
131        private static final int TYPICAL_MACOSX_MENU_HEIGHT = 24;
132        
133        private static int operatingSystem = -1;
134        
135        /** reference to map that maps allowed key names to their values (String, Integer)
136        and reference to map for mapping of values to their names */
137        private static Reference<Object> namesAndValues;
138    
139        /** Get the operating system on which NetBeans is running.
140        * @return one of the <code>OS_*</code> constants (such as {@link #OS_WINNT})
141        */
142        public static int getOperatingSystem() {
143            if (operatingSystem == -1) {
144                String osName = System.getProperty("os.name");
145    
146                if ("Windows NT".equals(osName)) { // NOI18N
147                    operatingSystem = OS_WINNT;
148                } else if ("Windows 95".equals(osName)) { // NOI18N
149                    operatingSystem = OS_WIN95;
150                } else if ("Windows 98".equals(osName)) { // NOI18N
151                    operatingSystem = OS_WIN98;
152                } else if ("Windows 2000".equals(osName)) { // NOI18N
153                    operatingSystem = OS_WIN2000;
154                } else if (osName.startsWith("Windows ")) { // NOI18N
155                    operatingSystem = OS_WIN_OTHER;
156                } else if ("Solaris".equals(osName)) { // NOI18N
157                    operatingSystem = OS_SOLARIS;
158                } else if (osName.startsWith("SunOS")) { // NOI18N
159                    operatingSystem = OS_SOLARIS;
160                }
161                // JDK 1.4 b2 defines os.name for me as "Redhat Linux" -jglick
162                else if (osName.endsWith("Linux")) { // NOI18N
163                    operatingSystem = OS_LINUX;
164                } else if ("HP-UX".equals(osName)) { // NOI18N
165                    operatingSystem = OS_HP;
166                } else if ("AIX".equals(osName)) { // NOI18N
167                    operatingSystem = OS_AIX;
168                } else if ("Irix".equals(osName)) { // NOI18N
169                    operatingSystem = OS_IRIX;
170                } else if ("SunOS".equals(osName)) { // NOI18N
171                    operatingSystem = OS_SUNOS;
172                } else if ("Digital UNIX".equals(osName)) { // NOI18N
173                    operatingSystem = OS_TRU64;
174                } else if ("OS/2".equals(osName)) { // NOI18N
175                    operatingSystem = OS_OS2;
176                } else if ("OpenVMS".equals(osName)) { // NOI18N
177                    operatingSystem = OS_VMS;
178                } else if (osName.equals("Mac OS X")) { // NOI18N
179                    operatingSystem = OS_MAC;
180                } else if (osName.startsWith("Darwin")) { // NOI18N
181                    operatingSystem = OS_MAC;
182                } else if (osName.toLowerCase(Locale.US).startsWith("freebsd")) { // NOI18N 
183                    operatingSystem = OS_FREEBSD;
184                } else {
185                    operatingSystem = OS_OTHER;
186                }
187            }
188            return operatingSystem;
189        }
190        
191        /** Test whether NetBeans is running on some variant of Windows.
192        * @return <code>true</code> if Windows, <code>false</code> if some other manner of operating system
193        */
194        public static boolean isWindows() {
195            return (getOperatingSystem() & OS_WINDOWS_MASK) != 0;
196        }
197    
198        /** Test whether NetBeans is running on some variant of Unix.
199        * Linux is included as well as the commercial vendors, and Mac OS X.
200        * @return <code>true</code> some sort of Unix, <code>false</code> if some other manner of operating system
201        */
202        public static boolean isUnix() {
203            return (getOperatingSystem() & OS_UNIX_MASK) != 0;
204        }
205        
206        /** Test whether the operating system supports icons on frames (windows).
207        * @return <code>true</code> if it does <em>not</em>
208        *
209        */
210        public static boolean isLargeFrameIcons() {
211            return (getOperatingSystem() == OS_SOLARIS) || (getOperatingSystem() == OS_HP);
212        }
213    
214        /**
215         * Finds out the monitor where the user currently has the input focus.
216         * This method is usually used to help the client code to figure out on
217         * which monitor it should place newly created windows/frames/dialogs.
218         *
219         * @return the GraphicsConfiguration of the monitor which currently has the
220         * input focus
221         */
222        private static GraphicsConfiguration getCurrentGraphicsConfiguration() {
223            Component focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
224            if (focusOwner != null) {
225                Window w = SwingUtilities.getWindowAncestor(focusOwner);
226                if (w != null) {
227                    return w.getGraphicsConfiguration();
228                }
229            }
230    
231            return GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
232        }
233    
234        /**
235         * Returns the usable area of the screen where applications can place its
236         * windows.  The method subtracts from the screen the area of taskbars,
237         * system menus and the like.  The screen this method applies to is the one
238         * which is considered current, ussually the one where the current input
239         * focus is.
240         *
241         * @return the rectangle of the screen where one can place windows
242         *
243         * @since 2.5
244         */
245        public static Rectangle getUsableScreenBounds() {
246            return getUsableScreenBounds(getCurrentGraphicsConfiguration());
247        }
248    
249        /**
250         * Returns the usable area of the screen where applications can place its
251         * windows.  The method subtracts from the screen the area of taskbars,
252         * system menus and the like.
253         *
254         * @param gconf the GraphicsConfiguration of the monitor
255         * @return the rectangle of the screen where one can place windows
256         *
257         * @since 2.5
258         */
259        public static Rectangle getUsableScreenBounds(GraphicsConfiguration gconf) {
260            if (gconf == null) {
261                gconf = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
262            }
263    
264            Rectangle bounds = new Rectangle(gconf.getBounds());
265    
266            String str;
267    
268            str = System.getProperty("netbeans.screen.insets"); // NOI18N
269    
270            if (str != null) {
271                StringTokenizer st = new StringTokenizer(str, ", "); // NOI18N
272    
273                if (st.countTokens() == 4) {
274                    try {
275                        bounds.y = Integer.parseInt(st.nextToken());
276                        bounds.x = Integer.parseInt(st.nextToken());
277                        bounds.height -= (bounds.y + Integer.parseInt(st.nextToken()));
278                        bounds.width -= (bounds.x + Integer.parseInt(st.nextToken()));
279                    } catch (NumberFormatException ex) {
280                        Logger.getAnonymousLogger().log(Level.WARNING, null, ex);
281                    }
282                }
283    
284                return bounds;
285            }
286    
287            str = System.getProperty("netbeans.taskbar.height"); // NOI18N
288    
289            if (str != null) {
290                bounds.height -= Integer.getInteger(str, 0).intValue();
291    
292                return bounds;
293            }
294    
295            try {
296                Toolkit toolkit = Toolkit.getDefaultToolkit();
297                Insets insets = toolkit.getScreenInsets(gconf);
298                bounds.y += insets.top;
299                bounds.x += insets.left;
300                bounds.height -= (insets.top + insets.bottom);
301                bounds.width -= (insets.left + insets.right);
302            } catch (Exception ex) {
303                Logger.getAnonymousLogger().log(Level.WARNING, null, ex);
304            }
305    
306            return bounds;
307        }
308        
309    
310        /** Initialization of the names and values
311        * @return array of two hashmaps first maps
312        *   allowed key names to their values (String, Integer)
313        *  and second
314        * hashtable for mapping of values to their names (Integer, String)
315        */
316        private static synchronized HashMap[] initNameAndValues() {
317            if (namesAndValues != null) {
318                HashMap[] arr = (HashMap[]) namesAndValues.get();
319    
320                if (arr != null) {
321                    return arr;
322                }
323            }
324    
325            Field[] fields;
326            // JW - fix Issue #353-swingx: play nicer inside sandbox.
327            try {
328                fields = KeyEvent.class.getDeclaredFields();
329    //           fields = KeyEvent.class.getFields();
330            } catch (SecurityException e) { 
331                // JW: need to do better? What are the use-cases where we don't have
332                // any access to the fields?
333                fields = new Field[0];
334            }
335    
336            HashMap<String,Integer> names = new HashMap<String,Integer>(((fields.length * 4) / 3) + 5, 0.75f);
337            HashMap<Integer,String> values = new HashMap<Integer,String>(((fields.length * 4) / 3) + 5, 0.75f);
338    
339            for (int i = 0; i < fields.length; i++) {
340                if (Modifier.isStatic(fields[i].getModifiers())) {
341                    String name = fields[i].getName();
342    
343                    if (name.startsWith("VK_")) { // NOI18N
344    
345                        // exclude VK
346                        name = name.substring(3);
347    
348                        try {
349                            int numb = fields[i].getInt(null);
350                            Integer value = new Integer(numb);
351                            names.put(name, value);
352                            values.put(value, name);
353                        } catch (IllegalArgumentException ex) {
354                        } catch (IllegalAccessException ex) {
355                        }
356                    }
357                }
358            }
359    
360            if (names.get("CONTEXT_MENU") == null) { // NOI18N
361    
362                Integer n = new Integer(0x20C);
363                names.put("CONTEXT_MENU", n); // NOI18N
364                values.put(n, "CONTEXT_MENU"); // NOI18N
365    
366                n = new Integer(0x20D);
367                names.put("WINDOWS", n); // NOI18N
368                values.put(n, "WINDOWS"); // NOI18N
369            }
370    
371            HashMap[] arr = { names, values };
372    
373            namesAndValues = new SoftReference<Object>(arr);
374    
375            return arr;
376        }
377    
378        /** Converts a Swing key stroke descriptor to a familiar Emacs-like name.
379        * @param stroke key description
380        * @return name of the key (e.g. <code>CS-F1</code> for control-shift-function key one)
381        * @see #stringToKey
382        */
383        public static String keyToString(KeyStroke stroke) {
384            StringBuffer sb = new StringBuffer();
385    
386            // add modifiers that must be pressed
387            if (addModifiers(sb, stroke.getModifiers())) {
388                sb.append('-');
389            }
390    
391            HashMap[] namesAndValues = initNameAndValues();
392    
393            String c = (String) namesAndValues[1].get(new Integer(stroke.getKeyCode()));
394    
395            if (c == null) {
396                sb.append(stroke.getKeyChar());
397            } else {
398                sb.append(c);
399            }
400    
401            return sb.toString();
402        }
403    
404        /** Construct a new key description from a given universal string
405        * description.
406        * Provides mapping between Emacs-like textual key descriptions and the
407        * <code>KeyStroke</code> object used in Swing.
408        * <P>
409        * This format has following form:
410        * <P><code>[C][A][S][M]-<em>identifier</em></code>
411        * <p>Where:
412        * <UL>
413        * <LI> <code>C</code> stands for the Control key
414        * <LI> <code>A</code> stands for the Alt key
415        * <LI> <code>S</code> stands for the Shift key
416        * <LI> <code>M</code> stands for the Meta key
417        * </UL>
418        * The format also supports two wildcard codes, to support differences in
419        * platforms.  These are the preferred choices for registering keystrokes,
420        * since platform conflicts will automatically be handled:
421        * <UL>
422        * <LI> <code>D</code> stands for the default menu accelerator - the Control
423        *  key on most platforms, the Command (meta) key on Macintosh</LI>
424        * <LI> <code>O</code> stands for the alternate accelerator - the Alt key on
425        *  most platforms, the Ctrl key on Macintosh (Macintosh uses Alt as a
426        *  secondary shift key for composing international characters - if you bind
427        *  Alt-8 to an action, a mac user with a French keyboard will not be able
428        *  to type the <code>[</code> character, which is a significant handicap</LI>
429        * </UL>
430        * If you use the wildcard characters, and specify a key which will conflict
431        * with keys the operating system consumes, it will be mapped to whichever
432        * choice can work - for example, on Macintosh, Command-Q is always consumed
433        * by the operating system, so <code>D-Q</code> will always map to Control-Q.
434        * <p>
435        * Every modifier before the hyphen must be pressed.
436        * <em>identifier</EM> can be any text constant from {@link KeyEvent} but
437        * without the leading <code>VK_</code> characters. So {@link KeyEvent#VK_ENTER} is described as
438        * <code>ENTER</code>.
439        *
440        * @param s the string with the description of the key
441        * @return key description object, or <code>null</code> if the string does not represent any valid key
442        */
443        public static KeyStroke stringToKey(String s) {
444            StringTokenizer st = new StringTokenizer(s.toUpperCase(Locale.ENGLISH), "-", true); // NOI18N
445    
446            int needed = 0;
447    
448            HashMap names = initNameAndValues()[0];
449    
450            int lastModif = -1;
451    
452            try {
453                for (;;) {
454                    String el = st.nextToken();
455    
456                    // required key
457                    if (el.equals("-")) { // NOI18N
458    
459                        if (lastModif != -1) {
460                            needed |= lastModif;
461                            lastModif = -1;
462                        }
463    
464                        continue;
465                    }
466    
467                    // if there is more elements
468                    if (st.hasMoreElements()) {
469                        // the text should describe modifiers
470                        lastModif = readModifiers(el);
471                    } else {
472                        // last text must be the key code
473                        Integer i = (Integer) names.get(el);
474                        boolean wildcard = (needed & CTRL_WILDCARD_MASK) != 0;
475    
476                        //Strip out the explicit mask - KeyStroke won't know
477                        //what to do with it
478                        needed = needed & ~CTRL_WILDCARD_MASK;
479    
480                        boolean macAlt = (needed & ALT_WILDCARD_MASK) != 0;
481                        needed = needed & ~ALT_WILDCARD_MASK;
482    
483                        if (i != null) {
484                            //#26854 - Default accelerator should be Command on mac
485                            if (wildcard) {
486                                needed |= getMenuShortCutKeyMask();
487    
488                                if ((getOperatingSystem() & OS_MAC) != 0) {
489                                    if (!usableKeyOnMac(i.intValue(), needed)) {
490                                        needed &= ~getMenuShortCutKeyMask();
491                                        needed |= KeyEvent.CTRL_MASK;
492                                    }
493                                }
494                            }
495    
496                            if (macAlt) {
497                                if (getOperatingSystem() == OS_MAC) {
498                                    needed |= KeyEvent.CTRL_MASK;
499                                } else {
500                                    needed |= KeyEvent.ALT_MASK;
501                                }
502                            }
503    
504                            return KeyStroke.getKeyStroke(i.intValue(), needed);
505                        } else {
506                            return null;
507                        }
508                    }
509                }
510            } catch (NoSuchElementException ex) {
511                return null;
512            }
513        }
514        /**
515         * need to guard against headlessExceptions when testing.
516         * @return the acceletor mask for shortcuts.
517         */
518        private static int getMenuShortCutKeyMask() {
519            if (GraphicsEnvironment.isHeadless()) {
520                return ((getOperatingSystem() & OS_MAC) != 0) ? 
521                        KeyEvent.META_MASK : KeyEvent.CTRL_MASK;
522            }
523     
524            return Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
525        }
526    
527        private static boolean usableKeyOnMac(int key, int mask) {
528            //All permutations fail for Q except ctrl
529            if (key == KeyEvent.VK_Q) {
530                return false;
531            }
532    
533            boolean isMeta = ((mask & KeyEvent.META_MASK) != 0) || ((mask & KeyEvent.CTRL_DOWN_MASK) != 0);
534    
535            boolean isAlt = ((mask & KeyEvent.ALT_MASK) != 0) || ((mask & KeyEvent.ALT_DOWN_MASK) != 0);
536    
537            boolean isOnlyMeta = isMeta && ((mask & ~(KeyEvent.META_DOWN_MASK | KeyEvent.META_MASK)) == 0);
538    
539            //Mac OS consumes keys Command+ these keys - the app will never see
540            //them, so CTRL should not be remapped for these
541            if (isOnlyMeta) {
542                return (key != KeyEvent.VK_H) && (key != KeyEvent.VK_SPACE) && (key != KeyEvent.VK_TAB);
543            } else return !((key == KeyEvent.VK_D) && isMeta && isAlt);
544        }
545    
546        /** Convert a space-separated list of Emacs-like key binding names to a list of Swing key strokes.
547        * @param s the string with keys
548        * @return array of key strokes, or <code>null</code> if the string description is not valid
549        * @see #stringToKey
550        */
551        public static KeyStroke[] stringToKeys(String s) {
552            StringTokenizer st = new StringTokenizer(s.toUpperCase(Locale.ENGLISH), " "); // NOI18N
553            ArrayList<KeyStroke> arr = new ArrayList<KeyStroke>();
554    
555            while (st.hasMoreElements()) {
556                s = st.nextToken();
557    
558                KeyStroke k = stringToKey(s);
559    
560                if (k == null) {
561                    return null;
562                }
563    
564                arr.add(k);
565            }
566    
567            return arr.toArray(new KeyStroke[arr.size()]);
568        }
569    
570        /** Adds characters for modifiers to the buffer.
571        * @param buf buffer to add to
572        * @param modif modifiers to add (KeyEvent.XXX_MASK)
573        * @return true if something has been added
574        */
575        private static boolean addModifiers(StringBuffer buf, int modif) {
576            boolean b = false;
577    
578            if ((modif & KeyEvent.CTRL_MASK) != 0) {
579                buf.append("C"); // NOI18N
580                b = true;
581            }
582    
583            if ((modif & KeyEvent.ALT_MASK) != 0) {
584                buf.append("A"); // NOI18N
585                b = true;
586            }
587    
588            if ((modif & KeyEvent.SHIFT_MASK) != 0) {
589                buf.append("S"); // NOI18N
590                b = true;
591            }
592    
593            if ((modif & KeyEvent.META_MASK) != 0) {
594                buf.append("M"); // NOI18N
595                b = true;
596            }
597    
598            if ((modif & CTRL_WILDCARD_MASK) != 0) {
599                buf.append("D");
600                b = true;
601            }
602    
603            if ((modif & ALT_WILDCARD_MASK) != 0) {
604                buf.append("O");
605                b = true;
606            }
607    
608            return b;
609        }
610    
611        /** Reads for modifiers and creates integer with required mask.
612        * @param s string with modifiers
613        * @return integer with mask
614        * @exception NoSuchElementException if some letter is not modifier
615        */
616        private static int readModifiers(String s) throws NoSuchElementException {
617            int m = 0;
618    
619            for (int i = 0; i < s.length(); i++) {
620                switch (s.charAt(i)) {
621                case 'C':
622                    m |= KeyEvent.CTRL_MASK;
623                    break;
624    
625                case 'A':
626                    m |= KeyEvent.ALT_MASK;
627                    break;
628    
629                case 'M':
630                    m |= KeyEvent.META_MASK;
631                    break;
632    
633                case 'S':
634                    m |= KeyEvent.SHIFT_MASK;
635                    break;
636    
637                case 'D':
638                    m |= CTRL_WILDCARD_MASK;
639                    break;
640    
641                case 'O':
642                    m |= ALT_WILDCARD_MASK;
643                    break;
644    
645                default:
646                    throw new NoSuchElementException(s);
647                }
648            }
649    
650            return m;
651        }
652        
653        /**
654        * Convert an array of objects to an array of primitive types.
655        * E.g. an <code>Integer[]</code> would be changed to an <code>int[]</code>.
656        * @param array the wrapper array
657        * @return a primitive array
658        * @throws IllegalArgumentException if the array element type is not a primitive wrapper
659        */
660        public static Object toPrimitiveArray(Object[] array) {
661            if (array instanceof Integer[]) {
662                int[] r = new int[array.length];
663                int i;
664                int k = array.length;
665    
666                for (i = 0; i < k; i++)
667                    r[i] = (array[i] == null) ? 0 : ((Integer) array[i]).intValue();
668    
669                return r;
670            }
671    
672            if (array instanceof Boolean[]) {
673                boolean[] r = new boolean[array.length];
674                int i;
675                int k = array.length;
676    
677                for (i = 0; i < k; i++)
678                    r[i] = (array[i] != null) && ((Boolean) array[i]).booleanValue();
679    
680                return r;
681            }
682    
683            if (array instanceof Byte[]) {
684                byte[] r = new byte[array.length];
685                int i;
686                int k = array.length;
687    
688                for (i = 0; i < k; i++)
689                    r[i] = (array[i] == null) ? 0 : ((Byte) array[i]).byteValue();
690    
691                return r;
692            }
693    
694            if (array instanceof Character[]) {
695                char[] r = new char[array.length];
696                int i;
697                int k = array.length;
698    
699                for (i = 0; i < k; i++)
700                    r[i] = (array[i] == null) ? 0 : ((Character) array[i]).charValue();
701    
702                return r;
703            }
704    
705            if (array instanceof Double[]) {
706                double[] r = new double[array.length];
707                int i;
708                int k = array.length;
709    
710                for (i = 0; i < k; i++)
711                    r[i] = (array[i] == null) ? 0 : ((Double) array[i]).doubleValue();
712    
713                return r;
714            }
715    
716            if (array instanceof Float[]) {
717                float[] r = new float[array.length];
718                int i;
719                int k = array.length;
720    
721                for (i = 0; i < k; i++)
722                    r[i] = (array[i] == null) ? 0 : ((Float) array[i]).floatValue();
723    
724                return r;
725            }
726    
727            if (array instanceof Long[]) {
728                long[] r = new long[array.length];
729                int i;
730                int k = array.length;
731    
732                for (i = 0; i < k; i++)
733                    r[i] = (array[i] == null) ? 0 : ((Long) array[i]).longValue();
734    
735                return r;
736            }
737    
738            if (array instanceof Short[]) {
739                short[] r = new short[array.length];
740                int i;
741                int k = array.length;
742    
743                for (i = 0; i < k; i++)
744                    r[i] = (array[i] == null) ? 0 : ((Short) array[i]).shortValue();
745    
746                return r;
747            }
748    
749            throw new IllegalArgumentException();
750        }
751        
752        /**
753        * Convert an array of primitive types to an array of objects.
754        * E.g. an <code>int[]</code> would be turned into an <code>Integer[]</code>.
755        * @param array the primitive array
756        * @return a wrapper array
757        * @throws IllegalArgumentException if the array element type is not primitive
758        */
759        public static Object[] toObjectArray(Object array) {
760            if (array instanceof Object[]) {
761                return (Object[]) array;
762            }
763    
764            if (array instanceof int[]) {
765                int i;
766                int k = ((int[]) array).length;
767                Integer[] r = new Integer[k];
768    
769                for (i = 0; i < k; i++)
770                    r[i] = new Integer(((int[]) array)[i]);
771    
772                return r;
773            }
774    
775            if (array instanceof boolean[]) {
776                int i;
777                int k = ((boolean[]) array).length;
778                Boolean[] r = new Boolean[k];
779    
780                for (i = 0; i < k; i++)
781                    r[i] = ((boolean[]) array)[i] ? Boolean.TRUE : Boolean.FALSE;
782    
783                return r;
784            }
785    
786            if (array instanceof byte[]) {
787                int i;
788                int k = ((byte[]) array).length;
789                Byte[] r = new Byte[k];
790    
791                for (i = 0; i < k; i++)
792                    r[i] = new Byte(((byte[]) array)[i]);
793    
794                return r;
795            }
796    
797            if (array instanceof char[]) {
798                int i;
799                int k = ((char[]) array).length;
800                Character[] r = new Character[k];
801    
802                for (i = 0; i < k; i++)
803                    r[i] = new Character(((char[]) array)[i]);
804    
805                return r;
806            }
807    
808            if (array instanceof double[]) {
809                int i;
810                int k = ((double[]) array).length;
811                Double[] r = new Double[k];
812    
813                for (i = 0; i < k; i++)
814                    r[i] = new Double(((double[]) array)[i]);
815    
816                return r;
817            }
818    
819            if (array instanceof float[]) {
820                int i;
821                int k = ((float[]) array).length;
822                Float[] r = new Float[k];
823    
824                for (i = 0; i < k; i++)
825                    r[i] = new Float(((float[]) array)[i]);
826    
827                return r;
828            }
829    
830            if (array instanceof long[]) {
831                int i;
832                int k = ((long[]) array).length;
833                Long[] r = new Long[k];
834    
835                for (i = 0; i < k; i++)
836                    r[i] = new Long(((long[]) array)[i]);
837    
838                return r;
839            }
840    
841            if (array instanceof short[]) {
842                int i;
843                int k = ((short[]) array).length;
844                Short[] r = new Short[k];
845    
846                for (i = 0; i < k; i++)
847                    r[i] = new Short(((short[]) array)[i]);
848    
849                return r;
850            }
851    
852            throw new IllegalArgumentException();
853        }
854    
855        /** Wrap multi-line strings (and get the individual lines).
856        * @param original  the original string to wrap
857        * @param width     the maximum width of lines
858        * @param breakIterator breaks original to chars, words, sentences, depending on what instance you provide.
859        * @param removeNewLines if <code>true</code>, any newlines in the original string are ignored
860        * @return the lines after wrapping
861        */
862        public static String[] wrapStringToArray(
863            String original, int width, BreakIterator breakIterator, boolean removeNewLines
864        ) {
865            if (original.length() == 0) {
866                return new String[] { original };
867            }
868    
869            String[] workingSet;
870    
871            // substitute original newlines with spaces,
872            // remove newlines from head and tail
873            if (removeNewLines) {
874                original = trimString(original);
875                original = original.replace('\n', ' ');
876                workingSet = new String[] { original };
877            } else {
878                StringTokenizer tokens = new StringTokenizer(original, "\n"); // NOI18N
879                int len = tokens.countTokens();
880                workingSet = new String[len];
881    
882                for (int i = 0; i < len; i++) {
883                    workingSet[i] = tokens.nextToken();
884                }
885            }
886    
887            if (width < 1) {
888                width = 1;
889            }
890    
891            if (original.length() <= width) {
892                return workingSet;
893            }
894    
895    widthcheck:  {
896                boolean ok = true;
897    
898                for (int i = 0; i < workingSet.length; i++) {
899                    ok = ok && (workingSet[i].length() < width);
900    
901                    if (!ok) {
902                        break widthcheck;
903                    }
904                }
905    
906                return workingSet;
907            }
908    
909            java.util.ArrayList<String> lines = new java.util.ArrayList<String>();
910    
911            int lineStart = 0; // the position of start of currently processed line in the original string
912    
913            for (int i = 0; i < workingSet.length; i++) {
914                if (workingSet[i].length() < width) {
915                    lines.add(workingSet[i]);
916                } else {
917                    breakIterator.setText(workingSet[i]);
918    
919                    int nextStart = breakIterator.next();
920                    int prevStart = 0;
921    
922                    do {
923                        while (((nextStart - lineStart) < width) && (nextStart != BreakIterator.DONE)) {
924                            prevStart = nextStart;
925                            nextStart = breakIterator.next();
926                        }
927    
928                        if (nextStart == BreakIterator.DONE) {
929                            nextStart = prevStart = workingSet[i].length();
930                        }
931    
932                        if (prevStart == 0) {
933                            prevStart = nextStart;
934                        }
935    
936                        lines.add(workingSet[i].substring(lineStart, prevStart));
937    
938                        lineStart = prevStart;
939                        prevStart = 0;
940                    } while (lineStart < workingSet[i].length());
941    
942                    lineStart = 0;
943                }
944            }
945    
946            String[] s = new String[lines.size()];
947    
948            return (String[]) lines.toArray(s);
949        }
950        
951        private static String trimString(String s) {
952            int idx = 0;
953            char c;
954            final int slen = s.length();
955    
956            if (slen == 0) {
957                return s;
958            }
959    
960            do {
961                c = s.charAt(idx++);
962            } while (((c == '\n') || (c == '\r')) && (idx < slen));
963    
964            s = s.substring(--idx);
965            idx = s.length() - 1;
966    
967            if (idx < 0) {
968                return s;
969            }
970    
971            do {
972                c = s.charAt(idx--);
973            } while (((c == '\n') || (c == '\r')) && (idx >= 0));
974    
975            return s.substring(0, idx + 2);
976        }    
977    }