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

org.elasticsearch.painless.lookup.PainlessLookupUtility Maven / Gradle / Ivy

The newest version!
/*
 * Licensed to Elasticsearch under one or more contributor
 * license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright
 * ownership. Elasticsearch licenses this file to you under
 * the Apache License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package org.elasticsearch.painless.lookup;

import org.elasticsearch.painless.spi.annotation.InjectConstantAnnotation;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * PainlessLookupUtility contains methods shared by {@link PainlessLookupBuilder}, {@link PainlessLookup}, and other classes within
 * Painless for conversion between type names and types along with some other various utility methods.
 *
 * The following terminology is used for variable names throughout the lookup package:
 *
 * A class is a set of methods and fields under a specific class name. A type is either a class or an array under a specific type name.
 * Note the distinction between class versus type is class means that no array classes will be be represented whereas type allows array
 * classes to be represented. The set of available classes will always be a subset of the available types.
 *
 * Under ambiguous circumstances most variable names are prefixed with asm, java, or painless. If the variable value is the same for asm,
 * java, and painless, no prefix is used. Target is used as a prefix to represent if a constructor, method, or field is being
 * called/accessed on that specific class.  Parameter is often a postfix used to represent if a type is used as a parameter to a
 * constructor, method, or field.
 *
 * 
    *
  • - javaClassName (String) - the fully qualified java class name where '$' tokens represent inner classes excluding * def and array types
  • * *
  • - javaClass (Class) - a java class excluding def and array types
  • * *
  • - javaType (Class) - a java class excluding def and including array types
  • * *
  • - importedClassName (String) - the imported painless class name where the java canonical class name is used without * the package qualifier * *
  • - canonicalClassName (String) - the fully qualified painless class name equivalent to the fully * qualified java canonical class name or imported painless class name for a class * including def and excluding array types where '.' tokens represent inner classes
  • * *
  • - canonicalTypeName (String) - the fully qualified painless type name equivalent to the fully * qualified java canonical type name or imported painless type name for a type * including def where '.' tokens represent inner classes and each set of '[]' tokens * at the end of the type name represent a single dimension for an array type
  • * *
  • - class/clazz (Class) - a painless class represented by a java class including def and excluding array * types
  • * *
  • - type (Class) - a painless type represented by a java class including def and array types
  • * *
  • - painlessClass (PainlessClass) - a painless class object
  • * *
  • - painlessMethod (PainlessMethod) - a painless method object
  • * *
  • - painlessField (PainlessField) - a painless field object
  • *
*/ public final class PainlessLookupUtility { /** * The name for an anonymous class. */ public static final String ANONYMOUS_CLASS_NAME = "$anonymous"; /** * The def type name as specified in the source for a script. */ public static final String DEF_CLASS_NAME = "def"; /** * The method name for all constructors. */ public static final String CONSTRUCTOR_NAME = ""; /** * Converts a canonical type name to a type based on the terminology specified as part of the documentation for * {@link PainlessLookupUtility}. Since canonical class names are a subset of canonical type names, this method will * safely convert a canonical class name to a class as well. */ public static Class canonicalTypeNameToType(String canonicalTypeName, Map> canonicalClassNamesToClasses) { Objects.requireNonNull(canonicalTypeName); Objects.requireNonNull(canonicalClassNamesToClasses); Class type = DEF_CLASS_NAME.equals(canonicalTypeName) ? def.class : canonicalClassNamesToClasses.get(canonicalTypeName); if (type != null) { return type; } int arrayDimensions = 0; int arrayIndex = canonicalTypeName.indexOf('['); if (arrayIndex != -1) { int typeNameLength = canonicalTypeName.length(); while (arrayIndex < typeNameLength) { if (canonicalTypeName.charAt(arrayIndex) == '[' && ++arrayIndex < typeNameLength && canonicalTypeName.charAt(arrayIndex++) == ']') { ++arrayDimensions; } else { return null; } } canonicalTypeName = canonicalTypeName.substring(0, canonicalTypeName.indexOf('[')); type = DEF_CLASS_NAME.equals(canonicalTypeName) ? def.class : canonicalClassNamesToClasses.get(canonicalTypeName); if (type != null) { char arrayBraces[] = new char[arrayDimensions]; Arrays.fill(arrayBraces, '['); String javaTypeName = new String(arrayBraces); if (type == boolean.class) { javaTypeName += "Z"; } else if (type == byte.class) { javaTypeName += "B"; } else if (type == short.class) { javaTypeName += "S"; } else if (type == char.class) { javaTypeName += "C"; } else if (type == int.class) { javaTypeName += "I"; } else if (type == long.class) { javaTypeName += "J"; } else if (type == float.class) { javaTypeName += "F"; } else if (type == double.class) { javaTypeName += "D"; } else { javaTypeName += "L" + type.getName() + ";"; } try { return Class.forName(javaTypeName); } catch (ClassNotFoundException cnfe) { throw new IllegalStateException("internal error", cnfe); } } } return null; } /** * Converts a type to a canonical type name based on the terminology specified as part of the documentation for * {@link PainlessLookupUtility}. Since classes are a subset of types, this method will safely convert a class * to a canonical class name as well. */ public static String typeToCanonicalTypeName(Class type) { Objects.requireNonNull(type); String canonicalTypeName = type.getCanonicalName(); if (canonicalTypeName == null) { canonicalTypeName = ANONYMOUS_CLASS_NAME; } else if (canonicalTypeName.startsWith(def.class.getCanonicalName())) { canonicalTypeName = canonicalTypeName.replace(def.class.getCanonicalName(), DEF_CLASS_NAME); } return canonicalTypeName; } /** * Converts a list of types to a list of canonical type names as a string based on the terminology specified as part of the * documentation for {@link PainlessLookupUtility}. Since classes are a subset of types, this method will safely convert a list * of classes or a mixed list of classes and types to a list of canonical type names as a string as well. */ public static String typesToCanonicalTypeNames(List> types) { StringBuilder typesStringBuilder = new StringBuilder("["); int anyTypesSize = types.size(); int anyTypesIndex = 0; for (Class painlessType : types) { String canonicalTypeName = typeToCanonicalTypeName(painlessType); typesStringBuilder.append(canonicalTypeName); if (++anyTypesIndex < anyTypesSize) { typesStringBuilder.append(","); } } typesStringBuilder.append("]"); return typesStringBuilder.toString(); } /** * Converts a java type to a type based on the terminology specified as part of {@link PainlessLookupUtility} where if a type is an * object class or object array, the returned type will be the equivalent def class or def array. Otherwise, this behaves as an * identity function. */ public static Class javaTypeToType(Class javaType) { Objects.requireNonNull(javaType); if (javaType.isArray()) { Class javaTypeComponent = javaType.getComponentType(); int arrayDimensions = 1; while (javaTypeComponent.isArray()) { javaTypeComponent = javaTypeComponent.getComponentType(); ++arrayDimensions; } if (javaTypeComponent == Object.class) { char[] arrayBraces = new char[arrayDimensions]; Arrays.fill(arrayBraces, '['); try { return Class.forName(new String(arrayBraces) + "L" + def.class.getName() + ";"); } catch (ClassNotFoundException cnfe) { throw new IllegalStateException("internal error", cnfe); } } } else if (javaType == Object.class) { return def.class; } return javaType; } /** * Converts a type to a java type based on the terminology specified as part of {@link PainlessLookupUtility} where if a type is a * def class or def array, the returned type will be the equivalent object class or object array. Otherwise, this behaves as an * identity function. */ public static Class typeToJavaType(Class type) { Objects.requireNonNull(type); if (type.isArray()) { Class typeComponent = type.getComponentType(); int arrayDimensions = 1; while (typeComponent.isArray()) { typeComponent = typeComponent.getComponentType(); ++arrayDimensions; } if (typeComponent == def.class) { char[] arrayBraces = new char[arrayDimensions]; Arrays.fill(arrayBraces, '['); try { return Class.forName(new String(arrayBraces) + "L" + Object.class.getName() + ";"); } catch (ClassNotFoundException cnfe) { throw new IllegalStateException("internal error", cnfe); } } } else if (type == def.class) { return Object.class; } return type; } /** * Converts a type to its boxed type equivalent if one exists based on the terminology specified as part of * {@link PainlessLookupUtility}. Otherwise, this behaves as an identity function. */ public static Class typeToBoxedType(Class type) { if (type == boolean.class) { return Boolean.class; } else if (type == byte.class) { return Byte.class; } else if (type == short.class) { return Short.class; } else if (type == char.class) { return Character.class; } else if (type == int.class) { return Integer.class; } else if (type == long.class) { return Long.class; } else if (type == float.class) { return Float.class; } else if (type == double.class) { return Double.class; } return type; } /** * Converts a type to its unboxed type equivalent if one exists based on the terminology specified as part of * {@link PainlessLookupUtility}. Otherwise, this behaves as an identity function. */ public static Class typeToUnboxedType(Class type) { if (type == Boolean.class) { return boolean.class; } else if (type == Byte.class) { return byte.class; } else if (type == Short.class) { return short.class; } else if (type == Character.class) { return char.class; } else if (type == Integer.class) { return int.class; } else if (type == Long.class) { return long.class; } else if (type == Float.class) { return float.class; } else if (type == Double.class) { return double.class; } return type; } /** * Checks if a type based on the terminology specified as part of {@link PainlessLookupUtility} is available as a constant type * where {@code true} is returned if the type is a constant type and {@code false} otherwise. */ public static boolean isConstantType(Class type) { return type == boolean.class || type == byte.class || type == short.class || type == char.class || type == int.class || type == long.class || type == float.class || type == double.class || type == String.class; } /** * Constructs a painless constructor key used to lookup painless constructors from a painless class. */ public static String buildPainlessConstructorKey(int constructorArity) { return CONSTRUCTOR_NAME + "/" + constructorArity; } /** * Constructs a painless method key used to lookup painless methods from a painless class. */ public static String buildPainlessMethodKey(String methodName, int methodArity) { return methodName + "/" + methodArity; } /** * Constructs a painless field key used to lookup painless fields from a painless class. */ public static String buildPainlessFieldKey(String fieldName) { return fieldName; } /** * Constructs an array of injectable constants for a specific {@link PainlessMethod} * derived from an {@link org.elasticsearch.painless.spi.annotation.InjectConstantAnnotation}. */ public static Object[] buildInjections(PainlessMethod painlessMethod, Map constants) { if (painlessMethod.annotations.containsKey(InjectConstantAnnotation.class) == false) { return new Object[0]; } List names = ((InjectConstantAnnotation)painlessMethod.annotations.get(InjectConstantAnnotation.class)).injects; Object[] injections = new Object[names.size()]; for (int i = 0; i < names.size(); i++) { String name = names.get(i); Object constant = constants.get(name); if (constant == null) { throw new IllegalStateException("constant [" + name + "] not found for injection into method " + "[" + buildPainlessMethodKey(painlessMethod.javaMethod.getName(), painlessMethod.typeParameters.size()) + "]"); } injections[i] = constant; } return injections; } private PainlessLookupUtility() { } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy