//
// @(#)Utils.java 4/2002
//
// Copyright 2002 Zachary DelProposto. All rights reserved.
// Use is subject to license terms.
//
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
// Or from http://www.gnu.org/
//
package dip.misc;
import java.awt.Color;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
//import dip.gui.dialog.ErrorDialog;
/**
* Various static utilities used by GUI and non-GUI classes.
* <pre>
* NOTE:
* to go from a String resource to a URL, use
* Utils.getURL()
* e.g., for a resource, called common/test.txt
* to use with Utils.getText():
* String RESOURCE = "resource/common/test.txt";
*
* String myText = Utils.getText(RESOURCE);
* to get the URL:
* URL url = Utils.getURL(RESOURCE);
* JeditorPane editorPane.setPage(url);
* </pre>
*/
public class Utils
{
// public constants
//public static final Border EMPTY_BORDER_5 = new EmptyBorder(5,5,5,5);
//public static final Border EMPTY_BORDER_10 = new EmptyBorder(10,10,10,10);
// private resource constants
public static final String UTILS_RES_NOT_FOUND = "UTILS_RES_NOT_FOUND";
public static final String UTILS_RES_ERR_DLG_TITLE = "UTILS_RES_ERR_DLG_TITLE";
public static final String UTILS_RES_ERR_DLG_TEXT = "UTILS_RES_ERR_DLG_TEXT";
public static final String FRAME_ICON = "resource/common/icons/frame-corner.png";
// private constants
private static final int TEXT_INSETS = 5;
private static final String HEX[] = {"0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f"};
private static final String RESOURCE_BASE_DIR = "resource/";
private static final String BASE_RESOURCE_FILE = "resource/il8n/i18ntext";
private static final String COMMON_RESOURCE_FILE = "resource/common/common";
private static final char[] EMAIL_ALLOWED = {'@','.','-','_','+','!'};
private static final char[] URL_ALLOWED = {' ',';','/','?',':','@','&','=','+','$',',','-','_','.','!','~','*','\'','|','%','#' };
// private regex
private static final Pattern REAL_COMMAS = Pattern.compile(",(?=([^\"]*\"[^\"]*\")*(?![^\"]*\"))");
private static ClassLoader classLoader = null;
private static Utils singleton = null;
private static ResourceBundle resourceBundle = null;
private static ResourceBundle commonBundle = null;
private static Locale chosenLocale = null;
private final static boolean isOSX;
private final static boolean isWindows;
// static code
static
{
singleton = new Utils();
classLoader = singleton.getClass().getClassLoader();
//toolkit = Toolkit.getDefaultToolkit();
isOSX = (System.getProperty("mrj.version", null) != null);
isWindows = (System.getProperty("os.name","").toLowerCase().indexOf("windows") >= 0);
// if a locale cannot be found, automatically defaults
// to the closest locale, or (at worst) BASE_RESOURCE_FILE.
chosenLocale = Locale.getDefault();
setResourceBundle(chosenLocale);
// now set the COMMON_RESOURCE_FILE
// this is not localized
try
{
commonBundle = ResourceBundle.getBundle(COMMON_RESOURCE_FILE, Locale.ENGLISH, classLoader);
}
catch(MissingResourceException mre)
{
//ErrorDialog.displayFatal(null, mre);
}
}
/** Force a load of a locale */
public static void loadLocale(Locale loc)
{
chosenLocale = loc;
setResourceBundle(chosenLocale);
}// loadLocale()
/** Get which Locale has been loaded or selected. */
public static Locale getLocale()
{
return chosenLocale;
}// getLocale()
/** Gets the classloader used for this class */
public static ClassLoader getClassLoader()
{
return classLoader;
}// getClassLoader()
/** Return the screen size. */
//public static Dimension getScreenSize()
//{
// return getScreenSize(1.0f);
//}// getScreenSize()
/**
* Returns the screen size multiplied by the given fraction,
* preserving the aspect ratio, and returns the result.
* <p>
* For example, getScreenSize(0.5f) would return a Dimension
* that was 400 x 300 if the screen size was 800 x 600 pixels.
*/
// public static Dimension getScreenSize(float fraction)
// {
// if(fraction <= 0)
// {
// throw new IllegalArgumentException("fraction <= 0");
// }
//
// Dimension size = toolkit.getScreenSize();
// size.width = (int) (size.width * fraction);
// size.height = (int) (size.height * fraction);
// return size;
// }// getScreenSize()
/**
* Returns the screen size multiplied by the given fraction,
* preserving the aspect ratio, and returns the result.
* <p>
* This is a hackish version, with two fractions. The second fraction
* is used for 'smaller' screens (defined as 800x600 or less).
*
*/
// public static Dimension getScreenSize(float fraction, float smallScreenFraction)
// {
// if(fraction <= 0 || smallScreenFraction <= 0 || smallScreenFraction < fraction)
// {
// throw new IllegalArgumentException("fraction <= 0");
// }
//
// Dimension size = toolkit.getScreenSize();
//
// final float f = (size.width <= 800 || size.height <= 600) ? smallScreenFraction : fraction;
//
// size.width = (int) (size.width * f);
// size.height = (int) (size.height * f);
// return size;
// }// getScreenSize()
/**
* Centers the component in the screen. If component is
* larger than the screen in a particular axis, then that
* axis is centered to the component.
*/
// public static void centerInScreen(Component c)
// {
// Dimension screenSize = toolkit.getScreenSize();
// Dimension componentSize = c.getSize();
//
// componentSize.width = (componentSize.width > screenSize.width) ? screenSize.width : componentSize.width;
// componentSize.height = (componentSize.height > screenSize.height) ? screenSize.height : componentSize.height;
//
// c.setLocation((screenSize.width - componentSize.width)/2, (screenSize.height - componentSize.height)/2);
// }// centerInScreen()
/**
* Centers the inner component within the outer component.
* If the outer component is null, or if the inner component
* is larger than the outer component (in either axis), the
* component is centered to the screen using centerInScreen().
*/
// public static void centerIn(Component inner, Component outer)
// {
// if(outer == null)
// {
// centerInScreen(inner);
// return;
// }
//
// Dimension parentSize = outer.getSize();
// Point pLoc = outer.getLocationOnScreen();
// Dimension componentSize = inner.getSize();
//
// if(componentSize.width > parentSize.width || componentSize.height > parentSize.height)
// {
// centerInScreen(inner);
// }
// else
// {
// inner.setLocation(pLoc.x+(parentSize.width - componentSize.width)/2, pLoc.y+(parentSize.height - componentSize.height)/2);
// }
// }// centerIn()
/**
* Appends the extension to the file, unless it was already
* appended. Will also use a '.' to separate if not contained
* in the extension.
*/
public static File appendExtension(File file, String ext)
{
String name = file.getName();
if(name.endsWith(ext))
{
return file;
}
StringBuffer sb = new StringBuffer(file.getPath());
if(ext.charAt(0) != '.')
{
sb.append('.');
}
sb.append(ext);
return new File(sb.toString());
}// appendExtension()
/** Create a <font> tag with the given color. */
// public static void setFontColor(StringBuffer sb, Color color)
// {
// setFontColor(sb, color.getRed(), color.getGreen(), color.getBlue());
// }// setFontColor()
/** Create a <font> tag with the given color. */
public static void setFontColor(StringBuffer sb, int r, int g, int b)
{
sb.append("<font color=\"#");
fastToHex(sb, r);
fastToHex(sb, g);
fastToHex(sb, b);
sb.append("\">");
}// setFontColor()
/** Popup an Error message dialog */
//public static void popupError(JFrame parent, String title, String text)
//{
// JOptionPane.showMessageDialog(parent, text, title, JOptionPane.ERROR_MESSAGE);
//}// popupError()
/** Popup an Info message dialog */
//public static void popupInfo(JFrame parent, String title, String text)
//{
// JOptionPane.showMessageDialog(parent, text, title, JOptionPane.INFORMATION_MESSAGE);
//}// popupError()
/**
* Gets the Resource Base (resource directory). This is needed
* for setting things like setBase() on HTML docuements.
*/
public static URL getResourceBase()
{
if(classLoader == null)
{
return null;
}
return classLoader.getResource(RESOURCE_BASE_DIR);
}// getResourceBase()
/** Get the resource base prefix. */
public static String getResourceBasePrefix()
{
return RESOURCE_BASE_DIR;
}// getResourceBasePrefix()
/********************************************************************
*
* Given a resource name, extract a URL for the given resource.
*
********************************************************************/
public static URL getURL(String name)
{
if(classLoader == null)
{
return null;
}
return classLoader.getResource(name);
}// getURL()
/********************************************************************
*
* Get an ImageIcon. Return null if an ImageIcon could not be found.
*
********************************************************************/
//public static ImageIcon getImageIcon(String name)
//{
// URL url = getURL(name);
// if(url != null)
// {
// return new ImageIcon( url );
// }
//
// return null;
//}// getImageIcon()
/********************************************************************
*
* Gets an InputStream to the named resource; throws an exception
* if the resource could not be found.
*
********************************************************************/
public static InputStream getInputStream(String name)
throws java.io.IOException
{
URL url = getURL(name);
if(url != null)
{
return url.openStream();
}
throw new FileNotFoundException( getLocalString(UTILS_RES_NOT_FOUND, name) );
}// getInputStream()
/********************************************************************
*
* Gets an InputStreamReader to the named resource.
*
********************************************************************/
public static InputStreamReader getInputStreamReader(String name)
throws java.io.IOException
{
URL url = getURL(name);
if(url != null)
{
return new InputStreamReader(url.openStream());
}
throw new FileNotFoundException( getLocalString(UTILS_RES_NOT_FOUND, name) );
}// getInputStreamReader()
/********************************************************************
*
* Gets a resource as a String.<p>
* Suitable for small text files / HTML files. May not be
* appropriate for larger text files.
* <p>
* A null string is returned if an error occurs.
*
********************************************************************/
public static String getText(String name)
{
BufferedReader br = null;
StringBuffer sb = null;
try
{
br = new BufferedReader(getInputStreamReader(name));
sb = new StringBuffer(4096);
String line = br.readLine();
while(line != null)
{
sb.append(line);
line = br.readLine();
}
return sb.toString();
}
catch(IOException e)
{
}
finally
{
if(br != null)
{
try
{
br.close();
}
catch(IOException e)
{}
}
}
return null;
}// getText()
/********************************************************************
*
* Gets a resource as a String.<p>
* Uses MessageFormat to replace the given argument with
* the argument provided.
*
*
********************************************************************/
public static String getText(String name, Object arg1)
{
return MessageFormat.format(getText(name), new Object[] {arg1});
}// getText()
/********************************************************************
*
* Gets a resource as a String.<p>
* Uses MessageFormat to replace the given argument with
* the arguments provided.
*
*
********************************************************************/
public static String getText(String name, Object[] args)
{
return MessageFormat.format(getText(name), args);
}// getText()
/********************************************************************
*
* Gets a resource as an Image.<p>
* Synchronous; may not suitable for large images.
* <p>
* null if error
*
********************************************************************/
// public static Image getImage(String name)
// {
// URL url = getURL(name);
// if(url != null)
// {
// return syncCreateImage(toolkit.getImage(url));
// }
//
// return null;
// }// getImage()
/**
*
* Returns an Image from the given URL.<p>
* Synchronous; may not suitable for large images.
* <p>
* null if error
*
*/
// public static Image getImage(URL url)
// {
// if(url != null)
// {
// return syncCreateImage(toolkit.getImage(url));
// }
//
// return null;
// }// getImage()
/********************************************************************
*
* Gets a resource as an Icon.<p>
* Synchronous; may not suitable for large images.
* <p>
* null if error
*
********************************************************************/
//public static Icon getIcon(String name)
//{
// URL url = getURL(name);
// if(url != null)
// {
// return new ImageIcon(url);
// }
// return null;
//}// getImage()
/**
* Ensure that the entire image has been created.
* <p>
* This method ensures that the Image desired has been
* fully realized (i.e., loaded or created) before
* returning a reference to it. If an error occurs, the
* returned Image will be <code>null</code>.
*
* @param ip ImageProducer image source.
*
* @return the completed Image object.
*/
// public static Image syncCreateImage(ImageProducer ip)
// {
// Image img = toolkit.createImage(ip);
// return syncCreateImage(img);
// }// syncCreateImage()
/**
* Ensure that the entire image has been created.
* <p>
* This method ensures that the Image desired has been
* fully realized (i.e., loaded or created) before
* returning a reference to it. If an error occurs, the
* returned Image will be <code>null</code>.
*
* @param img the image source.
*
* @return the completed Image object.
*/
// public static Image syncCreateImage(Image img)
// {
// synchronized(tracker)
// {
// tracker.addImage(img, 0);
//
// try
// {
// tracker.waitForID(0);
// tracker.removeImage(img, 0);
// }
// catch(InterruptedException e)
// {
// return null;
// }
//
// if(tracker.isErrorID(0))
// {
// return null;
// }
// }
// return img;
// }// syncCreateImage()
/********************************************************************
*
* Gets a resource-bundle String; this is for internationalization.
* <p>
* If resource is missing, a popup-error message is displayed.
* <p>.
*
********************************************************************/
public static String getLocalString(String key)
{
try
{
return resourceBundle.getString(key);
}
catch(Exception e)
{
e.printStackTrace();
//showNoLocalStringPopup(key, e);
}
return "[i18n:ERROR]";
}// getLocalString()
/********************************************************************
*
* Gets an array of resource-bundle Strings, for internationalization.
* <p>
* The Strings must be comma-separated. Spaces before/after are trimmed.
* Quotes have no special meaning.
* <p>.
*
********************************************************************/
public static String[] getLocalStringArray(String key)
{
String str = null;
try
{
str = resourceBundle.getString(key);
}
catch(Exception e)
{
e.printStackTrace();
//showNoLocalStringPopup(key, e);
return new String[0];
}
StringTokenizer st = new StringTokenizer(str, ",\n\r");
String[] array = new String[st.countTokens()];
for(int i=0; i<array.length; i++)
{
array[i] = st.nextToken().trim();
}
return array;
}// getLocalStringArray()
/********************************************************************
*
* Gets an array of resource-bundle ints, for internationalization.
* <p>
* The Integers must be comma-separated. Spaces before/after are trimmed.
* Quotes have no special meaning.
* <p>.
*
********************************************************************/
public static int[] getLocalIntArray(String key)
{
String[] str = getLocalStringArray(key);
int[] array = new int[str.length];
try
{
for(int i=0; i<array.length; i++)
{
array[i] = Integer.parseInt(str[i]);
}
}
catch(NumberFormatException e)
{
e.printStackTrace();
//showNoLocalStringPopup(key, e);
}
return array;
}// getLocalIntArray()
/********************************************************************
*
* Gets a resource-bundle String; this is for internationalization.
* Objects passed in are for MessageFormat arguments.
* <p>
* If resource is missing, a popup-error message is displayed.
* <p>.
*
********************************************************************/
public static String getLocalString(String key, Object arg1)
{
return MessageFormat.format(getLocalString(key), new Object[] {arg1});
}// getLocalString()
/********************************************************************
*
* Gets a resource-bundle String; this is for internationalization.
* Objects passed in are for MessageFormat arguments.
* <p>
* If resource is missing, a popup-error message is displayed.
* <p>.
*
********************************************************************/
public static String getLocalString(String key, Object arg1, Object arg2)
{
return MessageFormat.format(getLocalString(key), new Object[] {arg1, arg2});
}// getLocalString()
/********************************************************************
*
* Gets a resource-bundle String; this is for internationalization.
* Objects passed in are for MessageFormat arguments.
* <p>
* If resource is missing, a popup-error message is displayed.
* <p>.
*
********************************************************************/
public static String getLocalString(String key, Object arg1, Object arg2, Object arg3)
{
return MessageFormat.format(getLocalString(key), new Object[] {arg1, arg2, arg3});
}// getLocalString()
/********************************************************************
*
* Gets a resource-bundle String; this is for internationalization.
* Objects passed in are for MessageFormat arguments.
* <p>
* If resource is missing, a popup-error message is displayed.
* <p>.
*
********************************************************************/
public static String getLocalString(String key, Object[] args)
{
return MessageFormat.format(getLocalString(key), args);
}// getLocalString()
/********************************************************************
*
* Gets a resource-bundle String; this is for internationalization.
* <p>.
* If there is NO resource present, returns 'null'.
*
********************************************************************/
public static String getLocalStringNoEx(String key)
{
try
{
return resourceBundle.getString(key);
}
catch(MissingResourceException e)
{
}
return null;
}// getLocalStringNoEx()
/** What we display if we are missing a resource */
/*private static void showNoLocalStringPopup(String resourceKey, Exception e)
{
String title = getLocalStringNoEx(UTILS_RES_ERR_DLG_TITLE);
String text = getLocalStringNoEx(UTILS_RES_ERR_DLG_TEXT);
title = (title == null) ? "Resource Error" : title;
if(text == null)
{
text = "Could not find a needed resource \""+resourceKey+"\"; error:\n"+e.getMessage();
}
else
{
text = MessageFormat.format(text, new Object[] {resourceKey, e.getMessage()});
}
//popupError(null, title, text);
}// showNoLocalStringPopup()
*/
/********************************************************************
*
* Given a color string, converts it to a color value. This is typically
* used when parsing configuration files and preferences. This has been
* tightened to be SVG CSS (non keyword) compliant. Case insensitive.
* <p>
* Acceptable forms are:
* <ul>
* <li><code>#RGB</code> (3 hex values; values will be doubled (e.g.,
* #FAF becomes #FFAAFF)</li>
* <li><code>#RRGGBB</code> (6 hex digits)</li>
* <li><code>#RRGGBBAA</code> (8 hex digits; A is alpha value)</li>
* <li><code>rgb(r, g, b)</code> (where r, g, b are specified as
* integers (0-255) or percents (0-100%, 100% = 255).</li>
* </ul>
* <p>
* If parsing fails, defaultValue is returned.
*
********************************************************************/
public static Color parseColor(final String value, final Color defaultValue)
{
// Note that Color.decode() does NOT parse alpha values.
// original lengths: (with #): 4, 7, 9
//
String lcColor = value.trim().toLowerCase();
final int length = lcColor.length(); // original length
if( lcColor.startsWith("#") )
{
lcColor = lcColor.substring(1); // remove '#'
// expand 3-digit to 6 digit
if(length == 4)
{
// screw math!
StringBuffer sb = new StringBuffer(6);
sb.append(lcColor.charAt(0));
sb.append(lcColor.charAt(0));
sb.append(lcColor.charAt(1));
sb.append(lcColor.charAt(1));
sb.append(lcColor.charAt(2));
sb.append(lcColor.charAt(2));
lcColor = sb.toString();
}
if(length == 4 || length == 7 || length == 9)
{
try
{
// must parse as long, because RGBA bytes are
// 'unsigned' as a whole, and int is signed.
// if length == 9, we have an alpha value included.
//
final int colorBits = (int) Long.parseLong(lcColor, 16);
return new Color(colorBits, (length == 9));
}
catch(Exception e)
{
}
}
}
else if(lcColor.startsWith("rgb"))
{
lcColor = lcColor.substring(3); // remove "rgb"
StringTokenizer st = new StringTokenizer(lcColor, "(), ");
String[] sRGB = new String[3];
int idx = 0;
while(st.hasMoreTokens() && idx < 3)
{
sRGB[idx] = st.nextToken();
idx++;
}
if(idx == 3) // not enough values, if idx is less than 2
{
try
{
int rgb[] = new int[3];
for(int i=0; i<sRGB.length; i++)
{
if(sRGB[i].endsWith("%") && sRGB[i].length() > 1)
{
final int percent = Integer.parseInt(sRGB[i].substring(0,sRGB[i].length()-1));
rgb[i] = (int) (255.0f * ((float) percent / 100.0f));
}
else
{
rgb[i] = Integer.parseInt(sRGB[i]);
}
}
return new Color(rgb[0], rgb[1], rgb[2]);
}
catch(Exception e)
{
}
}
}
// error: return default color.
return defaultValue;
}// parseColor()
/********************************************************************
*
* Given a Color, convert it to a hex String.
* Includes alpha value. Null input is illegal.
* <p>
* The format of the output string is:
* "RRGGBBAA". No '#' is prepended. The String length
* is always 8 characters, zero-padded as necessary.
*
*
********************************************************************/
// public static String colorToHex(Color color)
// {
// return colorToHex(color, false);
// }// colorToHex()
/**
* Converts a Color to its Hexadecimal equivalent, and
* prepends a '#' sign if withPound is true.
*
*/
// public static String colorToHex(Color color, boolean withPound)
// {
// StringBuffer sb = new StringBuffer(9);
// if(withPound)
// {
// sb.append('#');
// }
// fastToHex(sb, color.getRed());
// fastToHex(sb, color.getBlue());
// fastToHex(sb, color.getGreen());
// fastToHex(sb, color.getAlpha());
// return sb.toString();
// }// colorToHex()
/**
* Converts a color to HTML hex color in the following format:
* <code>#RRGGBB</code>. Alpha values are not used.
*
*/
// public static String colorToHTMLHex(Color color)
// {
// StringBuffer sb = new StringBuffer(8);
// sb.append('#');
// fastToHex(sb, color.getRed());
// fastToHex(sb, color.getBlue());
// fastToHex(sb, color.getGreen());
// return sb.toString();
// }// colorToHTMLHex()
/**
* Creates a JEditorPane that has flowing, rich (HTML) text, but
* is not selectable or editable. This will also set the document
* base to be the same as Utils.getResourceBase().
* <p>
* This is non-focusable by default.
*
* @param blend match background of component if true.
*/
//public static JEditorPane createTextLabel(boolean blend)
//{
// return createTextLabel(null, blend, false);
//}// createTextLabel()
/**
* Creates a JEditorPane that has flowing, rich (HTML) text, but
* is not selectable or editable. This will also set the document
* base to be the same as Utils.getResourceBase().
* <p>
* This is non-focusable by default.
*
*
* @param text the initial text.
* @param blend match background of component if true.
*
*
*/
//public static JEditorPane createTextLabel(String text, boolean blend)
//{
// return createTextLabel(text, blend, false);
//}// createTextLabel()
/**
* Creates a JEditorPane that has flowing, rich (HTML) text, but
* is not selectable or editable. This will also set the document
* base to be the same as Utils.getResourceBase().
*
* @param text the initial text.
* @param blend match background of component if true.
* @param isFocusable true if this component can receive focus events
*/
/*public static JEditorPane createTextLabel(String text, boolean blend, final boolean isFocusable)
{
// use antialiasing only if non-blended
JEditorPane jep = null;
if(blend)
{
jep = new JEditorPane()
{
final boolean mayFocus = isFocusable;
public boolean isFocusable()
{
return mayFocus;
}
};
}
else
{
jep = new XJEditorPane()
{
final boolean mayFocus = isFocusable;
public boolean isFocusable()
{
return mayFocus;
}
};
}
// set the content type to HTML, and the the document base
jep.setContentType("text/html");
Document doc = jep.getDocument();
if(doc instanceof HTMLDocument)
{
((HTMLDocument)doc).setBase(Utils.getResourceBase());
}
if(!blend)
{
jep.setMargin(new Insets(TEXT_INSETS, TEXT_INSETS, TEXT_INSETS, TEXT_INSETS));
}
jep.setEditable(false);
jep.setHighlighter(null);
jep.setSelectedTextColor(null); // per BugID 4532590
jep.setForeground(UIManager.getColor("textText"));
if(blend)
{
jep.setBackground(UIManager.getColor("Label.text"));
}
if(text != null)
{
jep.setText(text);
}
return jep;
}// createTextLabel*/
/** Scale an Image to the given width and height (in pixels) */
// public static Image getScaledImage(Image src, int w, int h)
// {
// AreaAveragingScaleFilter scaleFilter = new AreaAveragingScaleFilter(w, h);
// return syncCreateImage(toolkit.createImage(new FilteredImageSource(src.getSource(), scaleFilter)));
// }
/** Scale an ImageIcon to the given width and height (in pixels) */
// public static ImageIcon rescaleImageIcon(ImageIcon src, int w, int h)
// {
// if(src.getIconWidth() != w || src.getIconHeight() != h)
// {
// AreaAveragingScaleFilter scaleFilter = new AreaAveragingScaleFilter(w, h);
// Image img = syncCreateImage(toolkit.createImage(new FilteredImageSource(src.getImage().getSource(), scaleFilter)));
// return new ImageIcon(img);
// }
//
// return src;
// }// rescaleImageIcon()
/**
* Scale an ImageIcon *down* to the given width and height (in pixels),
* keeping aspect ratio intact. No scaling is done if image is smaller
* then the desired max (specified) width/height
*/
// public static ImageIcon scaleDown(final ImageIcon src, final int maxW, final int maxH)
// {
// if(src.getIconWidth() >= maxW || src.getIconHeight() >= maxH)
// {
// int w = maxW;
// int h = maxH;
// final float aspect = (float) src.getIconWidth() / (float) src.getIconHeight();
//
// if(src.getIconWidth() >= src.getIconHeight())
// {
// w = maxW;
// h = (int) (w / aspect);
// }
// else
// {
// h = maxH;
// w = (int) (h * aspect);
// }
//
// AreaAveragingScaleFilter scaleFilter = new AreaAveragingScaleFilter(w, h);
// Image img = syncCreateImage(toolkit.createImage(new FilteredImageSource(src.getImage().getSource(), scaleFilter)));
// return new ImageIcon(img);
// }
//
// return src;
// }// scaleDown()
/** Return a scaled image that is proportionally scaled via the given factor (e.g., 0.5f = 1/2 size).*/
// public static Image getScaledImage(Image src, float factor)
// {
// int w = (int) (src.getWidth(null) * factor);
// int h = (int) (src.getHeight(null) * factor);
// return getScaledImage(src, w, h);
// }
/**
* A less flexible and faster but easier-to-use message formatting.
* A number enclosed within braces is replaced with the argument
* at that index. The toString() method is used to convert the
* Object argument into text.
*/
public static String format(String format, Object args[])
{
StringBuffer output = new StringBuffer(4096);
StringBuffer accum = new StringBuffer(64);
boolean inBrace = false;
StringTokenizer st = new StringTokenizer(format, "{}", true);
while(st.hasMoreTokens())
{
String tok = st.nextToken();
if("{".equals(tok) && !inBrace)
{
inBrace = true;
}
else if("}".equals(tok) && inBrace)
{
inBrace = false;
int i = -1;
try
{
i = Integer.parseInt(accum.toString());
}
catch(Exception e)
{
}
if(i >= 0 && i < args.length)
{
output.append(args[i]);
}
accum.setLength(0);
}
else
{
if(inBrace)
{
accum.append(tok);
}
else
{
output.append(tok);
}
}
}
return output.toString();
}// format()
/**
* Converts int (0-255) to hex value, appends a '0' before if only a single digit
* could have lookup table and index via shifting...
* this is ONLY for values of i between [0,255]
*/
private static void fastToHex(StringBuffer sb, int i)
{
sb.append(HEX[i >> 4]);
sb.append(HEX[i & 0x0000000F]);
}// fastToHex()
/**
* Gets the appropriate resource bundle. Uses the default locale.
* If the default locale cannot be found, ResourceBundle.getBundle()
* should find an acceptable substitute. If it cannot, the US English
* bundle is used.
* <p>
* If the default (US English) bundle cannot be found, a fatal error
* message is displayed, and the program will exit.
* <p>
* This requires the classLoader variable to be set.
*
*/
private static void setResourceBundle(Locale locale)
{
try
{
resourceBundle = ResourceBundle.getBundle(BASE_RESOURCE_FILE, locale, classLoader);
}
catch(MissingResourceException mre)
{
System.err.println(mre);
//popupError(null, "ERROR: Cannot Start", "Resource File cannot be found!\n"+mre.getMessage());
System.exit(1);
}
}// setResourceBundle()
/**
* Constructs a formatted text field that only allows valid email characters.
* These are defined as ASCII alphanumerics (a-z, A-Z, 0-9), plus the characters
* <b>!,.,-,_,@</b>. This is not (by any means) RFC822 compliant,
* but allows most email addresses through.
*/
/*public static JTextField createEmailTextField(int cols)
{
JTextField jtf = new JTextField(cols);
AbstractDocument doc = (AbstractDocument) jtf.getDocument();
doc.setDocumentFilter(new DocumentFilter()
{
public void insertString(DocumentFilter.FilterBypass fb, int offset, String text, AttributeSet attr)
throws BadLocationException
{
this.replace(fb, offset, 0, text, attr);
}// insertString()
public void replace(DocumentFilter.FilterBypass fb, int offset, int length, String text, AttributeSet attr)
throws BadLocationException
{
fb.replace(offset, length, getValidEmailString(text), attr);
}// replace()
private String getValidEmailString(String in)
{
StringBuffer buffer = new StringBuffer(in);
for(int i=buffer.length()-1; i>=0; i--)
{
char c = buffer.charAt(i);
if( !isValidEmail(c) )
{
buffer.deleteCharAt(i);
}
}
return buffer.toString();
}// getValidEmailString()
private boolean isValidEmail(char c)
{
// check letters (A-Z, a-z)
if( (c >= 0x0041 && c<= 0x005A)
|| (c >= 0x0061 && c<= 0x007A) )
{
return true;
}
// check digits (0-9)
if(c >= 0x0030 && c <= 0x0039)
{
return true;
}
// check misc chars
for(int i=0; i<EMAIL_ALLOWED.length; i++)
{
if(c == EMAIL_ALLOWED[i])
{
return true;
}
}
return false;
}// checkValid()
});
return jtf;
}// createEmailTextField()*/
/**
* Constructs a formatted text field that only allows valid URI characters.
* These are defined as ASCII alphanumerics (a-z, A-Z, 0-9), plus many
* additional characters, including: ;,/,?,:,@,&,=,+,$,,,-,_,.,!,~,*,',|,%
*
*
*/
/*public static JTextField createURITextField(int cols)
{
JTextField jtf = new JTextField(cols);
AbstractDocument doc = (AbstractDocument) jtf.getDocument();
doc.setDocumentFilter(new DocumentFilter()
{
public void insertString(DocumentFilter.FilterBypass fb, int offset, String text, AttributeSet attr)
throws BadLocationException
{
replace(fb, offset, 0, text, attr);
}// insertString()
public void replace(DocumentFilter.FilterBypass fb, int offset, int length, String text, AttributeSet attr)
throws BadLocationException
{
fb.replace(offset, length, getValidURLString(text), attr);
}// replace()
private String getValidURLString(String in)
{
StringBuffer buffer = new StringBuffer(in);
for(int i=buffer.length()-1; i>=0; i--)
{
final char c = buffer.charAt(i);
if( !isValidURL(c) )
{
buffer.deleteCharAt(i);
}
}
return buffer.toString();
}// getValidURLString()
private boolean isValidURL(char c)
{
// check letters (A-Z, a-z)
if( (c >= 0x0041 && c<= 0x005A)
|| (c >= 0x0061 && c<= 0x007A) )
{
return true;
}
// check digits (0-9)
if(c >= 0x0030 && c <= 0x0039)
{
return true;
}
// check misc chars
for(int i=0; i<URL_ALLOWED.length; i++)
{
if(c == URL_ALLOWED[i])
{
return true;
}
}
return false;
}// checkValid()
});
return jtf;
}// createURITextField()*/
/**
* Parses a line of CSV text into a String array.
* <p>
* This quoted text, unquoted text, and escaped quotes.
* Excel-style quote support could be added, but has not been tested
* adequately. Unescaped quotes are also acceptable in this parser.
* Commas with only whitespace between them return empty ("") Strings.
* Whitespace is removed around unquoted items.
* <p>
* For example:<br>
* <code>"hello", goodbye , """test",, "a line, a \"quote\""</code><br>
* Will parse into:<br>
* <code>hello|goodbye|""test||a line, a "quote"|</code><br>
* <p>
* Should never return null. NOTE that a entry of "" will return "" (array length 1).
* An array length of 0 is never returned.
*/
public static String[] parseCSV(String input)
{
Matcher m = REAL_COMMAS.matcher( input );
ArrayList matchList = new ArrayList();
// find all matches (except last)
// we trim extra whitespace from ends (no effect if within quotes)
int start = 0;
while(m.find())
{
matchList.add( input.substring(start, m.start()).trim() );
start = m.end();
}
// find last match
matchList.add( input.substring(start, input.length()).trim() );
// convert to array
// final: because array length doesn't change. Contents might, though.
final String[] matches = (String[]) matchList.toArray(new String[matchList.size()]);
// cleanup
for(int i=0; i<matches.length; i++)
{
if(matches[i].length() > 0)
{
StringBuffer sb = new StringBuffer(matches[i]);
// step 1: remove (if present) start/end quotes
if(sb.charAt(0) == '\"')
{
sb.deleteCharAt(0);
}
if(sb.charAt(sb.length()-1) == '\"')
{
sb.deleteCharAt(sb.length()-1);
}
// step 2: replace double quotes (excel-style) with single quote
// disabled. if this is enabled, step 3 should probably be disabled.
/*
int idx = 0;
while( (idx = sb.indexOf("\"\"", idx)) != -1 )
{
idx += 1;
sb.deleteCharAt(idx);
}
*/
// step 3: replace 'quoted quotes' (e.g.: \") with single quote
int idx = 0;
while( (idx = sb.indexOf("\\\"", idx)) != -1 )
{
sb.deleteCharAt(idx);
idx += 1;
}
// replace our string
matches[i] = sb.toString();
}
}
return matches;
}// parseCSV()
/**
* Similar to parseCSV(), but if the input string (after
* trimming) is null or empty, returns an array of length 0.
* XE for "eXtended Edition".
*/
public static String[] parseCSVXE(String input)
{
if(input == null || "".equals(input.trim()))
{
return new String[0];
}
return parseCSV(input);
}// parseCSVXE
/***
* Gets a common-bundle (non-localized) String.
* If the key is not found, an exception is thrown.
*/
public static String getCommonString(String key)
{
try
{
return commonBundle.getString(key);
}
catch(Exception e)
{
throw new IllegalStateException("Error/Missing Common Bundle Property: "+key);
}
}// getLocalString()
/***
* Gets a common-bundle (non-localized) String array.
* The string array is parsed with the parseCSV() method.
* If the key is not found, an exception is thrown.
*/
public static String[] getCommonStringArray(String key)
{
try
{
return parseCSV( commonBundle.getString(key) );
}
catch(Exception e)
{
throw new IllegalStateException("Error/Missing Common Bundle Property: "+key);
}
}// getLocalString()
/**
* Replaces all instances of "tofind" with "toReplace"
* in the given input String, and returns the String
* after replacement has occured. If no modification
* to input has occured, the same reference will be
* returned.
*/
public static String replaceAll(final String input, final String toFind, final String toReplace)
{
if(toFind == null || toReplace == null)
{
throw new IllegalArgumentException();
}
if(input == null)
{
return null;
}
final int toFindLen = toFind.length();
final int toReplaceLen = toReplace.length();
StringBuffer sb = new StringBuffer(input);
int idx = 0;
int start = sb.indexOf(toFind, idx);
boolean isModified = (start != -1);
while(start != -1)
{
int end = start + toFindLen;
sb.replace(start, end, toReplace);
// repeat search
idx = start + toReplaceLen;
start = sb.indexOf(toFind, idx);
}
return (isModified) ? sb.toString() : input;
}// replaceAll()
/**
* Replaces all instances of "tofind" with "toReplace"
* in the given input String, and returns the String
* after replacement has occured. If no modification
* to input has occured, the same reference will be
* returned.
* <p>
* toFind and toReplace must have the same lengths.
* <p>
* The order of replacement is the order of the toFind array.
*/
public static String replaceAll(final String input, final String[] toFind, final String[] toReplace)
{
if(toFind == null || toReplace == null || toFind.length != toReplace.length)
{
throw new IllegalArgumentException();
}
if(input == null)
{
return null;
}
StringBuffer sb = new StringBuffer(input);
boolean isModified = false;
for(int i=0; i<toFind.length; i++)
{
final int toFindLen = toFind[i].length();
final int toReplaceLen = toReplace[i].length();
int idx = 0;
int start = sb.indexOf(toFind[i], idx);
isModified = (isModified) || (start != -1);
while(start != -1)
{
int end = start + toFindLen;
sb.replace(start, end, toReplace[i]);
// repeat search
idx = start + toReplaceLen;
start = sb.indexOf(toFind[i], idx);
}
}
return (isModified) ? sb.toString() : input;
}// replaceAll()
/** Detect if we are running on Mac OS X */
public static boolean isOSX()
{
return isOSX;
}// isOSX()
/** Detect if we are running on Windows */
public static boolean isWindows()
{
return isWindows;
}// isWindows()
/** Constructor */
private Utils()
{
}// Utils()
}// class Utils