com.signalfx.shaded.jetty.util.TypeUtil Maven / Gradle / Ivy
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package com.signalfx.shaded.jetty.util;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.security.AccessController;
import java.security.CodeSource;
import java.security.PrivilegedAction;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import com.signalfx.shaded.jetty.util.log.Log;
import com.signalfx.shaded.jetty.util.log.Logger;
/**
* TYPE Utilities.
* Provides various static utility methods for manipulating types and their
* string representations.
*
* @since Jetty 4.1
*/
public class TypeUtil
{
private static final Logger LOG = Log.getLogger(TypeUtil.class);
public static final Class[] NO_ARGS = new Class[]{};
public static final int CR = '\r';
public static final int LF = '\n';
private static final HashMap> name2Class = new HashMap<>();
static
{
name2Class.put("boolean", java.lang.Boolean.TYPE);
name2Class.put("byte", java.lang.Byte.TYPE);
name2Class.put("char", java.lang.Character.TYPE);
name2Class.put("double", java.lang.Double.TYPE);
name2Class.put("float", java.lang.Float.TYPE);
name2Class.put("int", java.lang.Integer.TYPE);
name2Class.put("long", java.lang.Long.TYPE);
name2Class.put("short", java.lang.Short.TYPE);
name2Class.put("void", java.lang.Void.TYPE);
name2Class.put("java.lang.Boolean.TYPE", java.lang.Boolean.TYPE);
name2Class.put("java.lang.Byte.TYPE", java.lang.Byte.TYPE);
name2Class.put("java.lang.Character.TYPE", java.lang.Character.TYPE);
name2Class.put("java.lang.Double.TYPE", java.lang.Double.TYPE);
name2Class.put("java.lang.Float.TYPE", java.lang.Float.TYPE);
name2Class.put("java.lang.Integer.TYPE", java.lang.Integer.TYPE);
name2Class.put("java.lang.Long.TYPE", java.lang.Long.TYPE);
name2Class.put("java.lang.Short.TYPE", java.lang.Short.TYPE);
name2Class.put("java.lang.Void.TYPE", java.lang.Void.TYPE);
name2Class.put("java.lang.Boolean", java.lang.Boolean.class);
name2Class.put("java.lang.Byte", java.lang.Byte.class);
name2Class.put("java.lang.Character", java.lang.Character.class);
name2Class.put("java.lang.Double", java.lang.Double.class);
name2Class.put("java.lang.Float", java.lang.Float.class);
name2Class.put("java.lang.Integer", java.lang.Integer.class);
name2Class.put("java.lang.Long", java.lang.Long.class);
name2Class.put("java.lang.Short", java.lang.Short.class);
name2Class.put("Boolean", java.lang.Boolean.class);
name2Class.put("Byte", java.lang.Byte.class);
name2Class.put("Character", java.lang.Character.class);
name2Class.put("Double", java.lang.Double.class);
name2Class.put("Float", java.lang.Float.class);
name2Class.put("Integer", java.lang.Integer.class);
name2Class.put("Long", java.lang.Long.class);
name2Class.put("Short", java.lang.Short.class);
name2Class.put(null, java.lang.Void.TYPE);
name2Class.put("string", java.lang.String.class);
name2Class.put("String", java.lang.String.class);
name2Class.put("java.lang.String", java.lang.String.class);
}
private static final HashMap, String> class2Name = new HashMap<>();
static
{
class2Name.put(java.lang.Boolean.TYPE, "boolean");
class2Name.put(java.lang.Byte.TYPE, "byte");
class2Name.put(java.lang.Character.TYPE, "char");
class2Name.put(java.lang.Double.TYPE, "double");
class2Name.put(java.lang.Float.TYPE, "float");
class2Name.put(java.lang.Integer.TYPE, "int");
class2Name.put(java.lang.Long.TYPE, "long");
class2Name.put(java.lang.Short.TYPE, "short");
class2Name.put(java.lang.Void.TYPE, "void");
class2Name.put(java.lang.Boolean.class, "java.lang.Boolean");
class2Name.put(java.lang.Byte.class, "java.lang.Byte");
class2Name.put(java.lang.Character.class, "java.lang.Character");
class2Name.put(java.lang.Double.class, "java.lang.Double");
class2Name.put(java.lang.Float.class, "java.lang.Float");
class2Name.put(java.lang.Integer.class, "java.lang.Integer");
class2Name.put(java.lang.Long.class, "java.lang.Long");
class2Name.put(java.lang.Short.class, "java.lang.Short");
class2Name.put(null, "void");
class2Name.put(java.lang.String.class, "java.lang.String");
}
private static final HashMap, Method> class2Value = new HashMap<>();
static
{
try
{
Class[] s = {java.lang.String.class};
class2Value.put(java.lang.Boolean.TYPE,
java.lang.Boolean.class.getMethod("valueOf", s));
class2Value.put(java.lang.Byte.TYPE,
java.lang.Byte.class.getMethod("valueOf", s));
class2Value.put(java.lang.Double.TYPE,
java.lang.Double.class.getMethod("valueOf", s));
class2Value.put(java.lang.Float.TYPE,
java.lang.Float.class.getMethod("valueOf", s));
class2Value.put(java.lang.Integer.TYPE,
java.lang.Integer.class.getMethod("valueOf", s));
class2Value.put(java.lang.Long.TYPE,
java.lang.Long.class.getMethod("valueOf", s));
class2Value.put(java.lang.Short.TYPE,
java.lang.Short.class.getMethod("valueOf", s));
class2Value.put(java.lang.Boolean.class,
java.lang.Boolean.class.getMethod("valueOf", s));
class2Value.put(java.lang.Byte.class,
java.lang.Byte.class.getMethod("valueOf", s));
class2Value.put(java.lang.Double.class,
java.lang.Double.class.getMethod("valueOf", s));
class2Value.put(java.lang.Float.class,
java.lang.Float.class.getMethod("valueOf", s));
class2Value.put(java.lang.Integer.class,
java.lang.Integer.class.getMethod("valueOf", s));
class2Value.put(java.lang.Long.class,
java.lang.Long.class.getMethod("valueOf", s));
class2Value.put(java.lang.Short.class,
java.lang.Short.class.getMethod("valueOf", s));
}
catch (Exception e)
{
throw new Error(e);
}
}
private static final List, URI>> LOCATION_METHODS = new ArrayList<>();
private static final Function, URI> MODULE_LOCATION;
static
{
// Lookup order in LOCATION_METHODS is important.
LOCATION_METHODS.add(TypeUtil::getCodeSourceLocation);
Function, URI> moduleFunc = null;
try
{
Class clazzModuleLocation = TypeUtil.class.getClassLoader().loadClass(TypeUtil.class.getPackage().getName() + ".ModuleLocation");
Object obj = clazzModuleLocation.getConstructor().newInstance();
if (obj instanceof Function)
{
//noinspection unchecked
moduleFunc = (Function, URI>)obj;
LOCATION_METHODS.add(moduleFunc);
}
}
catch (Throwable t)
{
LOG.debug("This JVM Runtime does not support Modules, disabling Jetty internal support");
}
MODULE_LOCATION = moduleFunc;
LOCATION_METHODS.add(TypeUtil::getClassLoaderLocation);
LOCATION_METHODS.add(TypeUtil::getSystemClassLoaderLocation);
}
/**
* Array to List.
*
* Works like {@link Arrays#asList(Object...)}, but handles null arrays.
*
* @param a the array to convert to a list
* @param the array and list entry type
* @return a list backed by the array.
*/
public static List asList(T[] a)
{
if (a == null)
return Collections.emptyList();
return Arrays.asList(a);
}
/**
* Class from a canonical name for a type.
*
* @param name A class or type name.
* @return A class , which may be a primitive TYPE field..
*/
public static Class fromName(String name)
{
return name2Class.get(name);
}
/**
* Canonical name for a type.
*
* @param type A class , which may be a primitive TYPE field.
* @return Canonical name.
*/
public static String toName(Class type)
{
return class2Name.get(type);
}
/**
* Return the Classpath / Classloader reference for the
* provided class file.
*
*
* Convenience method for the code
*
*
*
* String ref = myObject.getClass().getName().replace('.','/') + ".class";
*
*
* @param clazz the class to reference
* @return the classpath reference syntax for the class file
*/
public static String toClassReference(Class clazz)
{
return TypeUtil.toClassReference(clazz.getName());
}
/**
* Return the Classpath / Classloader reference for the
* provided class file.
*
*
* Convenience method for the code
*
*
*
* String ref = myClassName.replace('.','/') + ".class";
*
*
* @param className the class to reference
* @return the classpath reference syntax for the class file
*/
public static String toClassReference(String className)
{
return StringUtil.replace(className, '.', '/').concat(".class");
}
/**
* Convert String value to instance.
*
* @param type The class of the instance, which may be a primitive TYPE field.
* @param value The value as a string.
* @return The value as an Object.
*/
public static Object valueOf(Class type, String value)
{
try
{
if (type.equals(java.lang.String.class))
return value;
Method m = class2Value.get(type);
if (m != null)
return m.invoke(null, value);
if (type.equals(java.lang.Character.TYPE) ||
type.equals(java.lang.Character.class))
return value.charAt(0);
Constructor c = type.getConstructor(java.lang.String.class);
return c.newInstance(value);
}
catch (NoSuchMethodException | IllegalAccessException | InstantiationException x)
{
LOG.ignore(x);
}
catch (InvocationTargetException x)
{
if (x.getTargetException() instanceof Error)
throw (Error)x.getTargetException();
LOG.ignore(x);
}
return null;
}
/**
* Convert String value to instance.
*
* @param type classname or type (eg int)
* @param value The value as a string.
* @return The value as an Object.
*/
public static Object valueOf(String type, String value)
{
return valueOf(fromName(type), value);
}
/**
* Parse an int from a substring.
* Negative numbers are not handled.
*
* @param s String
* @param offset Offset within string
* @param length Length of integer or -1 for remainder of string
* @param base base of the integer
* @return the parsed integer
* @throws NumberFormatException if the string cannot be parsed
*/
public static int parseInt(String s, int offset, int length, int base)
throws NumberFormatException
{
int value = 0;
if (length < 0)
length = s.length() - offset;
for (int i = 0; i < length; i++)
{
char c = s.charAt(offset + i);
int digit = convertHexDigit((int)c);
if (digit < 0 || digit >= base)
throw new NumberFormatException(s.substring(offset, offset + length));
value = value * base + digit;
}
return value;
}
/**
* Parse an int from a byte array of ascii characters.
* Negative numbers are not handled.
*
* @param b byte array
* @param offset Offset within string
* @param length Length of integer or -1 for remainder of string
* @param base base of the integer
* @return the parsed integer
* @throws NumberFormatException if the array cannot be parsed into an integer
*/
public static int parseInt(byte[] b, int offset, int length, int base)
throws NumberFormatException
{
int value = 0;
if (length < 0)
length = b.length - offset;
for (int i = 0; i < length; i++)
{
char c = (char)(0xff & b[offset + i]);
int digit = c - '0';
if (digit < 0 || digit >= base || digit >= 10)
{
digit = 10 + c - 'A';
if (digit < 10 || digit >= base)
digit = 10 + c - 'a';
}
if (digit < 0 || digit >= base)
throw new NumberFormatException(new String(b, offset, length));
value = value * base + digit;
}
return value;
}
public static byte[] parseBytes(String s, int base)
{
byte[] bytes = new byte[s.length() / 2];
for (int i = 0; i < s.length(); i += 2)
{
bytes[i / 2] = (byte)TypeUtil.parseInt(s, i, 2, base);
}
return bytes;
}
public static String toString(byte[] bytes, int base)
{
StringBuilder buf = new StringBuilder();
for (byte b : bytes)
{
int bi = 0xff & b;
int c = '0' + (bi / base) % base;
if (c > '9')
c = 'a' + (c - '0' - 10);
buf.append((char)c);
c = '0' + bi % base;
if (c > '9')
c = 'a' + (c - '0' - 10);
buf.append((char)c);
}
return buf.toString();
}
/**
* @param c An ASCII encoded character 0-9 a-f A-F
* @return The byte value of the character 0-16.
*/
public static byte convertHexDigit(byte c)
{
byte b = (byte)((c & 0x1f) + ((c >> 6) * 0x19) - 0x10);
if (b < 0 || b > 15)
throw new NumberFormatException("!hex " + c);
return b;
}
/**
* @param c An ASCII encoded character 0-9 a-f A-F
* @return The byte value of the character 0-16.
*/
public static int convertHexDigit(char c)
{
int d = ((c & 0x1f) + ((c >> 6) * 0x19) - 0x10);
if (d < 0 || d > 15)
throw new NumberFormatException("!hex " + c);
return d;
}
/**
* @param c An ASCII encoded character 0-9 a-f A-F
* @return The byte value of the character 0-16.
*/
public static int convertHexDigit(int c)
{
int d = ((c & 0x1f) + ((c >> 6) * 0x19) - 0x10);
if (d < 0 || d > 15)
throw new NumberFormatException("!hex " + c);
return d;
}
public static void toHex(byte b, Appendable buf)
{
try
{
int d = 0xf & ((0xF0 & b) >> 4);
buf.append((char)((d > 9 ? ('A' - 10) : '0') + d));
d = 0xf & b;
buf.append((char)((d > 9 ? ('A' - 10) : '0') + d));
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
public static void toHex(int value, Appendable buf) throws IOException
{
int d = 0xf & ((0xF0000000 & value) >> 28);
buf.append((char)((d > 9 ? ('A' - 10) : '0') + d));
d = 0xf & ((0x0F000000 & value) >> 24);
buf.append((char)((d > 9 ? ('A' - 10) : '0') + d));
d = 0xf & ((0x00F00000 & value) >> 20);
buf.append((char)((d > 9 ? ('A' - 10) : '0') + d));
d = 0xf & ((0x000F0000 & value) >> 16);
buf.append((char)((d > 9 ? ('A' - 10) : '0') + d));
d = 0xf & ((0x0000F000 & value) >> 12);
buf.append((char)((d > 9 ? ('A' - 10) : '0') + d));
d = 0xf & ((0x00000F00 & value) >> 8);
buf.append((char)((d > 9 ? ('A' - 10) : '0') + d));
d = 0xf & ((0x000000F0 & value) >> 4);
buf.append((char)((d > 9 ? ('A' - 10) : '0') + d));
d = 0xf & value;
buf.append((char)((d > 9 ? ('A' - 10) : '0') + d));
Integer.toString(0, 36);
}
public static void toHex(long value, Appendable buf) throws IOException
{
toHex((int)(value >> 32), buf);
toHex((int)value, buf);
}
public static String toHexString(byte b)
{
return toHexString(new byte[]{b}, 0, 1);
}
public static String toHexString(byte[] b)
{
return toHexString(b, 0, b.length);
}
public static String toHexString(byte[] b, int offset, int length)
{
StringBuilder buf = new StringBuilder();
for (int i = offset; i < offset + length; i++)
{
int bi = 0xff & b[i];
int c = '0' + (bi / 16) % 16;
if (c > '9')
c = 'A' + (c - '0' - 10);
buf.append((char)c);
c = '0' + bi % 16;
if (c > '9')
c = 'a' + (c - '0' - 10);
buf.append((char)c);
}
return buf.toString();
}
public static byte[] fromHexString(String s)
{
if (s.length() % 2 != 0)
throw new IllegalArgumentException(s);
byte[] array = new byte[s.length() / 2];
for (int i = 0; i < array.length; i++)
{
int b = Integer.parseInt(s.substring(i * 2, i * 2 + 2), 16);
array[i] = (byte)(0xff & b);
}
return array;
}
public static void dump(Class c)
{
System.err.println("Dump: " + c);
dump(c.getClassLoader());
}
public static void dump(ClassLoader cl)
{
System.err.println("Dump Loaders:");
while (cl != null)
{
System.err.println(" loader " + cl);
cl = cl.getParent();
}
}
@Deprecated
public static Object call(Class oClass, String methodName, Object obj, Object[] arg) throws InvocationTargetException, NoSuchMethodException
{
throw new UnsupportedOperationException();
}
@Deprecated
public static Object construct(Class klass, Object[] arguments) throws InvocationTargetException, NoSuchMethodException
{
throw new UnsupportedOperationException();
}
@Deprecated
public static Object construct(Class klass, Object[] arguments, Map namedArgMap) throws InvocationTargetException, NoSuchMethodException
{
throw new UnsupportedOperationException();
}
/**
* @param o Object to test for true
* @return True if passed object is not null and is either a Boolean with value true or evaluates to a string that evaluates to true.
*/
public static boolean isTrue(Object o)
{
if (o == null)
return false;
if (o instanceof Boolean)
return ((Boolean)o).booleanValue();
return Boolean.parseBoolean(o.toString());
}
/**
* @param o Object to test for false
* @return True if passed object is not null and is either a Boolean with value false or evaluates to a string that evaluates to false.
*/
public static boolean isFalse(Object o)
{
if (o == null)
return false;
if (o instanceof Boolean)
return !((Boolean)o).booleanValue();
return "false".equalsIgnoreCase(o.toString());
}
/**
* Attempt to find the Location of a loaded Class.
*
* This can be null for primitives, void, and in-memory classes.
*
*
* @param clazz the loaded class to find a location for.
* @return the location as a URI (this is a URI pointing to a holder of the class: a directory,
* a jar file, a {@code jrt://} resource, etc), or null of no location available.
*/
public static URI getLocationOfClass(Class clazz)
{
for (Function, URI> locationFunction : LOCATION_METHODS)
{
try
{
URI location = locationFunction.apply(clazz);
if (location != null)
{
return location;
}
}
catch (Throwable cause)
{
cause.printStackTrace(System.err);
}
}
return null;
}
public static URI getClassLoaderLocation(Class clazz)
{
return getClassLoaderLocation(clazz, clazz.getClassLoader());
}
public static URI getSystemClassLoaderLocation(Class clazz)
{
return getClassLoaderLocation(clazz, ClassLoader.getSystemClassLoader());
}
public static URI getClassLoaderLocation(Class clazz, ClassLoader loader)
{
if (loader == null)
{
return null;
}
try
{
String resourceName = TypeUtil.toClassReference(clazz);
if (loader != null)
{
URL url = loader.getResource(resourceName);
if (url != null)
{
URI uri = url.toURI();
String uriStr = uri.toASCIIString();
if (uriStr.startsWith("jar:file:"))
{
uriStr = uriStr.substring(4);
int idx = uriStr.indexOf("!/");
if (idx > 0)
{
return URI.create(uriStr.substring(0, idx));
}
}
return uri;
}
}
}
catch (URISyntaxException ignored)
{
}
return null;
}
public static URI getCodeSourceLocation(Class clazz)
{
try
{
ProtectionDomain domain = AccessController.doPrivileged((PrivilegedAction)() -> clazz.getProtectionDomain());
if (domain != null)
{
CodeSource source = domain.getCodeSource();
if (source != null)
{
URL location = source.getLocation();
if (location != null)
{
return location.toURI();
}
}
}
}
catch (URISyntaxException ignored)
{
}
return null;
}
public static URI getModuleLocation(Class clazz)
{
// In Jetty 10, this method can be implemented directly, without reflection
if (MODULE_LOCATION != null)
{
return MODULE_LOCATION.apply(clazz);
}
return null;
}
}