/**
* Copyright (c) 2003, Spellcast development team
* http://spellcast.dev.java.net/
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* [1] Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* [2] Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* [3] Neither the name "Spellcast development team" nor the names of
* its contributors may be used to endorse or promote products
* derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package net.java.dev.spellcast.utilities;
import java.awt.Color;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Arrays;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
/**
* Formed after the same idea as <code>SwingUtilities</code>, this contains common functions needed by many of the
* data-related classes. Any methods which are used by multiple instances of a JComponent and have a non-class-specific
* purpose should be placed into this class in order to simplify the overall design of the system and to facilitate
* documentation.
*/
public class DataUtilities
{
private static String jarPath = null;
private static JarFile jarFile = null;
static
{
URL url = UtilityConstants.SYSTEM_CLASSLOADER.getResource( "data" );
if ( url == null )
{
url = UtilityConstants.MAINCLASS_CLASSLOADER.getResource( "data" );
}
if ( url != null )
{
try
{
DataUtilities.jarPath = url.getPath();
String prefix = DataUtilities.jarPath.substring( 5, DataUtilities.jarPath.indexOf( "!" ) );
DataUtilities.jarFile = new JarFile( new File( prefix ) );
}
catch ( Exception e )
{
}
}
}
private static String lastMessage = null;
public static String getLastMessage()
{
String message = DataUtilities.lastMessage;
DataUtilities.lastMessage = null;
return message;
}
private static final String[] EMPTY_STRING_ARRAY = new String[ 0 ];
private static final File[] EMPTY_FILE_ARRAY = new File[ 0 ];
private static final FilenameFilter BACKUP_FILTER = new FilenameFilter()
{
public boolean accept( final File dir, final String name )
{
return !name.startsWith( "." ) && !name.endsWith( "~" ) && !name.endsWith( ".bak" ) && !name.endsWith( ".map" ) && name.indexOf( "datamaps" ) == -1 && dir.getPath().indexOf(
"datamaps" ) == -1;
}
};
public static String[] list( final File directory )
{
if ( !directory.exists() || !directory.isDirectory() )
{
return DataUtilities.EMPTY_STRING_ARRAY;
}
String[] result = directory.list( DataUtilities.BACKUP_FILTER );
Arrays.sort( result );
return result;
}
public static File[] listFiles( final File directory )
{
if ( !directory.exists() || !directory.isDirectory() )
{
return DataUtilities.EMPTY_FILE_ARRAY;
}
File[] result = directory.listFiles( DataUtilities.BACKUP_FILTER );
Arrays.sort( result );
return result;
}
/**
* A public function used to retrieve the reader for a file. Allows the referencing of files contained within a JAR,
* inside of a class tree, and from the local directory from which the Java command line is called. The priority is
* as listed, in reverse order. Note that rather than throwing an exception should the file not be successfully
* found, this function will instead print out an error message and simply return null.
*
* @param file the file to be retrieved
*/
public static BufferedReader getReader( final File file )
{
return DataUtilities.getReader( DataUtilities.getInputStream( file ) );
}
/**
* A public function used to retrieve the reader for a file. Allows the referencing of files contained within a JAR,
* inside of a class tree, and from the local directory from which the Java command line is called. The priority is
* as listed, in reverse order. Note that rather than throwing an exception should the file not be successfully
* found, this function will instead print out an error message and simply return null.
*
* @param directory the subdirectory of the file
* @param filename the name of the file to be retrieved
*/
public static BufferedReader getReader( final String directory, final String filename )
{
return DataUtilities.getReader( directory, filename, true );
}
public static BufferedReader getReader( final String directory, final String filename, final boolean allowOverride )
{
try
{
if ( filename.startsWith( "http://" ) )
{
HttpURLConnection connection = (HttpURLConnection) new URL( null, filename ).openConnection();
connection.setRequestProperty( "Connection", "close" ); // no need to keep-alive
InputStream istream = connection.getInputStream();
if ( connection.getResponseCode() != 200 )
{
return DataUtilities.getReader( DataUtilities.EMPTY_STREAM );
}
String encoding = connection.getContentEncoding();
if ( encoding == null )
{
encoding = "ISO-8859-1";
}
return DataUtilities.getReader( istream, encoding );
}
}
catch ( IOException e )
{
return DataUtilities.getReader( DataUtilities.EMPTY_STREAM );
}
return DataUtilities.getReader( DataUtilities.getInputStream( directory, filename, allowOverride ) );
}
public static BufferedReader getReader( final InputStream istream )
{
return DataUtilities.getReader( istream, "UTF-8" );
}
public static BufferedReader getReader( final InputStream istream, final String encoding )
{
if ( istream == null )
{
return DataUtilities.getReader( DataUtilities.EMPTY_STREAM );
}
InputStreamReader reader = null;
try
{
if ( encoding != null )
{
reader = new InputStreamReader( istream, encoding );
}
else
{
reader = new InputStreamReader( istream, "UTF-8" );
}
}
catch ( Exception e )
{
reader = new InputStreamReader( istream );
}
return new BufferedReader( reader );
}
public static InputStream getInputStream( final File file )
{
File parent = file.getParentFile();
if ( parent != null && !parent.exists() )
{
parent.mkdirs();
}
try
{
if ( !file.exists() )
{
file.createNewFile();
}
return new FileInputStream( file );
}
catch ( Exception e )
{
e.printStackTrace();
return DataUtilities.EMPTY_STREAM;
}
}
/**
* A public function used to retrieve the input stream, given a filename. Allows referencing images within a JAR,
* inside of a class tree, and from the local directory from which the Java command line is called. The priority is
* as listed, in reverse order.
*
* @param directory the subtree in which the file can be found
* @param filename the name of the file to be retrieved
*/
public static InputStream getInputStream( final String directory, final String filename )
{
return DataUtilities.getInputStream( directory, filename, true );
}
public static InputStream getInputStream( String directory, String filename, final boolean allowOverride )
{
// Reformat the name of the directory and the filename to use
// strictly forward slashes.
directory = directory.replaceAll( File.separator.replaceAll( "\\\\", "\\\\\\\\" ), "/" );
filename = filename.replaceAll( File.separator.replaceAll( "\\\\", "\\\\\\\\" ), "/" );
if ( directory.length() > 0 && !directory.endsWith( "/" ) )
{
directory += "/";
}
String fullname = directory + filename;
DataUtilities.lastMessage = null;
InputStream locationAsInputStream = DataUtilities.getOverrideStream( fullname, allowOverride );
if ( locationAsInputStream != null )
{
DataUtilities.lastMessage = "Using data override: " + fullname;
return locationAsInputStream;
}
locationAsInputStream = DataUtilities.getInputStream( UtilityConstants.SYSTEM_CLASSLOADER, fullname );
if ( locationAsInputStream != null )
{
return locationAsInputStream;
}
locationAsInputStream = DataUtilities.getInputStream( UtilityConstants.MAINCLASS_CLASSLOADER, fullname );
if ( locationAsInputStream != null )
{
return locationAsInputStream;
}
// if it's gotten this far, the file does not exist
return DataUtilities.EMPTY_STREAM;
}
public static InputStream getOverrideStream( String fullname, final boolean allowOverride )
{
// Don't look for an override file unless allowed to
if ( !allowOverride )
{
return null;
}
// See if override file exists
File override = new File( UtilityConstants.ROOT_LOCATION, fullname );
if ( !override.exists() )
{
return null;
}
// See if override file is newer than that shipped with the .jar file
if ( DataUtilities.jarFile == null )
{
DataUtilities.lastMessage = "Unable to locate jar file (" + DataUtilities.jarPath + ") for internal data";
}
else
{
ZipEntry internal = DataUtilities.jarFile.getEntry( fullname );
if ( internal != null )
{
// This file exists internally. Check creation dates.
long idate = internal.getTime();
long odate = override.lastModified();
// If internal date is newer, skip override file
if ( idate > odate )
{
DataUtilities.lastMessage = "Skipping stale data override: " + fullname;
return null;
}
}
}
try
{
return new FileInputStream( override );
}
catch ( Exception e )
{
e.printStackTrace();
}
return null;
}
private static InputStream getInputStream( final ClassLoader loader, final String filename )
{
return loader.getResourceAsStream( filename );
}
public static OutputStream getOutputStream( final String filename )
{
return DataUtilities.getOutputStream( new File( filename ) );
}
public static OutputStream getOutputStream( final File file )
{
return DataUtilities.getOutputStream( file, false );
}
public static OutputStream getOutputStream( final File file, final boolean shouldAppend )
{
File directory = file.getParentFile();
if ( directory != null && !directory.exists() )
{
directory.mkdirs();
}
try
{
return new FileOutputStream( file, shouldAppend );
}
catch ( Exception e )
{
e.printStackTrace();
return DataUtilities.MEMORY_OUTPUT_STREAM;
}
}
/**
* In a lot of HTML documents and still others, colors are represented using the RGB values, concatenated as
* hexadecimal strings. This function is used to create that hexadecimal string from a color object. Note that the
* traditional pound symbol will also be part of the string.
*
* @param c The color to be translated to a hexadecimal string.
* @return The hexadecimal string representation of the color
*/
public static String toHexString( final Color c )
{
if ( c == null )
{
return "#000000";
}
StringBuffer hexString = new StringBuffer( 7 );
hexString.append( '#' );
int bitmask = ( 1 << 24 ) - 1;
hexString.append( DataUtilities.toHexString( c.getRGB() & bitmask, 6 ) );
return hexString.toString();
}
/**
* In a lot of HTML documents and still others, colors are represented using the RGB values, concatenated as
* hexadecimal strings. This function is used to create a color object from such a hexadecimal string.
*
* @param hexString The hexadecimal string (with # prefix) to be translated to a color
* @return The color represented by this hexadecimal string
*/
public static Color toColor( final String hexString )
{
return new Color( Character.digit( hexString.charAt( 1 ), 16 ) * 16 + Character.digit(
hexString.charAt( 2 ), 16 ), Character.digit( hexString.charAt( 3 ), 16 ) * 16 + Character.digit(
hexString.charAt( 4 ), 16 ), Character.digit( hexString.charAt( 5 ), 16 ) * 16 + Character.digit(
hexString.charAt( 6 ), 16 ) );
}
/**
* The static <code>getHexString()</code> method found in the <code>Long</code> class has the advantage of
* converting a value to a hexadecimal string. However, it does not fix this hexadecimal string to any length. Thus,
* the purpose of this function is to convert a given long to a zero-filled hexadecimal string of given length.
* Note, however, that negative values and values which cannot be represented with the given number of hexadecimal
* digits will cause an exception to be thrown.
*
* @param value The value to convert to hexadecimal
* @param digitCount The number of digits to use
* @return The hexadecimal string representation of the given value, set to the given length
*/
public static String toHexString( final long value, final int digitCount )
{
if ( value < 0 )
{
throw new IllegalArgumentException( "This function cannot convert negative values" );
}
String hexString = Long.toHexString( value );
if ( hexString.length() > digitCount )
{
throw new IllegalArgumentException(
value + " cannot be represented in " + digitCount + " hexadecimal digits" );
}
StringBuffer hexBuffer = new StringBuffer( digitCount );
int zeroesToAdd = digitCount - hexString.length();
for ( int i = 0; i < zeroesToAdd; ++i )
{
hexBuffer.append( 0 );
}
hexBuffer.append( hexString );
return hexBuffer.toString();
}
/**
* A method to convert a given text string to its HTML equivalent. This method is used primarily by the
* <code>ChatBuffer</code> component to translate the plain text values returned by the
* <code>getChatDisplayForm()</code> to values that can be displayed in a <code>JEditorPane</code> set to display
* HTML.
*
* @param plainText The plain text to be converted to HTML
* @return The HTML representation of the plain text.
*/
public static String convertToHTML( final String plainText )
{
if ( plainText == null || plainText.length() == 0 )
{
return "";
}
String html = plainText;
html = html.replaceAll( "&", "&" ); // first replace all ampersands
html = html.replaceAll( "<", "<" ); // then replace all < symbols
// finally, replace all the new lines with the <br> tag
html =
html.replaceAll( UtilityConstants.LINE_BREAK, "<br>" + UtilityConstants.LINE_BREAK );
// the conversion to HTML is complete
return html;
}
private static ByteArrayInputStream EMPTY_STREAM = new ByteArrayInputStream( new byte[ 0 ] );
private static ByteArrayOutputStream MEMORY_OUTPUT_STREAM = new ByteArrayOutputStream();
}