/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * 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.
*
* * Neither the name of the copyright holder 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 HOLDER 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 com.twelvemonkeys.lang;
import java.io.*;
import java.lang.reflect.
Array;
import java.lang.reflect.
Field;
import java.lang.reflect.
InvocationTargetException;
import java.lang.reflect.
Method;
import java.security.
AccessController;
import java.security.
PrivilegedAction;
import java.util.
HashMap;
import java.util.
Map;
import java.util.
Properties;
/**
* A utility class with some useful system-related functions.
* <p/>
* <em>NOTE: This class is not considered part of the public API and may be
* changed without notice</em>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
*
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/lang/SystemUtil.java#3 $
*
*/
public final class
SystemUtil {
/** {@code ".xml"} */
public static
String XML_PROPERTIES = ".xml";
/** {@code ".properties"} */
public static
String STD_PROPERTIES = ".properties";
// Disallow creating objects of this type
private
SystemUtil() {
}
/** This class marks an inputstream as containing XML, does nothing */
private static class
XMLPropertiesInputStream extends
FilterInputStream {
public
XMLPropertiesInputStream(
InputStream pIS) {
super(
pIS);
}
}
/**
* Gets the named resource as a stream from the given Class' Classoader.
* If the pGuessSuffix parameter is true, the method will try to append
* typical properties file suffixes, such as ".properties" or ".xml".
*
* @param pClassLoader the class loader to use
* @param pName name of the resource
* @param pGuessSuffix guess suffix
*
* @return an input stream reading from the resource
*/
private static
InputStream getResourceAsStream(
ClassLoader pClassLoader,
String pName, boolean
pGuessSuffix) {
InputStream is;
if (!
pGuessSuffix) {
is =
pClassLoader.
getResourceAsStream(
pName);
// If XML, wrap stream
if (
is != null &&
pName.
endsWith(
XML_PROPERTIES)) {
is = new
XMLPropertiesInputStream(
is);
}
}
else {
// Try normal properties
is =
pClassLoader.
getResourceAsStream(
pName +
STD_PROPERTIES);
// Try XML
if (
is == null) {
is =
pClassLoader.
getResourceAsStream(
pName +
XML_PROPERTIES);
// Wrap stream
if (
is != null) {
is = new
XMLPropertiesInputStream(
is);
}
}
}
// Return stream
return
is;
}
/**
* Gets the named file as a stream from the current directory.
* If the pGuessSuffix parameter is true, the method will try to append
* typical properties file suffixes, such as ".properties" or ".xml".
*
* @param pName name of the resource
* @param pGuessSuffix guess suffix
*
* @return an input stream reading from the resource
*/
private static
InputStream getFileAsStream(
String pName, boolean
pGuessSuffix) {
InputStream is = null;
File propertiesFile;
try {
if (!
pGuessSuffix) {
// Get file
propertiesFile = new
File(
pName);
if (
propertiesFile.
exists()) {
is = new
FileInputStream(
propertiesFile);
// If XML, wrap stream
if (
pName.
endsWith(
XML_PROPERTIES)) {
is = new
XMLPropertiesInputStream(
is);
}
}
}
else {
// Try normal properties
propertiesFile = new
File(
pName +
STD_PROPERTIES);
if (
propertiesFile.
exists()) {
is = new
FileInputStream(
propertiesFile);
}
else {
// Try XML
propertiesFile = new
File(
pName +
XML_PROPERTIES);
if (
propertiesFile.
exists()) {
// Wrap stream
is = new
XMLPropertiesInputStream(new
FileInputStream(
propertiesFile));
}
}
}
}
catch (
FileNotFoundException fnf) {
// Should not happen, as we always test that the file .exists()
// before creating InputStream
// assert false;
}
return
is;
}
/**
* Utility method for loading a named properties-file for a class.
* <P>
* The properties-file is loaded through either:
* <OL>
* <LI>The given class' class loader (from classpath)</LI>
* <LI>Or, the system class loader (from classpath)</LI>
* <LI>Or, if it cannot be found in the classpath, an attempt to read from
* the current directory (or full path if given).</LI>
* </OL>
* <P>
* Both normal java.util.Properties and com.twelvemonkeys.util.XMLProperties
* are supported (XML-properties must have ".xml" as its file extension).
*
* @param pClass The class to load properties for. If this parameter is
* {@code null}, the method will work exactly as
* {@link #loadProperties(String)}
* @param pName The name of the properties-file. If this parameter is
* {@code null}, the method will work exactly as
* {@link #loadProperties(Class)}
*
* @return A Properties mapping read from the given file or for the given
* class. <!--If no properties-file was found, an empty Properties object is
* returned.-->
*
* @throws NullPointerException if both {@code pName} and
* {@code pClass} paramters are {@code null}
* @throws IOException if an error occurs during load.
* @throws FileNotFoundException if no properties-file could be found.
*
* @see #loadProperties(String)
* @see #loadProperties(Class)
* @see java.lang.ClassLoader#getResourceAsStream
* @see java.lang.ClassLoader#getSystemResourceAsStream
*
* @todo Reconsider ever using the System ClassLoader: http://www.javaworld.com/javaworld/javaqa/2003-06/01-qa-0606-load.html
* @todo Consider using Context Classloader instead?
*/
public static
Properties loadProperties(
Class pClass,
String pName) throws
IOException
{
// Convert to name the classloader understands
String name = !
StringUtil.
isEmpty(
pName) ?
pName :
pClass.
getName().
replace('.', '/');
// Should we try to guess suffix?
boolean
guessSuffix = (
pName == null ||
pName.
indexOf('.') < 0);
InputStream is;
// TODO: WHAT IF MULTIPLE RESOURCES EXISTS?!
// Try loading resource through the current class' classloader
if (
pClass != null && (
is =
getResourceAsStream(
pClass.
getClassLoader(),
name,
guessSuffix)) != null) {
//&& (is = getResourceAsStream(pClass, name, guessSuffix)) != null) {
// Nothing to do
//System.out.println(((is instanceof XMLPropertiesInputStream) ?
// "XML-properties" : "Normal .properties")
// + " from Class' ClassLoader");
}
// If that fails, try the system classloader
else if ((
is =
getResourceAsStream(
ClassLoader.
getSystemClassLoader(),
name,
guessSuffix)) != null) {
//else if ((is = getSystemResourceAsStream(name, guessSuffix)) != null) {
// Nothing to do
//System.out.println(((is instanceof XMLPropertiesInputStream) ?
// "XML-properties" : "Normal .properties")
// + " from System ClassLoader");
}
// All failed, try loading from file
else if ((
is =
getFileAsStream(
name,
guessSuffix)) != null) {
//System.out.println(((is instanceof XMLPropertiesInputStream) ?
// "XML-properties" : "Normal .properties")
// + " from System ClassLoader");
}
else {
if (
guessSuffix) {
// TODO: file extension iterator or something...
throw new
FileNotFoundException(
name + ".properties or " +
name + ".xml");
}
else {
throw new
FileNotFoundException(
name);
}
}
// We have inputstream now, load...
try {
return
loadProperties(
is);
}
finally {
// NOTE: If is == null, a FileNotFoundException must have been thrown above
try {
is.
close();
}
catch (
IOException ioe) {
// Not critical...
}
}
}
/**
* Utility method for loading a properties-file for a given class.
* The properties are searched for on the form
* "com/package/ClassName.properties" or
* "com/package/ClassName.xml".
* <P>
* The properties-file is loaded through either:
* <OL>
* <LI>The given class' class loader (from classpath)</LI>
* <LI>Or, the system class loader (from classpath)</LI>
* <LI>Or, if it cannot be found in the classpath, an attempt to read from
* the current directory (or full path if given).</LI>
* </OL>
* <P>
* Both normal java.util.Properties and com.twelvemonkeys.util.XMLProperties
* are supported (XML-properties must have ".xml" as its file extension).
*
* @param pClass The class to load properties for
* @return A Properties mapping for the given class. <!--If no properties-
* file was found, an empty Properties object is returned.-->
*
* @throws NullPointerException if the {@code pClass} paramters is
* {@code null}
* @throws IOException if an error occurs during load.
* @throws FileNotFoundException if no properties-file could be found.
*
* @see #loadProperties(String)
* @see #loadProperties(Class, String)
* @see java.lang.ClassLoader#getResourceAsStream
* @see java.lang.ClassLoader#getSystemResourceAsStream
*
*/
public static
Properties loadProperties(
Class pClass) throws
IOException {
return
loadProperties(
pClass, null);
}
/**
* Utility method for loading a named properties-file.
* <P>
* The properties-file is loaded through either:
* <OL>
* <LI>The system class loader (from classpath)</LI>
* <LI>Or, if it cannot be found in the classpath, an attempt to read from
* the current directory.</LI>
* </OL>
* <P>
* Both normal java.util.Properties and com.twelvemonkeys.util.XMLProperties
* are supported (XML-properties must have ".xml" as its file extension).
*
* @param pName The name of the properties-file.
* @return A Properties mapping read from the given file. <!--If no properties-
* file was found, an empty Properties object is returned.-->
*
* @throws NullPointerException if the {@code pName} paramters is
* {@code null}
* @throws IOException if an error occurs during load.
* @throws FileNotFoundException if no properties-file could be found.
*
* @see #loadProperties(Class)
* @see #loadProperties(Class, String)
* @see java.lang.ClassLoader#getSystemResourceAsStream
*
*/
public static
Properties loadProperties(
String pName) throws
IOException {
return
loadProperties(null,
pName);
}
/*
* Utility method for loading a properties-file.
* <P>
* The properties files may also be contained in a zip/jar-file named
* by the {@code com.twelvemonkeys.util.Config} system property (use "java -D"
* to override). Default is "config.zip" in the current directory.
*
* @param pName The name of the file to loaded
* @return A Properties mapping for the given class. If no properties-
* file was found, an empty Properties object is returned.
*
*/
/*
public static Properties loadProperties(String pName) throws IOException {
// Use XML?
boolean useXML = pName.endsWith(XML_PROPERTIES) ? true : false;
InputStream is = null;
File file = new File(pName);
String configName = System.getProperty("com.twelvemonkeys.util.Config");
File configArchive = new File(!StringUtil.isEmpty(configName)
? configName : DEFAULT_CONFIG);
// Get input stream to the file containing the properties
if (file.exists()) {
// Try reading from file, normal way
is = new FileInputStream(file);
}
else if (configArchive.exists()) {
// Try reading properties from zip-file
ZipFile zip = new ZipFile(configArchive);
ZipEntry ze = zip.getEntry(pName);
if (ze != null) {
is = zip.getInputStream(ze);
}
}
// Do the loading
try {
// Load the properties
return loadProperties(is, useXML);
}
finally {
// Try closing the archive to free resources
if (is != null) {
try {
is.close();
}
catch (IOException ioe) {
// Not critical...
}
}
}
}
*/
/**
* Returns a Properties, loaded from the given inputstream. If the given
* inputstream is null, then an empty Properties object is returned.
*
* @param pInput the inputstream to read from
*
* @return a Properties object read from the given stream, or an empty
* Properties mapping, if the stream is null.
*
* @throws IOException if an error occurred when reading from the input
* stream.
*
*/
private static
Properties loadProperties(
InputStream pInput)
throws
IOException {
if (
pInput == null) {
throw new
IllegalArgumentException("InputStream == null!");
}
Properties mapping = new
Properties();
/*if (pInput instanceof XMLPropertiesInputStream) {
mapping = new XMLProperties();
}
else {
mapping = new Properties();
}*/
// Load the properties
mapping.
load(
pInput);
return
mapping;
}
@
SuppressWarnings({"SuspiciousSystemArraycopy"})
public static
Object clone(final
Cloneable pObject) throws
CloneNotSupportedException {
if (
pObject == null) {
return null; // Null is clonable.. Easy. ;-)
}
// All arrays does have a clone method, but it's invisible for reflection...
// By luck, multi-dimensional primitive arrays are instances of Object[]
if (
pObject instanceof
Object[]) {
return ((
Object[])
pObject).
clone();
}
else if (
pObject.
getClass().
isArray()) {
// One-dimensional primitive array, cloned manually
int
lenght =
Array.
getLength(
pObject);
Object clone =
Array.
newInstance(
pObject.
getClass().
getComponentType(),
lenght);
System.
arraycopy(
pObject, 0,
clone, 0,
lenght);
return
clone;
}
try {
// Find the clone method
Method clone = null;
Class clazz =
pObject.
getClass();
do {
try {
clone =
clazz.
getDeclaredMethod("clone");
break; // Found, or throws exception above
}
catch (
NoSuchMethodException ignore) {
// Ignore
}
}
while ((
clazz =
clazz.
getSuperclass()) != null);
// NOTE: This should never happen
if (
clone == null) {
throw new
CloneNotSupportedException(
pObject.
getClass().
getName());
}
// Override access if needed
if (!
clone.
isAccessible()) {
clone.
setAccessible(true);
}
// Invoke clone method on original object
return
clone.
invoke(
pObject);
}
catch (
SecurityException e) {
CloneNotSupportedException cns = new
CloneNotSupportedException(
pObject.
getClass().
getName());
cns.
initCause(
e);
throw
cns;
}
catch (
IllegalAccessException e) {
throw new
CloneNotSupportedException(
pObject.
getClass().
getName());
}
catch (
InvocationTargetException e) {
if (
e.
getTargetException() instanceof
CloneNotSupportedException) {
throw (
CloneNotSupportedException)
e.
getTargetException();
}
else if (
e.
getTargetException() instanceof
RuntimeException) {
throw (
RuntimeException)
e.
getTargetException();
}
else if (
e.
getTargetException() instanceof
Error) {
throw (
Error)
e.
getTargetException();
}
throw new
CloneNotSupportedException(
pObject.
getClass().
getName());
}
}
// public static void loadLibrary(String pLibrary) {
// NativeLoader.loadLibrary(pLibrary);
// }
//
// public static void loadLibrary(String pLibrary, ClassLoader pLoader) {
// NativeLoader.loadLibrary(pLibrary, pLoader);
// }
public static void
main(
String[]
args) throws
CloneNotSupportedException {
System.
out.
println("clone: " +
args.
clone().length + " (" +
args.length + ")");
System.
out.
println("copy: " + ((
String[])
clone(
args)).length + " (" +
args.length + ")");
int[]
ints = {1,2,3};
int[]
copies = (int[])
clone(
ints);
System.
out.
println("Copies: " +
copies.length + " (" +
ints.length + ")");
int[][]
intsToo = {{1}, {2,3}, {4,5,6}};
int[][]
copiesToo = (int[][])
clone(
intsToo);
System.
out.
println("Copies: " +
copiesToo.length + " (" +
intsToo.length + ")");
System.
out.
println("Copies0: " +
copiesToo[0].length + " (" +
intsToo[0].length + ")");
System.
out.
println("Copies1: " +
copiesToo[1].length + " (" +
intsToo[1].length + ")");
System.
out.
println("Copies2: " +
copiesToo[2].length + " (" +
intsToo[2].length + ")");
Map<
String,
String>
map = new
HashMap<
String,
String>();
for (
String arg :
args) {
map.
put(
arg,
arg);
}
Map copy = (
Map)
clone((
Cloneable)
map);
System.
out.
println("Map : " +
map);
System.
out.
println("Copy: " +
copy);
/*
SecurityManager sm = System.getSecurityManager();
try {
System.setSecurityManager(new SecurityManager() {
public void checkPermission(Permission perm) {
if (perm.getName().equals("suppressAccessChecks")) {
throw new SecurityException();
}
//super.checkPermission(perm);
}
});
*/
Cloneable cloneable = new
Cloneable() {}; // No public clone method
Cloneable clone = (
Cloneable)
clone(
cloneable);
System.
out.
println("cloneable: " +
cloneable);
System.
out.
println("clone: " +
clone);
/*
}
finally {
System.setSecurityManager(sm);
}
*/
AccessController.
doPrivileged(new
PrivilegedAction<
Void>() {
public
Void run() {
return null;
}
},
AccessController.
getContext());
//String string = args.length > 0 ? args[0] : "jaffa";
//clone(string);
}
/**
* Tests if a named class is generally available.
* If a class is considered available, a call to
* {@code Class.forName(pClassName)} will not result in an exception.
*
* @param pClassName the class name to test
* @return {@code true} if available
*/
public static boolean
isClassAvailable(
String pClassName) {
return
isClassAvailable(
pClassName, (
ClassLoader) null);
}
/**
* Tests if a named class is available from another class.
* If a class is considered available, a call to
* {@code Class.forName(pClassName, true, pFromClass.getClassLoader())}
* will not result in an exception.
*
* @param pClassName the class name to test
* @param pFromClass the class to test from
* @return {@code true} if available
*/
public static boolean
isClassAvailable(
String pClassName,
Class pFromClass) {
ClassLoader loader =
pFromClass != null ?
pFromClass.
getClassLoader() : null;
return
isClassAvailable(
pClassName,
loader);
}
private static boolean
isClassAvailable(
String pClassName,
ClassLoader pLoader) {
try {
// TODO: Sometimes init is not needed, but need to find a way to know...
getClass(
pClassName, true,
pLoader);
return true;
}
catch (
SecurityException ignore) {
// Ignore
}
catch (
ClassNotFoundException ignore) {
// Ignore
}
catch (
LinkageError ignore) {
// Ignore
}
return false;
}
public static boolean
isFieldAvailable(final
String pClassName, final
String pFieldName) {
return
isFieldAvailable(
pClassName,
pFieldName, (
ClassLoader) null);
}
public static boolean
isFieldAvailable(final
String pClassName, final
String pFieldName, final
Class pFromClass) {
ClassLoader loader =
pFromClass != null ?
pFromClass.
getClassLoader() : null;
return
isFieldAvailable(
pClassName,
pFieldName,
loader);
}
private static boolean
isFieldAvailable(final
String pClassName, final
String pFieldName, final
ClassLoader pLoader) {
try {
Class cl =
getClass(
pClassName, false,
pLoader);
Field field =
cl.
getField(
pFieldName);
if (
field != null) {
return true;
}
}
catch (
ClassNotFoundException ignore) {
// Ignore
}
catch (
LinkageError ignore) {
// Ignore
}
catch (
NoSuchFieldException ignore) {
// Ignore
}
return false;
}
public static boolean
isMethodAvailable(
String pClassName,
String pMethodName) {
// Finds void only
return
isMethodAvailable(
pClassName,
pMethodName, null, (
ClassLoader) null);
}
public static boolean
isMethodAvailable(
String pClassName,
String pMethodName,
Class[]
pParams) {
return
isMethodAvailable(
pClassName,
pMethodName,
pParams, (
ClassLoader) null);
}
public static boolean
isMethodAvailable(
String pClassName,
String pMethodName,
Class[]
pParams,
Class pFromClass) {
ClassLoader loader =
pFromClass != null ?
pFromClass.
getClassLoader() : null;
return
isMethodAvailable(
pClassName,
pMethodName,
pParams,
loader);
}
private static boolean
isMethodAvailable(
String pClassName,
String pMethodName,
Class[]
pParams,
ClassLoader pLoader) {
try {
Class cl =
getClass(
pClassName, false,
pLoader);
Method method =
cl.
getMethod(
pMethodName,
pParams);
if (
method != null) {
return true;
}
}
catch (
ClassNotFoundException ignore) {
// Ignore
}
catch (
LinkageError ignore) {
// Ignore
}
catch (
NoSuchMethodException ignore) {
// Ignore
}
return false;
}
private static
Class getClass(
String pClassName, boolean
pInitialize,
ClassLoader pLoader) throws
ClassNotFoundException {
// NOTE: We need the context class loader, as SystemUtil's
// class loader may have a totally different class loader than
// the original caller class (as in Class.forName(cn, false, null)).
ClassLoader loader =
pLoader != null ?
pLoader :
Thread.
currentThread().
getContextClassLoader();
return
Class.
forName(
pClassName,
pInitialize,
loader);
}
}