// Near Infinity - An Infinity Engine Browser and Editor
// Copyright (C) 2001 - 2005 Jon Olav Hauglid
// See LICENSE.txt for license information
package org.infinity.util;
import java.awt.Dimension;
import java.nio.charset.Charset;
import java.util.Comparator;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import javax.swing.JComponent;
/**
* A general-purpose class containing useful function not fitting elsewhere.
*/
public class Misc
{
/** The default ANSI charset. */
public static final Charset CHARSET_DEFAULT = Charset.forName("windows-1252");
/** The UTF-8 charset. */
public static final Charset CHARSET_UTF8 = Charset.forName("UTF-8");
/** The US-ASCII charset. */
public static final Charset CHARSET_ASCII = Charset.forName("US-ASCII");
/** Returns the line separator string which is used by the current operating system. */
public static final String LINE_SEPARATOR = System.getProperty("line.separator");
/**
* Returns a comparator that compares the string representation of the specified objects
* in a case-insensitive way. */
public static <T> Comparator<T> getIgnoreCaseComparator()
{
return new Comparator<T>() {
@Override
public int compare(T o1, T o2)
{
return (o1.toString().compareToIgnoreCase(o2.toString()));
}
@Override
public boolean equals(Object obj)
{
return toString().equalsIgnoreCase(obj.toString());
}
};
}
/**
* Attempts to detect the character set of the text data in the specified byte buffer.
* @param data Text data as byte array.
* @return The detected character set or the ANSI charset "windows-1252"
* if autodetection was not successful.
*/
public static Charset detectCharset(byte[] data)
{
return detectCharset(data, CHARSET_DEFAULT);
}
/**
* Attempts to detect the character set of the text data in the specified byte buffer.
* @param data Text data as byte array.
* @param defaultCharset The default charset to return if autodetection is not successful.
* (Default: windows-1252)
* @return The detected character set or {@code defaultCharset}
* if autodetection was not successful.
*/
public static Charset detectCharset(byte[] data, Charset defaultCharset)
{
if (defaultCharset == null) {
defaultCharset = CHARSET_DEFAULT;
}
Charset retVal = null;
if (data != null) {
if (data.length >= 3 &&
data[0] == -17 && data[1] == -69 && data[2] == -65) { // UTF-8 BOM (0xef, 0xbb, 0xbf)
retVal = Charset.forName("utf-8");
} else if (data.length >= 2 &&
data[0] == -2 && data[1] == -1) { // UTF-16 BOM (0xfeff) in big-endian order
retVal = Charset.forName("utf-16be");
} else if (data.length >= 2 &&
data[0] == -1 && data[1] == -2) { // UTF-16 BOM (0xfeff) in little-endian order
retVal = Charset.forName("utf-16le");
}
}
if (retVal == null) {
retVal = defaultCharset;
}
return retVal;
}
/**
* Attempts to convert the specified string into a numeric value. Returns defValue of value does
* not contain a valid number.
*/
public static int toNumber(String value, int defValue)
{
if (value != null) {
try {
return Integer.parseInt(value);
} catch (NumberFormatException e) {
}
}
return defValue;
}
/**
* Attempts to convert the specified string of given base "radix" into a numeric value.
* Returns defValue of value does not contain a valid number.
*/
public static int toNumber(String value, int radix, int defValue)
{
if (value != null) {
try {
return Integer.parseInt(value, radix);
} catch (NumberFormatException e) {
}
}
return defValue;
}
/** Swaps byte order of the specified short value. */
public static final short swap16(short v)
{
return (short)(((v & 0xff) << 8) | ((v >> 8) & 0xff));
}
/** Swaps byte order of the specified int value. */
public static final int swap32(int v)
{
return ((v << 24) & 0xff000000) |
((v << 8) & 0x00ff0000) |
((v >> 8) & 0x0000ff00) |
((v >> 24) & 0x000000ff);
}
/** Swaps byte order of the specified long value. */
public static final long swap64(long v)
{
return ((v << 56) & 0xff00000000000000L) |
((v << 40) & 0x00ff000000000000L) |
((v << 24) & 0x0000ff0000000000L) |
((v << 8) & 0x000000ff00000000L) |
((v >> 8) & 0x00000000ff000000L) |
((v >> 24) & 0x0000000000ff0000L) |
((v >> 40) & 0x000000000000ff00L) |
((v >> 56) & 0x00000000000000ffL);
}
/** Swaps byte order of every short value in the specified array. */
public static final void swap(short[] array)
{
if (array != null) {
for (int i = 0, cnt = array.length; i < cnt; i++) {
array[i] = swap16(array[i]);
}
}
}
/** Swaps byte order of every int value in the specified array. */
public static final void swap(int[] array)
{
if (array != null) {
for (int i = 0, cnt = array.length; i < cnt; i++) {
array[i] = swap32(array[i]);
}
}
}
/** Swaps byte order of every long value in the specified array. */
public static final void swap(long[] array)
{
if (array != null) {
for (int i = 0, cnt = array.length; i < cnt; i++) {
array[i] = swap64(array[i]);
}
}
}
/** Converts a short value into a byte array (little-endian). */
public static final byte[] shortToArray(short value)
{
return new byte[]{(byte)(value & 0xff),
(byte)((value >> 8) & 0xff)};
}
/** Converts an int value into a byte array (little-endian). */
public static final byte[] intToArray(int value)
{
return new byte[]{(byte)(value & 0xff),
(byte)((value >> 8) & 0xff),
(byte)((value >> 16) & 0xff),
(byte)((value >> 24) & 0xff)};
}
/** Converts a long value into a byte array (little-endian). */
public static final byte[] longToArray(long value)
{
return new byte[]{(byte)(value & 0xffL),
(byte)((value >> 8) & 0xffL),
(byte)((value >> 16) & 0xffL),
(byte)((value >> 24) & 0xffL),
(byte)((value >> 32) & 0xffL),
(byte)((value >> 40) & 0xffL),
(byte)((value >> 48) & 0xffL),
(byte)((value >> 56) & 0xffL)};
}
/**
* Sign-extends the specified {@code int} value consisting of the specified number of significant bits.
* @param value The {@code int} value to sign-extend.
* @param bits Size of {@code value} in bits.
* @return A sign-extended version of {@code value}.
*/
public static final int signExtend(int value, int bits)
{
return (value << (32 - bits)) >> (32 - bits);
}
/**
* Sign-extends the specified {@code long} value consisting of the specified number of significant bits.
* @param value The {@code long} value to sign-extend.
* @param bits Size of {@code value} in bits.
* @return A sign-extended version of {@code value}.
*/
public static final long signExtend(long value, int bits)
{
return (value << (64 - bits)) >> (64 - bits);
}
/**
* Creates a thread pool with a pool size depending on the number of available CPU cores.<br>
* <br>
* <b>numThreads:</b> Number of available CPU cores.<br>
* <b>maxQueueSize:</b> 2 x {@code numThreads}.<br>
* @return A ThreadPoolExecutor instance.
*/
public static ThreadPoolExecutor createThreadPool()
{
int numThreads = Runtime.getRuntime().availableProcessors();
return createThreadPool(numThreads, numThreads*2);
}
/**
* Creates a thread pool with the specified parameters.
* @param numThreads Max. number of parallel threads to execute. Must be >= 1.
* @param maxQueueSize Max. size of the working queue. Must be >= {@code numThreads}.
* @return A ThreadPoolExecutor instance.
*/
public static ThreadPoolExecutor createThreadPool(int numThreads, int maxQueueSize)
{
numThreads = Math.max(1, numThreads);
maxQueueSize = Math.max(numThreads, maxQueueSize);
return new ThreadPoolExecutor(numThreads, numThreads, 0L, TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<Runnable>(maxQueueSize));
}
/**
* Helper routine which can be used to check or block execution of new threads while the
* blocking queue is full.
* @param executor The executor to query.
* @param block Specify {@code true} to block execution as long as the queue is full.
* @param maxWaitMs Specify max. time to block queue, in milliseconds. Specify -1 to block indefinitely.
* @return {@code true} if queue is ready for new elements, {@code false} otherwise.
*/
public static boolean isQueueReady(ThreadPoolExecutor executor, boolean block, int maxWaitMs)
{
if (executor != null) {
if (block) {
if (maxWaitMs < 0) { maxWaitMs = Integer.MAX_VALUE; }
int curWaitMs = 0;
while (curWaitMs < maxWaitMs && executor.getQueue().size() > executor.getCorePoolSize()) {
try { Thread.sleep(1); } catch (InterruptedException e) {}
curWaitMs++;
}
}
return executor.getQueue().size() <= executor.getCorePoolSize();
}
return false;
}
/**
* Returns a prototype dimension object based on the height of {@code c} and the width of (@code prototype}.
* @param c The component to derive height and properties for calculating width.
* @param prototype The prototype string used to derive width.
* @return The {@link Dimension} object with calculated width and height.
*/
public static Dimension getPrototypeSize(JComponent c, String prototype)
{
Dimension d = null;
if (c != null) {
d = new Dimension();
d.height = c.getPreferredSize().height;
d.width = c.getFontMetrics(c.getFont()).stringWidth(prototype);
}
return d;
}
// Contains static functions only
private Misc() {}
}