T[] shallowCopy(final T[] array)
return array.clone();
}
+ /**
+ * Creates and returns an array containing the provided elements.
+ *
+ * This method accepts a variable number of arguments and returns them as an array of type {@code T[]}.
+ * It is primarily used to facilitate array creation in generic contexts, where type inference is necessary.
+ *
+ *
Example Usage:
+ *
{@code
+ * String[] stringArray = createArray("Apple", "Banana", "Cherry");
+ * Integer[] integerArray = createArray(1, 2, 3, 4);
+ * Person[] personArray = createArray(new Person("Alice"), new Person("Bob"));
+ * }
+ *
+ * Important Considerations:
+ *
+ * - Type Safety: Due to type erasure in Java generics, this method does not perform any type checks
+ * beyond what is already enforced by the compiler. Ensure that all elements are of the expected type {@code T} to avoid
+ * {@code ClassCastException} at runtime.
+ * - Heap Pollution: The method is annotated with {@link SafeVarargs} to suppress warnings related to heap
+ * pollution when using generics with varargs. It is safe to use because the method does not perform any unsafe operations
+ * on the varargs parameter.
+ * - Null Elements: The method does not explicitly handle {@code null} elements. If {@code null} values
+ * are passed, they will be included in the returned array.
+ * - Immutable Arrays: The returned array is mutable. To create an immutable array, consider wrapping it
+ * using {@link java.util.Collections#unmodifiableList(List)} or using third-party libraries like Guava's
+ * {@link com.google.common.collect.ImmutableList}.
+ *
+ *
+ * @param the component type of the array
+ * @param elements the elements to be stored in the array
+ * @return an array containing the provided elements
+ * @throws NullPointerException if the {@code elements} array is {@code null}
+ */
+ @SafeVarargs
+ public static T[] createArray(T... elements) {
+ return elements;
+ }
+
/**
* Adds all the elements of the given arrays into a new array.
*
diff --git a/src/main/java/com/cedarsoftware/util/ClassUtilities.java b/src/main/java/com/cedarsoftware/util/ClassUtilities.java
index 4e467a0c0..cd4536169 100644
--- a/src/main/java/com/cedarsoftware/util/ClassUtilities.java
+++ b/src/main/java/com/cedarsoftware/util/ClassUtilities.java
@@ -43,9 +43,11 @@ public class ClassUtilities
private static final Map, Class>> primitiveToWrapper = new HashMap<>(20, .8f);
private static final Map> nameToClass = new HashMap<>();
private static final Map, Class>> wrapperMap = new HashMap<>();
+ // Cache for OSGi ClassLoader to avoid repeated reflection calls
+ private static volatile ClassLoader osgiClassLoader;
+ private static volatile boolean osgiChecked = false;
- static
- {
+ static {
prims.add(Byte.class);
prims.add(Short.class);
prims.add(Integer.class);
@@ -99,11 +101,11 @@ public class ClassUtilities
* Add alias names for classes to allow .forName() to bring the class (.class) back with the alias name.
* Because the alias to class name mappings are static, it is expected that these are set up during initialization
* and not changed later.
+ *
* @param clazz Class to add an alias for
* @param alias String alias name
*/
- public static void addPermanentClassAlias(Class> clazz, String alias)
- {
+ public static void addPermanentClassAlias(Class> clazz, String alias) {
nameToClass.put(alias, clazz);
}
@@ -111,15 +113,16 @@ public static void addPermanentClassAlias(Class> clazz, String alias)
* Remove alias name for classes to prevent .forName() from fetching the class with the alias name.
* Because the alias to class name mappings are static, it is expected that these are set up during initialization
* and not changed later.
+ *
* @param alias String alias name
*/
- public static void removePermanentClassAlias(String alias)
- {
+ public static void removePermanentClassAlias(String alias) {
nameToClass.remove(alias);
}
/**
* Computes the inheritance distance between two classes/interfaces/primitive types.
+ *
* @param source The source class, interface, or primitive type.
* @param destination The destination class, interface, or primitive type.
* @return The number of steps from the source to the destination, or -1 if no path exists.
@@ -197,29 +200,27 @@ public static int computeInheritanceDistance(Class> source, Class> destinati
* @return boolean true if the passed in class is a Java primitive, false otherwise. The Wrapper classes
* Integer, Long, Boolean, etc. are considered primitives by this method.
*/
- public static boolean isPrimitive(Class> c)
- {
+ public static boolean isPrimitive(Class> c) {
return c.isPrimitive() || prims.contains(c);
}
/**
* Compare two primitives.
+ *
* @return 0 if they are the same, -1 if not. Primitive wrapper classes are consider the same as primitive classes.
*/
private static int comparePrimitiveToWrapper(Class> source, Class> destination) {
- try
- {
+ try {
return source.getField("TYPE").get(null).equals(destination) ? 0 : -1;
- }
- catch (Exception e)
- {
+ } catch (Exception e) {
return -1;
}
}
/**
* Given the passed in String class name, return the named JVM class.
- * @param name String name of a JVM class.
+ *
+ * @param name String name of a JVM class.
* @param classLoader ClassLoader to use when searching for JVM classes.
* @return Class instance of the named JVM class or null if not found.
*/
@@ -230,7 +231,7 @@ public static Class> forName(String name, ClassLoader classLoader) {
try {
return internalClassForName(name, classLoader);
- } catch(SecurityException e) {
+ } catch (SecurityException e) {
throw new IllegalArgumentException("Security exception, classForName() call on: " + name, e);
} catch (Exception e) {
return null;
@@ -272,66 +273,43 @@ private static Class> loadClass(String name, ClassLoader classLoader) throws C
boolean arrayType = false;
Class> primitiveArray = null;
- while (className.startsWith("["))
- {
+ while (className.startsWith("[")) {
arrayType = true;
- if (className.endsWith(";"))
- {
+ if (className.endsWith(";")) {
className = className.substring(0, className.length() - 1);
}
- if (className.equals("[B"))
- {
+ if (className.equals("[B")) {
primitiveArray = byte[].class;
- }
- else if (className.equals("[S"))
- {
+ } else if (className.equals("[S")) {
primitiveArray = short[].class;
- }
- else if (className.equals("[I"))
- {
+ } else if (className.equals("[I")) {
primitiveArray = int[].class;
- }
- else if (className.equals("[J"))
- {
+ } else if (className.equals("[J")) {
primitiveArray = long[].class;
- }
- else if (className.equals("[F"))
- {
+ } else if (className.equals("[F")) {
primitiveArray = float[].class;
- }
- else if (className.equals("[D"))
- {
+ } else if (className.equals("[D")) {
primitiveArray = double[].class;
- }
- else if (className.equals("[Z"))
- {
+ } else if (className.equals("[Z")) {
primitiveArray = boolean[].class;
- }
- else if (className.equals("[C"))
- {
+ } else if (className.equals("[C")) {
primitiveArray = char[].class;
}
int startpos = className.startsWith("[L") ? 2 : 1;
className = className.substring(startpos);
}
Class> currentClass = null;
- if (null == primitiveArray)
- {
- try
- {
+ if (null == primitiveArray) {
+ try {
currentClass = classLoader.loadClass(className);
- }
- catch (ClassNotFoundException e)
- {
+ } catch (ClassNotFoundException e) {
currentClass = Thread.currentThread().getContextClassLoader().loadClass(className);
}
}
- if (arrayType)
- {
+ if (arrayType) {
currentClass = (null != primitiveArray) ? primitiveArray : Array.newInstance(currentClass, 0).getClass();
- while (name.startsWith("[["))
- {
+ while (name.startsWith("[[")) {
currentClass = Array.newInstance(currentClass, 0).getClass();
name = name.substring(1);
}
@@ -372,4 +350,89 @@ public static Class> toPrimitiveWrapperClass(Class> primitiveClass) {
public static boolean doesOneWrapTheOther(Class> x, Class> y) {
return wrapperMap.get(x) == y;
}
+
+ /**
+ * Obtains the appropriate ClassLoader depending on whether the environment is OSGi, JPMS, or neither.
+ *
+ * @return the appropriate ClassLoader
+ */
+ public static ClassLoader getClassLoader() {
+ // Attempt to detect and handle OSGi environment
+ ClassLoader cl = getOSGiClassLoader();
+ if (cl != null) {
+ return cl;
+ }
+
+ // Use the thread's context ClassLoader if available
+ cl = Thread.currentThread().getContextClassLoader();
+ if (cl != null) {
+ return cl;
+ }
+
+ // Fallback to the ClassLoader that loaded this utility class
+ cl = ClassUtilities.class.getClassLoader();
+ if (cl != null) {
+ return cl;
+ }
+
+ // As a last resort, use the system ClassLoader
+ return ClassLoader.getSystemClassLoader();
+ }
+
+ /**
+ * Attempts to retrieve the OSGi Bundle's ClassLoader using FrameworkUtil.
+ *
+ * @return the OSGi Bundle's ClassLoader if in an OSGi environment; otherwise, null
+ */
+ private static ClassLoader getOSGiClassLoader() {
+ if (osgiChecked) {
+ return osgiClassLoader;
+ }
+
+ synchronized (ClassUtilities.class) {
+ if (osgiChecked) {
+ return osgiClassLoader;
+ }
+
+ try {
+ // Load the FrameworkUtil class from OSGi
+ Class> frameworkUtilClass = Class.forName("org.osgi.framework.FrameworkUtil");
+
+ // Get the getBundle(Class>) method
+ Method getBundleMethod = frameworkUtilClass.getMethod("getBundle", Class.class);
+
+ // Invoke FrameworkUtil.getBundle(thisClass) to get the Bundle instance
+ Object bundle = getBundleMethod.invoke(null, ClassUtilities.class);
+
+ if (bundle != null) {
+ // Get BundleWiring class
+ Class> bundleWiringClass = Class.forName("org.osgi.framework.wiring.BundleWiring");
+
+ // Get the adapt(Class) method
+ Method adaptMethod = bundle.getClass().getMethod("adapt", Class.class);
+
+ // Invoke bundle.adapt(BundleWiring.class) to get the BundleWiring instance
+ Object bundleWiring = adaptMethod.invoke(bundle, bundleWiringClass);
+
+ if (bundleWiring != null) {
+ // Get the getClassLoader() method from BundleWiring
+ Method getClassLoaderMethod = bundleWiringClass.getMethod("getClassLoader");
+
+ // Invoke getClassLoader() to obtain the ClassLoader
+ Object classLoader = getClassLoaderMethod.invoke(bundleWiring);
+
+ if (classLoader instanceof ClassLoader) {
+ osgiClassLoader = (ClassLoader) classLoader;
+ }
+ }
+ }
+ } catch (Exception e) {
+ // OSGi FrameworkUtil is not present; not in an OSGi environment
+ } finally {
+ osgiChecked = true;
+ }
+ }
+
+ return osgiClassLoader;
+ }
}
diff --git a/src/main/java/com/cedarsoftware/util/convert/MapConversions.java b/src/main/java/com/cedarsoftware/util/convert/MapConversions.java
index 7fce43c89..8a7e75ca3 100644
--- a/src/main/java/com/cedarsoftware/util/convert/MapConversions.java
+++ b/src/main/java/com/cedarsoftware/util/convert/MapConversions.java
@@ -182,7 +182,7 @@ static java.sql.Date toSqlDate(Object from, Converter converter) {
if (epochTime == null) {
return fromMap(from, converter, java.sql.Date.class, new String[]{EPOCH_MILLIS}, new String[]{TIME, ZONE + OPTIONAL}, new String[]{DATE, TIME, ZONE + OPTIONAL});
}
- return new java.sql.Date(epochTime.getKey());
+ return new java.sql.Date(epochTime.getKey() + epochTime.getValue() / 1_000_000);
}
static Date toDate(Object from, Converter converter) {
@@ -190,7 +190,7 @@ static Date toDate(Object from, Converter converter) {
if (epochTime == null) {
return fromMap(from, converter, Date.class, new String[]{EPOCH_MILLIS}, new String[]{TIME, ZONE + OPTIONAL}, new String[]{DATE, TIME, ZONE + OPTIONAL});
}
- return new Date(epochTime.getKey());
+ return new Date(epochTime.getKey() + epochTime.getValue() / 1_000_000);
}
/**
@@ -220,9 +220,7 @@ static Timestamp toTimestamp(Object from, Converter converter) {
}
Timestamp timestamp = new Timestamp(epochTime.getKey());
- if (timestamp.getTime() % 1000 == 0) { // Add nanoseconds *if* Timestamp time was only to second resolution
- timestamp.setNanos(epochTime.getValue());
- }
+ timestamp.setNanos(epochTime.getValue());
return timestamp;
}
diff --git a/src/test/java/com/cedarsoftware/util/convert/ConverterEverythingTest.java b/src/test/java/com/cedarsoftware/util/convert/ConverterEverythingTest.java
index 94e90732a..520f929da 100644
--- a/src/test/java/com/cedarsoftware/util/convert/ConverterEverythingTest.java
+++ b/src/test/java/com/cedarsoftware/util/convert/ConverterEverythingTest.java
@@ -1748,6 +1748,7 @@ private static void loadSqlDateTests() {
{zdt("1970-01-01T00:00:00.999Z"), new java.sql.Date(999), true},
});
TEST_DB.put(pair(Map.class, java.sql.Date.class), new Object[][] {
+ { mapOf(TIME, 1703043551033L), new java.sql.Date(1703043551033L), false},
{ mapOf(EPOCH_MILLIS, -1L, DATE, "1970-01-01", TIME, "08:59:59.999", ZONE, TOKYO_Z.toString()), new java.sql.Date(-1L), true},
{ mapOf(EPOCH_MILLIS, 0L, DATE, "1970-01-01", TIME, "09:00", ZONE, TOKYO_Z.toString()), new java.sql.Date(0L), true},
{ mapOf(EPOCH_MILLIS, 1L, DATE, "1970-01-01", TIME, "09:00:00.001", ZONE, TOKYO_Z.toString()), new java.sql.Date(1L), true},