All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.deephaven.engine.table.impl.select.QueryScopeParamTypeUtil Maven / Gradle / Ivy

/**
 * Copyright (c) 2016-2022 Deephaven Data Labs and Patent Pending
 */
package io.deephaven.engine.table.impl.select;

import groovy.lang.Closure;
import io.deephaven.engine.context.QueryCompiler;
import io.deephaven.util.type.TypeUtils;

import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Utilities for expanding QueryScope parameter classes.
 */
public class QueryScopeParamTypeUtil {

    public static Class classFromType(final Type declaredType) {
        if (declaredType instanceof Class) {
            return (Class) declaredType;
        }
        if (declaredType instanceof ParameterizedType) {
            return (Class) ((ParameterizedType) declaredType).getRawType();
        }
        return null;
    }

    /**
     * Get a map from binary name to declared type for the dynamic classes referenced by an array of param classes.
     *
     * @param params The parameters to operate on
     * @return The result map
     */
    public static Map> expandParameterClasses(final List> params) {
        final Map> found = new HashMap<>();
        params.forEach(cls -> visitParameterClass(found, cls));
        return found;
    }

    private static void visitParameterClass(final Map> found, Class cls) {
        while (cls.isArray()) {
            cls = classFromType(cls.getComponentType());
        }

        final String name = cls.getName();
        if (!name.startsWith(QueryCompiler.DYNAMIC_GROOVY_CLASS_PREFIX)) {
            return;
        }

        final Class seen = found.get(name);
        if (seen != null) {
            if (seen != cls) {
                throw new UnsupportedOperationException(
                        "Parameter list may not include multiple versions of the same class: "
                                + name + ". Was the class redefined in your shell?");
            }
            // we don't need to revisit this class
            return;
        }
        found.put(name, cls);

        // Visit Methods
        Arrays.stream(cls.getMethods()).forEach(m -> {
            visitParameterClass(found, m.getReturnType());
            Arrays.stream(m.getParameterTypes()).forEach(t -> visitParameterClass(found, t));
        });
        // Visit Fields
        Arrays.stream(cls.getFields()).forEach(f -> visitParameterClass(found, f.getType()));
        final Class componentType = cls.getComponentType();
        if (componentType != null) {
            visitParameterClass(found, componentType);
        }
    }

    public static  Class getDeclaredClass(final T value) {
        Type declaredType = getDeclaredType(value);
        Class cls = classFromType(declaredType);

        if (cls == null) {
            throw new IllegalStateException("Unexpected declared type of type '"
                    + declaredType.getClass().getCanonicalName() + "'");
        }

        return cls;
    }

    public static  Type getDeclaredType(final T value) {
        // in newer versions of groovy, our closures will be subtypes that evade the logic in getDeclaredType
        // (they will return a null Class#getCanonicalName b/c they are dynamic classes).
        final Class type;
        if (value == null) {
            type = Object.class;
        } else if (value instanceof Enum) {
            type = ((Enum) value).getDeclaringClass();
        } else if (value instanceof Closure) {
            type = Closure.class;
        } else {
            type = value.getClass();
        }
        return getDeclaredType(type);
    }

    private static Type getDeclaredType(final Class origType) {
        Class type = origType;
        while (type != Object.class) {
            if (Modifier.isPublic(type.getModifiers()) && !type.isAnonymousClass()) {
                break;
            }

            Type[] interfaces = type.getGenericInterfaces();
            for (Type ityp : interfaces) {
                Class iface = null;
                if (ityp instanceof Class) {
                    iface = (Class) ityp;
                } else if (ityp instanceof ParameterizedType) {
                    ParameterizedType pt = (ParameterizedType) ityp;
                    Type rawType = pt.getRawType();
                    if (rawType instanceof Class) {
                        iface = (Class) rawType;
                    }
                }

                if (iface != null && Modifier.isPublic(iface.getModifiers()) && iface.getMethods().length > 0) {
                    return ityp;
                }
            }

            type = type.getSuperclass();
        }

        return type;
    }

    public static  String getDeclaredTypeName(T value) {
        return getDeclaredClass(value).getCanonicalName();
    }

    public static  String getPrimitiveTypeNameIfAvailable(final T value) {
        if (value == null) {
            return getDeclaredTypeName(null);
        }
        Class type = getDeclaredClass(value);
        if (TypeUtils.isBoxedType(type)) {
            return TypeUtils.getUnboxedType(type).getCanonicalName();
        }
        return getDeclaredTypeName(value);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy