package de.take_weiland.mods.commons.util; import com.google.common.base.Predicate; import com.google.common.collect.AbstractIterator; import com.google.common.collect.Iterators; import de.take_weiland.mods.commons.Unsafe; import de.take_weiland.mods.commons.internal.SevenCommons; import de.take_weiland.mods.commons.reflect.Invoke; import de.take_weiland.mods.commons.reflect.SCReflection; import org.jetbrains.annotations.NotNull; import sun.misc.JavaLangAccess; import sun.misc.SharedSecrets; import javax.annotation.Nullable; import javax.annotation.ParametersAreNonnullByDefault; import java.lang.invoke.MethodHandle; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.*; import static java.lang.invoke.MethodHandles.publicLookup; /** * <p>Various utility methods.</p> */ @ParametersAreNonnullByDefault public final class JavaUtils { /** * <p>Returns a String representation of the given Object. For arrays uses the appropriate {@code toString} method * {@link java.util.Arrays}, otherwise uses {@link java.util.Objects#toString(Object)}.</p> * @param o the Object * @return a String representation */ public static String toString(@Nullable Object o) { if (o instanceof boolean[]) { return Arrays.toString((boolean[]) o); } else if (o instanceof byte[]) { return Arrays.toString((byte[]) o); } else if (o instanceof short[]) { return Arrays.toString((short[]) o); } else if (o instanceof char[]) { return Arrays.toString((char[]) o); } else if (o instanceof int[]) { return Arrays.toString((int[]) o); } else if (o instanceof long[]) { return Arrays.toString((long[]) o); } else if (o instanceof float[]) { return Arrays.toString((float[]) o); } else if (o instanceof double[]) { return Arrays.toString((double[]) o); } else if (o instanceof Object[]) { return Arrays.deepToString((Object[]) o); } else { return Objects.toString(o); } } /** * <p>Return a proper hashCode for the given object. If the object is an array, the appropriate hashCode method in * the {@link Arrays} class will be called.</p> * @param o the object * @return a hash code for the object */ public static int hashCode(Object o) { if (o instanceof boolean[]) { return Arrays.hashCode((boolean[]) o); } else if (o instanceof byte[]) { return Arrays.hashCode((byte[]) o); } else if (o instanceof short[]) { return Arrays.hashCode((short[]) o); } else if (o instanceof char[]) { return Arrays.hashCode((char[]) o); } else if (o instanceof int[]) { return Arrays.hashCode((int[]) o); } else if (o instanceof long[]) { return Arrays.hashCode((long[]) o); } else if (o instanceof float[]) { return Arrays.hashCode((float[]) o); } else if (o instanceof double[]) { return Arrays.hashCode((double[]) o); } else if (o instanceof Object[]) { return Arrays.deepHashCode((Object[]) o); } else { return Objects.hashCode(o); } } /** * <p>Returns the value at the given index in the arrayor null if the index is out of bounds.</p> * @param array the array * @param index the index * @return the element at index {@code index} or null */ public static <T> T get(T[] array, int index) { return index >= 0 && index < array.length ? array[index] : null; } /** * <p>Returns the value at the given index in the array or {@code defaultValue} if the index is out of bounds.</p> * @param array the array * @param index the index * @param defaultValue the default value * @return the element at index {@code index} or the default value */ public static <T, D extends T, R extends T> T get(R[] array, int index, D defaultValue) { return index >= 0 && index < array.length ? array[index] : defaultValue; } /** * <p>Returns the value at the given index in the List or null if the index is out of bounds.</p> * @param list the List * @param index the index * @return the element at index {@code index} or null */ public static <T> T get(List<T> list, int index) { return index >= 0 && index < list.size() ? list.get(index) : null; } /** * <p>Returns the value at the given index in the List or {@code defaultValue} if the index is out of bounds.</p> * @param list the List * @param index the index * @param defaultValue the default value * @return the element at index {@code index} or the default value */ public static <T, D extends T, V extends T> T get(List<V> list, int index, D defaultValue) { return index >= 0 && index < list.size() ? list.get(index) : defaultValue; } /** * <p>Remove all elements from the given Iterable.</p> * @param iterable the Iterable */ public static void clear(Iterable<?> iterable) { if (iterable instanceof Collection) { ((Collection<?>) iterable).clear(); } else { clear(iterable.iterator()); } } /** * <p>Remove all elements from the given Iterator.</p> * @param it the Iterator */ public static void clear(Iterator<?> it) { while (it.hasNext()) { it.next(); it.remove(); } } public static <T extends Cloneable> T clone(T t) { try { //noinspection unchecked return (T) CloneableAcc.instance.clone(t); } catch (CloneNotSupportedException e) { throw new IllegalArgumentException("Tried to clone a non-cloneable object " + t, e); } } private interface CloneableAcc { CloneableAcc instance = SCReflection.createAccessor(CloneableAcc.class); @Invoke(method = "clone") Object clone(Object t) throws CloneNotSupportedException; } /** * <p>Get an {@code Iterable} that iterates the class hierarchy of the given class in subclass to superclass order.</p> * <p>If {@code interfaceBehavior} is set to {@code INCLUDE}, each class in the hierarchy will be followed by the * interfaces implemented by that class, unless they have already been returned by the Iterator.</p> * @param clazz the class * @param interfaceBehavior whether or not to include interfaces * @return an Iterable */ public static Iterable<Class<?>> hierarchy(final Class<?> clazz, final Interfaces interfaceBehavior) { return new Iterable<Class<?>>() { @NotNull @Override public Iterator<Class<?>> iterator() { Iterator<Class<?>> hierarchy = new HierarchyIterator(clazz); return interfaceBehavior == Interfaces.IGNORE ? hierarchy : new InterfaceIterator(hierarchy); } }; } /** * <p>Behavior for {@link #hierarchy(Class, de.take_weiland.mods.commons.util.JavaUtils.Interfaces)}.</p> */ public enum Interfaces { /** * <p>Ignore interfaces.</p> */ IGNORE, /** * <p>Include interfaces.</p> */ INCLUDE } static final class InterfaceIterator extends AbstractIterator<Class<?>> { private final Iterator<Class<?>> hierarchy; private final Predicate<Class<?>> ifaceFilter; private Iterator<Class<?>> currentIfaces; InterfaceIterator(Iterator<Class<?>> hierarchy) { this.hierarchy = hierarchy; final Set<Class<?>> seenInterfaces = new HashSet<>(); ifaceFilter = new Predicate<Class<?>>() { @Override public boolean apply(@Nullable Class<?> input) { assert input != null; return seenInterfaces.add(input); } }; } @Override protected Class<?> computeNext() { if (currentIfaces != null && currentIfaces.hasNext()) { return currentIfaces.next(); } else if (!hierarchy.hasNext()) { return endOfData(); } else { Class<?> next = hierarchy.next(); currentIfaces = Iterators.filter(Iterators.forArray(next.getInterfaces()), ifaceFilter); return next; } } } static final class HierarchyIterator extends AbstractIterator<Class<?>> { private Class<?> current; HierarchyIterator(Class<?> root) { this.current = root; } @Override protected Class<?> computeNext() { Class<?> next = current; if (next == null) { return endOfData(); } else { current = next.getSuperclass(); return next; } } } /** * <p>Throw the given {@code Throwable} as if it as an unchecked Exception. This method always throws, the return type * is just to satisfy the compiler.</p> * <p>Usage:<pre><code>throw JavaUtils.throwUnchecked(new Throwable()); * </code></pre></p> * @param t the Throwable * @return nothing, this method always throws */ @Unsafe public static RuntimeException throwUnchecked(Throwable t) { // return is never reached return JavaUtils.<RuntimeException>throwUnchecked0(t); } @SuppressWarnings("unchecked") private static <T extends Throwable> RuntimeException throwUnchecked0(Throwable t) throws T { // dirty hack. the cast doesn't exist in bytecode, so it always succeeds throw (T) t; } /** * <p>Get all constants defined in the given enum class. This is equivalent to {@code E.values()} except that the array * returned by this method is not cloned and as thus shared across the entire application. <strong>Therefor the * array must not be modified!</strong></p> * @param clazz the enum class * @return all defined constants */ @Unsafe public static <E extends Enum<E>> E[] getEnumConstantsShared(Class<E> clazz) { return EnumValuesGetter.instance.getEnumValues(clazz); } abstract static class EnumValuesGetter { static final EnumValuesGetter instance; static { EnumValuesGetter e; try { Class.forName("sun.misc.SharedSecrets"); e = (EnumValuesGetter) Class.forName("de.take_weiland.mods.commons.util.JavaUtils$EnumGetterShared").newInstance(); } catch (Throwable t) { SevenCommons.LOGGER.info("sun.misc.SharedSecrets not found. Falling back to default EnumGetter"); e = new EnumGetterCloned(); } instance = e; } abstract <T extends Enum<T>> T[] getEnumValues(Class<T> clazz); } final static class EnumGetterCloned extends EnumValuesGetter { @Override <T extends Enum<T>> T[] getEnumValues(Class<T> clazz) { return clazz.getEnumConstants(); } } @SuppressWarnings("unused") final static class EnumGetterShared extends EnumValuesGetter { private static final JavaLangAccess langAcc = SharedSecrets.getJavaLangAccess(); @Override <T extends Enum<T>> T[] getEnumValues(Class<T> clazz) { return langAcc.getEnumConstantsShared(clazz); } } /** * <p>Get the enum constant with the given ordinal value in the given enum class.</p> * <p>This method is equivalent to {@code E.values()[ordinal]}, but is potentially more efficient.</p> * @param clazz the enum class * @param ordinal the ordinal value * @return the enum constant */ public static <T extends Enum<T>> T byOrdinal(Class<T> clazz, int ordinal) { return getEnumConstantsShared(clazz)[ordinal]; } /** * <p>Get the type of enum values of the given EnumSet.</p> * @param enumSet the EnumSet * @return the type of enum values */ public static <E extends Enum<E>> Class<E> getType(EnumSet<E> enumSet) { try { //noinspection unchecked return (Class<E>) EnumSetAcc.getter.invokeExact(enumSet); } catch (Throwable t) { throw throwUnchecked(t); } } @SuppressWarnings("UtilityClassWithoutPrivateConstructor") static final class EnumSetAcc { static final MethodHandle getter; static { Field result = null; for (Field field : EnumSet.class.getDeclaredFields()) { if (!Modifier.isStatic(field.getModifiers()) && field.getType() == Class.class) { result = field; break; } } if (result == null) { throw new RuntimeException("Failed to find type field in EnumSet!"); } result.setAccessible(true); try { getter = publicLookup().unreflectGetter(result); } catch (IllegalAccessException e) { throw new RuntimeException(e); } } } /** * <p>Check if {@code sun.misc.Unsafe} is available on this JVM.</p> * * @return true if sun.misc.Unsafe was found */ @Unsafe public static boolean hasUnsafe() { initUnsafe(); return unsafe != null; } /** * <p>Return the {@code sun.misc.Unsafe} instance if it is available, {@code null} otherwise.</p> * @return the {@code sun.misc.Unsafe} instance or null */ @SuppressWarnings("unchecked") @Unsafe public static <T> T getUnsafe() { initUnsafe(); return (T) unsafe; } private static Object unsafe; private static volatile boolean unsafeChecked; private static void initUnsafe() { if (unsafeChecked) { return; } synchronized (JavaUtils.class) { if (unsafeChecked) return; unsafeChecked = true; try { Class<?> unsafeClass = Class.forName("sun.misc.Unsafe"); for (Field field : unsafeClass.getDeclaredFields()) { if (!Modifier.isStatic(field.getModifiers())) { continue; } field.setAccessible(true); Object value = field.get(null); if (unsafeClass.isInstance(value)) { unsafe = value; break; } } } catch (Exception e) { // no unsafe } } } private JavaUtils() { } }