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

org.jdesktop.swingx.util.Utilities Maven / Gradle / Ivy

There is a newer version: 1.6.8
Show newest version
/*
 * $Id$
 *
 * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle,
 * Santa Clara, California 95054, U.S.A. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */
package org.jdesktop.swingx.util;


import java.awt.Component;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.awt.Insets;
import java.awt.KeyboardFocusManager;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.KeyEvent;
import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.text.BreakIterator;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Locale;
import java.util.NoSuchElementException;
import java.util.StringTokenizer;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;

/**
 * Contribution from NetBeans: Issue #319-swingx. 

* * PENDING: need to reconcile with OS, JVM... added as-is * because needed the shortcut handling to fix # * * @author apple */ public class Utilities { private Utilities() { } private static final int CTRL_WILDCARD_MASK = 32768; private static final int ALT_WILDCARD_MASK = CTRL_WILDCARD_MASK * 2; /** Operating system is Windows NT. */ public static final int OS_WINNT = 1 << 0; /** Operating system is Windows 95. */ public static final int OS_WIN95 = OS_WINNT << 1; /** Operating system is Windows 98. */ public static final int OS_WIN98 = OS_WIN95 << 1; /** Operating system is Solaris. */ public static final int OS_SOLARIS = OS_WIN98 << 1; /** Operating system is Linux. */ public static final int OS_LINUX = OS_SOLARIS << 1; /** Operating system is HP-UX. */ public static final int OS_HP = OS_LINUX << 1; /** Operating system is IBM AIX. */ public static final int OS_AIX = OS_HP << 1; /** Operating system is SGI IRIX. */ public static final int OS_IRIX = OS_AIX << 1; /** Operating system is Sun OS. */ public static final int OS_SUNOS = OS_IRIX << 1; /** Operating system is Compaq TRU64 Unix */ public static final int OS_TRU64 = OS_SUNOS << 1; /** Operating system is OS/2. */ public static final int OS_OS2 = OS_TRU64 << 2; /** Operating system is Mac. */ public static final int OS_MAC = OS_OS2 << 1; /** Operating system is Windows 2000. */ public static final int OS_WIN2000 = OS_MAC << 1; /** Operating system is Compaq OpenVMS */ public static final int OS_VMS = OS_WIN2000 << 1; /** *Operating system is one of the Windows variants but we don't know which *one it is */ public static final int OS_WIN_OTHER = OS_VMS << 1; /** Operating system is unknown. */ public static final int OS_OTHER = OS_WIN_OTHER << 1; /** Operating system is FreeBSD * @since 4.50 */ public static final int OS_FREEBSD = OS_OTHER << 1; /** A mask for Windows platforms. */ public static final int OS_WINDOWS_MASK = OS_WINNT | OS_WIN95 | OS_WIN98 | OS_WIN2000 | OS_WIN_OTHER; /** A mask for Unix platforms. */ public static final int OS_UNIX_MASK = OS_SOLARIS | OS_LINUX | OS_HP | OS_AIX | OS_IRIX | OS_SUNOS | OS_TRU64 | OS_MAC | OS_FREEBSD; /** A height of the windows's taskbar */ public static final int TYPICAL_WINDOWS_TASKBAR_HEIGHT = 27; /** A height of the Mac OS X's menu */ private static final int TYPICAL_MACOSX_MENU_HEIGHT = 24; private static int operatingSystem = -1; /** reference to map that maps allowed key names to their values (String, Integer) and reference to map for mapping of values to their names */ private static Reference namesAndValues; /** Get the operating system on which NetBeans is running. * @return one of the OS_* constants (such as {@link #OS_WINNT}) */ public static int getOperatingSystem() { if (operatingSystem == -1) { String osName = System.getProperty("os.name"); if ("Windows NT".equals(osName)) { // NOI18N operatingSystem = OS_WINNT; } else if ("Windows 95".equals(osName)) { // NOI18N operatingSystem = OS_WIN95; } else if ("Windows 98".equals(osName)) { // NOI18N operatingSystem = OS_WIN98; } else if ("Windows 2000".equals(osName)) { // NOI18N operatingSystem = OS_WIN2000; } else if (osName.startsWith("Windows ")) { // NOI18N operatingSystem = OS_WIN_OTHER; } else if ("Solaris".equals(osName)) { // NOI18N operatingSystem = OS_SOLARIS; } else if (osName.startsWith("SunOS")) { // NOI18N operatingSystem = OS_SOLARIS; } // JDK 1.4 b2 defines os.name for me as "Redhat Linux" -jglick else if (osName.endsWith("Linux")) { // NOI18N operatingSystem = OS_LINUX; } else if ("HP-UX".equals(osName)) { // NOI18N operatingSystem = OS_HP; } else if ("AIX".equals(osName)) { // NOI18N operatingSystem = OS_AIX; } else if ("Irix".equals(osName)) { // NOI18N operatingSystem = OS_IRIX; } else if ("SunOS".equals(osName)) { // NOI18N operatingSystem = OS_SUNOS; } else if ("Digital UNIX".equals(osName)) { // NOI18N operatingSystem = OS_TRU64; } else if ("OS/2".equals(osName)) { // NOI18N operatingSystem = OS_OS2; } else if ("OpenVMS".equals(osName)) { // NOI18N operatingSystem = OS_VMS; } else if (osName.equals("Mac OS X")) { // NOI18N operatingSystem = OS_MAC; } else if (osName.startsWith("Darwin")) { // NOI18N operatingSystem = OS_MAC; } else if (osName.toLowerCase(Locale.US).startsWith("freebsd")) { // NOI18N operatingSystem = OS_FREEBSD; } else { operatingSystem = OS_OTHER; } } return operatingSystem; } /** Test whether NetBeans is running on some variant of Windows. * @return true if Windows, false if some other manner of operating system */ public static boolean isWindows() { return (getOperatingSystem() & OS_WINDOWS_MASK) != 0; } /** Test whether NetBeans is running on some variant of Unix. * Linux is included as well as the commercial vendors, and Mac OS X. * @return true some sort of Unix, false if some other manner of operating system */ public static boolean isUnix() { return (getOperatingSystem() & OS_UNIX_MASK) != 0; } /** Test whether the operating system supports icons on frames (windows). * @return true if it does not * */ public static boolean isLargeFrameIcons() { return (getOperatingSystem() == OS_SOLARIS) || (getOperatingSystem() == OS_HP); } /** * Finds out the monitor where the user currently has the input focus. * This method is usually used to help the client code to figure out on * which monitor it should place newly created windows/frames/dialogs. * * @return the GraphicsConfiguration of the monitor which currently has the * input focus */ private static GraphicsConfiguration getCurrentGraphicsConfiguration() { Component focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(); if (focusOwner != null) { Window w = SwingUtilities.getWindowAncestor(focusOwner); if (w != null) { return w.getGraphicsConfiguration(); } } return GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration(); } /** * Returns the usable area of the screen where applications can place its * windows. The method subtracts from the screen the area of taskbars, * system menus and the like. The screen this method applies to is the one * which is considered current, ussually the one where the current input * focus is. * * @return the rectangle of the screen where one can place windows * * @since 2.5 */ public static Rectangle getUsableScreenBounds() { return getUsableScreenBounds(getCurrentGraphicsConfiguration()); } /** * Returns the usable area of the screen where applications can place its * windows. The method subtracts from the screen the area of taskbars, * system menus and the like. * * @param gconf the GraphicsConfiguration of the monitor * @return the rectangle of the screen where one can place windows * * @since 2.5 */ public static Rectangle getUsableScreenBounds(GraphicsConfiguration gconf) { if (gconf == null) { gconf = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration(); } Rectangle bounds = new Rectangle(gconf.getBounds()); String str; str = System.getProperty("netbeans.screen.insets"); // NOI18N if (str != null) { StringTokenizer st = new StringTokenizer(str, ", "); // NOI18N if (st.countTokens() == 4) { try { bounds.y = Integer.parseInt(st.nextToken()); bounds.x = Integer.parseInt(st.nextToken()); bounds.height -= (bounds.y + Integer.parseInt(st.nextToken())); bounds.width -= (bounds.x + Integer.parseInt(st.nextToken())); } catch (NumberFormatException ex) { Logger.getAnonymousLogger().log(Level.WARNING, null, ex); } } return bounds; } str = System.getProperty("netbeans.taskbar.height"); // NOI18N if (str != null) { bounds.height -= Integer.getInteger(str, 0).intValue(); return bounds; } try { Toolkit toolkit = Toolkit.getDefaultToolkit(); Insets insets = toolkit.getScreenInsets(gconf); bounds.y += insets.top; bounds.x += insets.left; bounds.height -= (insets.top + insets.bottom); bounds.width -= (insets.left + insets.right); } catch (Exception ex) { Logger.getAnonymousLogger().log(Level.WARNING, null, ex); } return bounds; } /** Initialization of the names and values * @return array of two hashmaps first maps * allowed key names to their values (String, Integer) * and second * hashtable for mapping of values to their names (Integer, String) */ private static synchronized HashMap[] initNameAndValues() { if (namesAndValues != null) { HashMap[] arr = (HashMap[]) namesAndValues.get(); if (arr != null) { return arr; } } Field[] fields; // JW - fix Issue #353-swingx: play nicer inside sandbox. try { fields = KeyEvent.class.getDeclaredFields(); // fields = KeyEvent.class.getFields(); } catch (SecurityException e) { // JW: need to do better? What are the use-cases where we don't have // any access to the fields? fields = new Field[0]; } HashMap names = new HashMap(((fields.length * 4) / 3) + 5, 0.75f); HashMap values = new HashMap(((fields.length * 4) / 3) + 5, 0.75f); for (int i = 0; i < fields.length; i++) { if (Modifier.isStatic(fields[i].getModifiers())) { String name = fields[i].getName(); if (name.startsWith("VK_")) { // NOI18N // exclude VK name = name.substring(3); try { int numb = fields[i].getInt(null); Integer value = new Integer(numb); names.put(name, value); values.put(value, name); } catch (IllegalArgumentException ex) { } catch (IllegalAccessException ex) { } } } } if (names.get("CONTEXT_MENU") == null) { // NOI18N Integer n = new Integer(0x20C); names.put("CONTEXT_MENU", n); // NOI18N values.put(n, "CONTEXT_MENU"); // NOI18N n = new Integer(0x20D); names.put("WINDOWS", n); // NOI18N values.put(n, "WINDOWS"); // NOI18N } HashMap[] arr = { names, values }; namesAndValues = new SoftReference(arr); return arr; } /** Converts a Swing key stroke descriptor to a familiar Emacs-like name. * @param stroke key description * @return name of the key (e.g. CS-F1 for control-shift-function key one) * @see #stringToKey */ public static String keyToString(KeyStroke stroke) { StringBuffer sb = new StringBuffer(); // add modifiers that must be pressed if (addModifiers(sb, stroke.getModifiers())) { sb.append('-'); } HashMap[] namesAndValues = initNameAndValues(); String c = (String) namesAndValues[1].get(new Integer(stroke.getKeyCode())); if (c == null) { sb.append(stroke.getKeyChar()); } else { sb.append(c); } return sb.toString(); } /** Construct a new key description from a given universal string * description. * Provides mapping between Emacs-like textual key descriptions and the * KeyStroke object used in Swing. *

* This format has following form: *

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

Where: *

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

* Every modifier before the hyphen must be pressed. * identifier can be any text constant from {@link KeyEvent} but * without the leading VK_ characters. So {@link KeyEvent#VK_ENTER} is described as * ENTER. * * @param s the string with the description of the key * @return key description object, or null if the string does not represent any valid key */ public static KeyStroke stringToKey(String s) { StringTokenizer st = new StringTokenizer(s.toUpperCase(Locale.ENGLISH), "-", true); // NOI18N int needed = 0; HashMap names = initNameAndValues()[0]; int lastModif = -1; try { for (;;) { String el = st.nextToken(); // required key if (el.equals("-")) { // NOI18N if (lastModif != -1) { needed |= lastModif; lastModif = -1; } continue; } // if there is more elements if (st.hasMoreElements()) { // the text should describe modifiers lastModif = readModifiers(el); } else { // last text must be the key code Integer i = (Integer) names.get(el); boolean wildcard = (needed & CTRL_WILDCARD_MASK) != 0; //Strip out the explicit mask - KeyStroke won't know //what to do with it needed = needed & ~CTRL_WILDCARD_MASK; boolean macAlt = (needed & ALT_WILDCARD_MASK) != 0; needed = needed & ~ALT_WILDCARD_MASK; if (i != null) { //#26854 - Default accelerator should be Command on mac if (wildcard) { needed |= getMenuShortCutKeyMask(); if ((getOperatingSystem() & OS_MAC) != 0) { if (!usableKeyOnMac(i.intValue(), needed)) { needed &= ~getMenuShortCutKeyMask(); needed |= KeyEvent.CTRL_MASK; } } } if (macAlt) { if (getOperatingSystem() == OS_MAC) { needed |= KeyEvent.CTRL_MASK; } else { needed |= KeyEvent.ALT_MASK; } } return KeyStroke.getKeyStroke(i.intValue(), needed); } else { return null; } } } } catch (NoSuchElementException ex) { return null; } } /** * need to guard against headlessExceptions when testing. * @return the acceletor mask for shortcuts. */ private static int getMenuShortCutKeyMask() { if (GraphicsEnvironment.isHeadless()) { return ((getOperatingSystem() & OS_MAC) != 0) ? KeyEvent.META_MASK : KeyEvent.CTRL_MASK; } return Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(); } private static boolean usableKeyOnMac(int key, int mask) { //All permutations fail for Q except ctrl if (key == KeyEvent.VK_Q) { return false; } boolean isMeta = ((mask & KeyEvent.META_MASK) != 0) || ((mask & KeyEvent.CTRL_DOWN_MASK) != 0); boolean isAlt = ((mask & KeyEvent.ALT_MASK) != 0) || ((mask & KeyEvent.ALT_DOWN_MASK) != 0); boolean isOnlyMeta = isMeta && ((mask & ~(KeyEvent.META_DOWN_MASK | KeyEvent.META_MASK)) == 0); //Mac OS consumes keys Command+ these keys - the app will never see //them, so CTRL should not be remapped for these if (isOnlyMeta) { return (key != KeyEvent.VK_H) && (key != KeyEvent.VK_SPACE) && (key != KeyEvent.VK_TAB); } else return !((key == KeyEvent.VK_D) && isMeta && isAlt); } /** Convert a space-separated list of Emacs-like key binding names to a list of Swing key strokes. * @param s the string with keys * @return array of key strokes, or null if the string description is not valid * @see #stringToKey */ public static KeyStroke[] stringToKeys(String s) { StringTokenizer st = new StringTokenizer(s.toUpperCase(Locale.ENGLISH), " "); // NOI18N ArrayList arr = new ArrayList(); while (st.hasMoreElements()) { s = st.nextToken(); KeyStroke k = stringToKey(s); if (k == null) { return null; } arr.add(k); } return arr.toArray(new KeyStroke[arr.size()]); } /** Adds characters for modifiers to the buffer. * @param buf buffer to add to * @param modif modifiers to add (KeyEvent.XXX_MASK) * @return true if something has been added */ private static boolean addModifiers(StringBuffer buf, int modif) { boolean b = false; if ((modif & KeyEvent.CTRL_MASK) != 0) { buf.append("C"); // NOI18N b = true; } if ((modif & KeyEvent.ALT_MASK) != 0) { buf.append("A"); // NOI18N b = true; } if ((modif & KeyEvent.SHIFT_MASK) != 0) { buf.append("S"); // NOI18N b = true; } if ((modif & KeyEvent.META_MASK) != 0) { buf.append("M"); // NOI18N b = true; } if ((modif & CTRL_WILDCARD_MASK) != 0) { buf.append("D"); b = true; } if ((modif & ALT_WILDCARD_MASK) != 0) { buf.append("O"); b = true; } return b; } /** Reads for modifiers and creates integer with required mask. * @param s string with modifiers * @return integer with mask * @exception NoSuchElementException if some letter is not modifier */ private static int readModifiers(String s) throws NoSuchElementException { int m = 0; for (int i = 0; i < s.length(); i++) { switch (s.charAt(i)) { case 'C': m |= KeyEvent.CTRL_MASK; break; case 'A': m |= KeyEvent.ALT_MASK; break; case 'M': m |= KeyEvent.META_MASK; break; case 'S': m |= KeyEvent.SHIFT_MASK; break; case 'D': m |= CTRL_WILDCARD_MASK; break; case 'O': m |= ALT_WILDCARD_MASK; break; default: throw new NoSuchElementException(s); } } return m; } /** * Convert an array of objects to an array of primitive types. * E.g. an Integer[] would be changed to an int[]. * @param array the wrapper array * @return a primitive array * @throws IllegalArgumentException if the array element type is not a primitive wrapper */ public static Object toPrimitiveArray(Object[] array) { if (array instanceof Integer[]) { int[] r = new int[array.length]; int i; int k = array.length; for (i = 0; i < k; i++) r[i] = (array[i] == null) ? 0 : ((Integer) array[i]).intValue(); return r; } if (array instanceof Boolean[]) { boolean[] r = new boolean[array.length]; int i; int k = array.length; for (i = 0; i < k; i++) r[i] = (array[i] != null) && ((Boolean) array[i]).booleanValue(); return r; } if (array instanceof Byte[]) { byte[] r = new byte[array.length]; int i; int k = array.length; for (i = 0; i < k; i++) r[i] = (array[i] == null) ? 0 : ((Byte) array[i]).byteValue(); return r; } if (array instanceof Character[]) { char[] r = new char[array.length]; int i; int k = array.length; for (i = 0; i < k; i++) r[i] = (array[i] == null) ? 0 : ((Character) array[i]).charValue(); return r; } if (array instanceof Double[]) { double[] r = new double[array.length]; int i; int k = array.length; for (i = 0; i < k; i++) r[i] = (array[i] == null) ? 0 : ((Double) array[i]).doubleValue(); return r; } if (array instanceof Float[]) { float[] r = new float[array.length]; int i; int k = array.length; for (i = 0; i < k; i++) r[i] = (array[i] == null) ? 0 : ((Float) array[i]).floatValue(); return r; } if (array instanceof Long[]) { long[] r = new long[array.length]; int i; int k = array.length; for (i = 0; i < k; i++) r[i] = (array[i] == null) ? 0 : ((Long) array[i]).longValue(); return r; } if (array instanceof Short[]) { short[] r = new short[array.length]; int i; int k = array.length; for (i = 0; i < k; i++) r[i] = (array[i] == null) ? 0 : ((Short) array[i]).shortValue(); return r; } throw new IllegalArgumentException(); } /** * Convert an array of primitive types to an array of objects. * E.g. an int[] would be turned into an Integer[]. * @param array the primitive array * @return a wrapper array * @throws IllegalArgumentException if the array element type is not primitive */ public static Object[] toObjectArray(Object array) { if (array instanceof Object[]) { return (Object[]) array; } if (array instanceof int[]) { int i; int k = ((int[]) array).length; Integer[] r = new Integer[k]; for (i = 0; i < k; i++) r[i] = new Integer(((int[]) array)[i]); return r; } if (array instanceof boolean[]) { int i; int k = ((boolean[]) array).length; Boolean[] r = new Boolean[k]; for (i = 0; i < k; i++) r[i] = ((boolean[]) array)[i] ? Boolean.TRUE : Boolean.FALSE; return r; } if (array instanceof byte[]) { int i; int k = ((byte[]) array).length; Byte[] r = new Byte[k]; for (i = 0; i < k; i++) r[i] = new Byte(((byte[]) array)[i]); return r; } if (array instanceof char[]) { int i; int k = ((char[]) array).length; Character[] r = new Character[k]; for (i = 0; i < k; i++) r[i] = new Character(((char[]) array)[i]); return r; } if (array instanceof double[]) { int i; int k = ((double[]) array).length; Double[] r = new Double[k]; for (i = 0; i < k; i++) r[i] = new Double(((double[]) array)[i]); return r; } if (array instanceof float[]) { int i; int k = ((float[]) array).length; Float[] r = new Float[k]; for (i = 0; i < k; i++) r[i] = new Float(((float[]) array)[i]); return r; } if (array instanceof long[]) { int i; int k = ((long[]) array).length; Long[] r = new Long[k]; for (i = 0; i < k; i++) r[i] = new Long(((long[]) array)[i]); return r; } if (array instanceof short[]) { int i; int k = ((short[]) array).length; Short[] r = new Short[k]; for (i = 0; i < k; i++) r[i] = new Short(((short[]) array)[i]); return r; } throw new IllegalArgumentException(); } /** Wrap multi-line strings (and get the individual lines). * @param original the original string to wrap * @param width the maximum width of lines * @param breakIterator breaks original to chars, words, sentences, depending on what instance you provide. * @param removeNewLines if true, any newlines in the original string are ignored * @return the lines after wrapping */ public static String[] wrapStringToArray( String original, int width, BreakIterator breakIterator, boolean removeNewLines ) { if (original.length() == 0) { return new String[] { original }; } String[] workingSet; // substitute original newlines with spaces, // remove newlines from head and tail if (removeNewLines) { original = trimString(original); original = original.replace('\n', ' '); workingSet = new String[] { original }; } else { StringTokenizer tokens = new StringTokenizer(original, "\n"); // NOI18N int len = tokens.countTokens(); workingSet = new String[len]; for (int i = 0; i < len; i++) { workingSet[i] = tokens.nextToken(); } } if (width < 1) { width = 1; } if (original.length() <= width) { return workingSet; } widthcheck: { boolean ok = true; for (int i = 0; i < workingSet.length; i++) { ok = ok && (workingSet[i].length() < width); if (!ok) { break widthcheck; } } return workingSet; } java.util.ArrayList lines = new java.util.ArrayList(); int lineStart = 0; // the position of start of currently processed line in the original string for (int i = 0; i < workingSet.length; i++) { if (workingSet[i].length() < width) { lines.add(workingSet[i]); } else { breakIterator.setText(workingSet[i]); int nextStart = breakIterator.next(); int prevStart = 0; do { while (((nextStart - lineStart) < width) && (nextStart != BreakIterator.DONE)) { prevStart = nextStart; nextStart = breakIterator.next(); } if (nextStart == BreakIterator.DONE) { nextStart = prevStart = workingSet[i].length(); } if (prevStart == 0) { prevStart = nextStart; } lines.add(workingSet[i].substring(lineStart, prevStart)); lineStart = prevStart; prevStart = 0; } while (lineStart < workingSet[i].length()); lineStart = 0; } } String[] s = new String[lines.size()]; return (String[]) lines.toArray(s); } private static String trimString(String s) { int idx = 0; char c; final int slen = s.length(); if (slen == 0) { return s; } do { c = s.charAt(idx++); } while (((c == '\n') || (c == '\r')) && (idx < slen)); s = s.substring(--idx); idx = s.length() - 1; if (idx < 0) { return s; } do { c = s.charAt(idx--); } while (((c == '\n') || (c == '\r')) && (idx >= 0)); return s.substring(0, idx + 2); } }