/*
 * $Id$
 *
 * SARL is an general-purpose agent programming language.
 * More details on http://www.sarl.io
 *
 * Copyright (C) 2014-2026 SARL.io, the original authors and main authors.
 *
 * Licensed 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 io.sarl.lang.util;

import static io.sarl.lang.core.util.SarlUtils.HIDDEN_MEMBER_CHARACTER;
import static io.sarl.lang.core.util.SarlUtils.isHiddenMember;

import java.lang.annotation.Annotation;
import java.lang.reflect.Modifier;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.function.BiConsumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.xtend.core.xtend.XtendFunction;
import org.eclipse.xtend.core.xtend.XtendParameter;
import org.eclipse.xtend.core.xtend.XtendTypeDeclaration;
import org.eclipse.xtext.EcoreUtil2;
import org.eclipse.xtext.common.types.JvmConstraintOwner;
import org.eclipse.xtext.common.types.JvmConstructor;
import org.eclipse.xtext.common.types.JvmDeclaredType;
import org.eclipse.xtext.common.types.JvmField;
import org.eclipse.xtext.common.types.JvmFormalParameter;
import org.eclipse.xtext.common.types.JvmGenericType;
import org.eclipse.xtext.common.types.JvmIdentifiableElement;
import org.eclipse.xtext.common.types.JvmLowerBound;
import org.eclipse.xtext.common.types.JvmMember;
import org.eclipse.xtext.common.types.JvmOperation;
import org.eclipse.xtext.common.types.JvmParameterizedTypeReference;
import org.eclipse.xtext.common.types.JvmType;
import org.eclipse.xtext.common.types.JvmTypeConstraint;
import org.eclipse.xtext.common.types.JvmTypeParameter;
import org.eclipse.xtext.common.types.JvmTypeParameterDeclarator;
import org.eclipse.xtext.common.types.JvmTypeReference;
import org.eclipse.xtext.common.types.JvmUpperBound;
import org.eclipse.xtext.common.types.JvmVisibility;
import org.eclipse.xtext.common.types.JvmWildcardTypeReference;
import org.eclipse.xtext.common.types.TypesFactory;
import org.eclipse.xtext.common.types.util.TypeReferences;
import org.eclipse.xtext.naming.QualifiedName;
import org.eclipse.xtext.nodemodel.util.NodeModelUtils;
import org.eclipse.xtext.serializer.ISerializer;
import org.eclipse.xtext.util.EmfFormatter;
import org.eclipse.xtext.util.XtextVersion;
import org.eclipse.xtext.xbase.XAbstractFeatureCall;
import org.eclipse.xtext.xbase.XExpression;
import org.eclipse.xtext.xbase.XFeatureCall;
import org.eclipse.xtext.xbase.XMemberFeatureCall;
import org.eclipse.xtext.xbase.XVariableDeclaration;
import org.eclipse.xtext.xbase.annotations.xAnnotations.XAnnotation;
import org.eclipse.xtext.xbase.compiler.ImportManager;
import org.eclipse.xtext.xbase.jvmmodel.JvmTypeReferenceBuilder;
import org.eclipse.xtext.xbase.jvmmodel.JvmTypesBuilder;
import org.eclipse.xtext.xbase.lib.CollectionLiterals;
import org.eclipse.xtext.xbase.lib.Functions.Function1;
import org.eclipse.xtext.xbase.lib.Pure;
import org.eclipse.xtext.xbase.typesystem.conformance.TypeConformanceComputationArgument;
import org.eclipse.xtext.xbase.typesystem.references.LightweightTypeReference;
import org.eclipse.xtext.xbase.typesystem.references.LightweightTypeReferenceFactory;
import org.eclipse.xtext.xbase.typesystem.references.StandardTypeReferenceOwner;
import org.eclipse.xtext.xbase.typesystem.util.CommonTypeComputationServices;
import org.eclipse.xtext.xtype.XFunctionTypeRef;
import org.eclipse.xtext.xtype.XtypeFactory;
import org.osgi.framework.Version;

import com.google.common.base.Objects;
import com.google.common.base.Strings;
import com.google.common.collect.Iterables;

import io.sarl.lang.core.SARLVersion;
import io.sarl.lang.core.annotation.EarlyExit;
import io.sarl.lang.core.util.OutParameter;
import io.sarl.lang.sarl.SarlAction;
import io.sarl.lang.sarl.SarlFormalParameter;
import io.sarl.lang.sarl.actionprototype.ActionParameterTypes;
import io.sarl.lang.sarl.actionprototype.ActionPrototype;
import io.sarl.lang.sarl.actionprototype.IActionPrototypeProvider;
import io.sarl.lang.services.SARLGrammarKeywordAccess;

/**
 * Utilities functions on JvmElements.
 *
 * @author $Author: sgalland$
 * @version $FullVersion$
 * @mavengroupid $GroupId$
 * @mavenartifactid $ArtifactId$
 */
public final class Utils {

	/** Name of a static initializer.
	 *
	 * @since 0.6
	 */
	private static final String STATIC_CONSTRUCTOR_NAME = "static$new"; //$NON-NLS-1$

	private static final String PREFIX_DEFAULT_VALUE_FUNCTION = HIDDEN_MEMBER_CHARACTER + "DEFAULT_VALUE" //$NON-NLS-1$
			+ HIDDEN_MEMBER_CHARACTER;

	private static final String PREFIX_CAPACITY_IMPLEMENTATION = HIDDEN_MEMBER_CHARACTER + "CAPACITY_USE" //$NON-NLS-1$
			+ HIDDEN_MEMBER_CHARACTER;

	private static final String POSTFIX_CAPACITY_IMPLEMENTATION_CALLER = HIDDEN_MEMBER_CHARACTER + "CALLER"; //$NON-NLS-1$

	private static final String PREFIX_GUARD_EVALUATOR = HIDDEN_MEMBER_CHARACTER + "guardEvaluator" //$NON-NLS-1$
			+ HIDDEN_MEMBER_CHARACTER;

	private static final String PREFIX_GUARD = HIDDEN_MEMBER_CHARACTER + "behaviorUnitGuard" //$NON-NLS-1$
			+ HIDDEN_MEMBER_CHARACTER;

	private static final String PREFIX_EVENT_HANDLER = HIDDEN_MEMBER_CHARACTER + "behaviorUnit" //$NON-NLS-1$
			+ HIDDEN_MEMBER_CHARACTER;

	private static final String PREFIX_LOCAL_VARIABLE = "___SARLlocal_"; //$NON-NLS-1$

	private static final String HIDDEN_MEMBER_REPLACEMENT_CHARACTER = "_"; //$NON-NLS-1$

	private static final String SARL_PACKAGE_PREFIX;

	private static final String SARL_VERSION_FIELD_NAME_STR = "SPECIFICATION_RELEASE_VERSION_STRING"; //$NON-NLS-1$

	private static boolean checkSarlVersionClass = true;

	private static final Pattern IMPLICIT_LAMBDA_PARAMETER_PATTERN = Pattern.compile("^\\$[0-9]+$"); //$NON-NLS-1$

	/** @since 0.15
	 */
	private static final String JAVA_MAIN_FUNCTION_NAME = "main"; //$NON-NLS-1$

	static {
		final var name = new StringBuilder();
		final var components = EarlyExit.class.getPackage().getName().split("\\."); //$NON-NLS-1$
		final var len = Math.min(3, components.length);
		for (var i = 0; i < len; ++i) {
			name.append(components[i]);
			name.append("."); //$NON-NLS-1$
		}
		SARL_PACKAGE_PREFIX = name.toString();
	}

	private Utils() {
		//
	}

	/** Replies if the given name is for a function that could be considered as the main (Java) entry point of the program.
	 *
	 * @param name the name of the function to test.
	 * @return {@code true} if the given name is for a main Java function.
	 * @since 0.15
	 */
	public static boolean isNameForJavaMainFunction(String name) {
		return Objects.equal(JAVA_MAIN_FUNCTION_NAME, name);
	}

	/** Replies the name for a function that could be considered as the main (Java) entry point of the program.
	 *
	 * @return the function simple name.
	 * @since 0.15
	 */
	public static String getNameForJavaMainFunction() {
		return JAVA_MAIN_FUNCTION_NAME;
	}

	/** Replies if the given function is a main function.
	 *
	 * @param function the function.
	 * @param services the services for comparing the types.
	 * @return {@code true} if the function is a main function.
	 * @since 0.15
	 */
	public static boolean isMainFunctionDeclaration(XtendFunction function, CommonTypeComputationServices services) {
		if (isMainFunctionDeclarationExcludingReturnType(function, services)) {
			var retType = toLightweightTypeReference(function.getReturnType(), services);
			if (retType == null || retType.isPrimitiveVoid()) {
				return true;
			}
		}
		return false;
	}

	/** Replies if the given function is a main function without considering the return type.
	 *
	 * @param function the function.
	 * @param services the services for comparing the types.
	 * @return {@code true} if the function is a main function.
	 * @since 0.15
	 */
	public static boolean isMainFunctionDeclarationExcludingReturnType(XtendFunction function, CommonTypeComputationServices services) {
		if (isNameForJavaMainFunction(function.getName()) && function.isStatic() && !function.isDispatch()) {
			if (function.getParameters().size() == 1) {
				final var providedArg = function.getParameters().get(0);
				var argType = toLightweightTypeReference(providedArg.getParameterType(), services);
				final var isVarArgsFromSource = isVarArg(function.getParameters());
				if (!isVarArgsFromSource && argType.isArray()) {
					argType = argType.getComponentType();
				}
				return argType.isType(String.class);
			}
		}
		return false;
	}

	/** Replies if the given name is the hidden name for a parameter's default value.
	 *
	 * @param name the name to test.
	 * @return {@code true} if the given name is the one for a default value.
	 * @since 0.12
	 */
	@Pure
	public static boolean isDynamicDefaultValueFunctionName(String name) {
		return name != null && name.startsWith(PREFIX_DEFAULT_VALUE_FUNCTION);
	}

	/** Replies if the given name is the hidden name for a static constructor.
	 *
	 * @param name the name to test.
	 * @return {@code true} if the given name is the one for a static constructor.
	 * @since 0.12
	 */
	@Pure
	public static boolean isStaticConstructorName(String name) {
		return STATIC_CONSTRUCTOR_NAME.equals(name);
	}

	/** Replies the name given to the static constructors.
	 *
	 * @return the hidden name for static constructors.
	 * @since 0.12
	 */
	@Pure
	public static String getStaticConstructorName() {
		return STATIC_CONSTRUCTOR_NAME;
	}

	/** Analyzing the type hierarchy of the given interface and
	 * extract hierarchy information.
	 *
	 * @param jvmElement - the element to analyze
	 * @param operations - filled with the operations inside and inherited by the element.
	 * @param fields - filled with the fields inside and inherited by the element.
	 * @param sarlSignatureProvider - provider of tools related to action signatures.
	 * @see org.eclipse.xtext.xbase.typesystem.override.OverrideHelper
	 */
	public static void populateInterfaceElements(
			JvmDeclaredType jvmElement,
			Map<ActionPrototype, JvmOperation> operations,
			Map<String, JvmField> fields,
			IActionPrototypeProvider sarlSignatureProvider) {
		for (final var feature : jvmElement.getAllFeatures()) {
			if (!"java.lang.Object".equals(feature.getDeclaringType().getQualifiedName())) { //$NON-NLS-1$
				if (operations != null && feature instanceof JvmOperation operation) {
					final var sig = sarlSignatureProvider.createParameterTypesFromJvmModel(
							operation.isVarArgs(), operation.getParameters());
					final var actionKey = sarlSignatureProvider.createActionPrototype(
							operation.getSimpleName(), sig);
					operations.put(actionKey, operation);
				} else if (fields != null && feature instanceof JvmField) {
					fields.put(feature.getSimpleName(), (JvmField) feature);
				}
			}
		}
	}

	/** Analyzing the type hierarchy of the given element, and
	 * extract any type-related information.
	 *
	 * @param jvmElement - the element to analyze
	 * @param finalOperations - filled with the final operations inherited by the element.
	 * @param overridableOperations - filled with the oervrideable operations inherited by the element.
	 * @param inheritedFields - filled with the fields inherited by the element.
	 * @param operationsToImplement - filled with the abstract operations inherited by the element.
	 * @param superConstructors - filled with the construstors of the super type.
	 * @param sarlSignatureProvider - provider of tools related to action signatures.
	 * @see org.eclipse.xtext.xbase.typesystem.override.OverrideHelper
	 */
	public static void populateInheritanceContext(
			JvmDeclaredType jvmElement,
			Map<ActionPrototype, JvmOperation> finalOperations,
			Map<ActionPrototype, JvmOperation> overridableOperations,
			Map<String, JvmField> inheritedFields,
			Map<ActionPrototype, JvmOperation> operationsToImplement,
			Map<ActionParameterTypes, JvmConstructor> superConstructors,
			IActionPrototypeProvider sarlSignatureProvider) {
		populateInheritanceContext(
				jvmElement,
				jvmElement.getExtendedClass(),
				jvmElement.getExtendedInterfaces(),
				finalOperations, overridableOperations, inheritedFields,
				operationsToImplement, superConstructors, sarlSignatureProvider);
	}

	/** Analyzing the type hierarchy of the given element, and
	 * extract any type-related information.
	 *
	 * @param jvmElement - the element to analyze
	 * @param extendedClass the extended class.
	 * @param extendedInterfaces the extended interfaces.
	 * @param finalOperations - filled with the final operations inherited by the element.
	 * @param overridableOperations - filled with the oervrideable operations inherited by the element.
	 * @param inheritedFields - filled with the fields inherited by the element.
	 * @param operationsToImplement - filled with the abstract operations inherited by the element.
	 * @param superConstructors - filled with the construstors of the super type.
	 * @param sarlSignatureProvider - provider of tools related to action signatures.
	 * @see org.eclipse.xtext.xbase.typesystem.override.OverrideHelper
	 */
	public static void populateInheritanceContext(
			JvmDeclaredType jvmElement,
			JvmTypeReference extendedClass,
			Iterable<? extends JvmTypeReference> extendedInterfaces,
			Map<ActionPrototype, JvmOperation> finalOperations,
			Map<ActionPrototype, JvmOperation> overridableOperations,
			Map<String, JvmField> inheritedFields,
			Map<ActionPrototype, JvmOperation> operationsToImplement,
			Map<ActionParameterTypes, JvmConstructor> superConstructors,
			IActionPrototypeProvider sarlSignatureProvider) {
		// Get the operations that must be implemented
		if (operationsToImplement != null && extendedInterfaces != null) {
			for (final var interfaceReference : extendedInterfaces) {
				final var interfaceReferenceType = interfaceReference.getType();
				if (interfaceReferenceType instanceof JvmGenericType cvalue) {
					for (final var feature : cvalue.getAllFeatures()) {
						if (!"java.lang.Object".equals(//$NON-NLS-1$
								feature.getDeclaringType().getQualifiedName())) {
							if (feature instanceof JvmOperation operation) {
								final var sig = sarlSignatureProvider.createParameterTypesFromJvmModel(
										operation.isVarArgs(), operation.getParameters());
								final var actionKey = sarlSignatureProvider.createActionPrototype(
										operation.getSimpleName(), sig);
								if (operation.isDefault()) {
									if (overridableOperations != null) {
										overridableOperations.put(actionKey, operation);
									}
								} else {
									if (operationsToImplement != null) {
										operationsToImplement.put(actionKey, operation);
									}
								}
							}
						}
					}
				}
			}
		}

		// Check on the implemented features, inherited from the super type
		if (extendedClass != null) {
			final var extendedClassJvmType = extendedClass.getType();
			if (extendedClassJvmType instanceof JvmGenericType parentType) {
				for (final var feature : parentType.getAllFeatures()) {
					if (!"java.lang.Object".equals(feature.getDeclaringType().getQualifiedName()) //$NON-NLS-1$
							&& isVisible(jvmElement, feature)
							&& !isHiddenMember(feature.getSimpleName())) {
						if (feature instanceof JvmOperation operation) {
							if (!feature.isStatic()) {
								final var sig = sarlSignatureProvider.createParameterTypesFromJvmModel(
										operation.isVarArgs(), operation.getParameters());
								final var actionKey = sarlSignatureProvider.createActionPrototype(
										feature.getSimpleName(), sig);
								if (operation.isAbstract() && !operation.isDefault()) {
									if (operationsToImplement != null) {
										operationsToImplement.put(actionKey, operation);
									}
								} else if (operation.isFinal()) {
									if (finalOperations != null) {
										finalOperations.put(actionKey, operation);
									}
									if (operationsToImplement != null) {
										operationsToImplement.remove(actionKey);
									}
								} else {
									if (overridableOperations != null) {
										overridableOperations.put(actionKey, operation);
									}
									if (operationsToImplement != null) {
										operationsToImplement.remove(actionKey);
									}
								}
							}
						} else if (feature instanceof JvmField cvalue && inheritedFields != null) {
							inheritedFields.put(feature.getSimpleName(), cvalue);
						}
					}
				}

				if (superConstructors != null) {
					for (final var cons : parentType.getDeclaredConstructors()) {
						final var sig = sarlSignatureProvider.createParameterTypesFromJvmModel(
								cons.isVarArgs(), cons.getParameters());
						superConstructors.put(sig,  cons);
					}
				}
			}
		}
	}

	/** Replies if the target feature is visible from the type.
	 *
	 * @param fromType - the type from which the feature visibility is tested.
	 * @param target - the feature to test for the visibility.
	 * @return {@code true} if the given type can see the target feature.
	 */
	public static boolean isVisible(JvmDeclaredType fromType, JvmMember target) {
		switch (target.getVisibility()) {
		case DEFAULT:
			return target.getDeclaringType().getPackageName().equals(fromType.getPackageName());
		case PROTECTED:
		case PUBLIC:
			return true;
		case PRIVATE:
		default:
		}
		return false;
	}

	/** Replies if the last parameter is a variadic parameter.
	 *
	 * @param params - parameters.
	 * @return {@code true} if the late parameter is variadic.
	 */
	public static boolean isVarArg(List<? extends XtendParameter> params) {
		assert params != null;
		if (params.size() > 0) {
			final var param = params.get(params.size() - 1);
			assert param != null;
			return param.isVarArg();
		}
		return false;
	}

	/** Replies if the given name is related to an hidden parameter for closures.
	 *
	 * <p>An hidden parameter for closures is the name assigned to a formal parameter
	 * for a closure when no name is explicitly provided.
	 *
	 * @param name - the name to test.
	 * @return {@code true} if the given name is reserved by SARL.
	 */
	public static boolean isImplicitLambdaParameterName(String name) {
		return IMPLICIT_LAMBDA_PARAMETER_PATTERN.matcher(name).matches();
	}

	/** Replies a fixed version of the given name assuming
	 * that it is an hidden action, and reformating
	 * the reserved text.
	 *
	 * <p>An hidden action is an action that is generated by the SARL
	 * compiler, and that cannot be defined by the SARL user.
	 *
	 * @param name - the name to fix.
	 * @return the fixed name.
	 */
	public static String fixHiddenMember(String name) {
		return name.replaceAll(Pattern.quote(HIDDEN_MEMBER_CHARACTER),
				Matcher.quoteReplacement(HIDDEN_MEMBER_REPLACEMENT_CHARACTER));
	}

	/** Create the name of the hidden function that is containing a parameter's default value.
	 *
	 * @param id the id of the default value.
	 * @return the method name.
	 * @since 0.12
	 */
	public static String createNameForHiddenDefaultValueFunction(String id) {
		return PREFIX_DEFAULT_VALUE_FUNCTION + fixHiddenMember(id.toUpperCase());
	}

	/** Create the name of the hidden field that is containing a capacity implementation.
	 *
	 * @param id the id of the capacity.
	 * @return the field name.
	 */
	public static String createNameForHiddenCapacityImplementationAttribute(String id) {
		return PREFIX_CAPACITY_IMPLEMENTATION + fixHiddenMember(id.toUpperCase()).replace(".", //$NON-NLS-1$
				HIDDEN_MEMBER_REPLACEMENT_CHARACTER);
	}

	/** Create the name of the hidden method that is calling a capacity implementation.
	 *
	 * @param capacityImplementationFieldName the name of the extension field.
	 * @return the method name.
	 */
	public static String createNameForHiddenCapacityImplementationCallingMethodFromFieldName(
			String capacityImplementationFieldName) {
		return capacityImplementationFieldName + POSTFIX_CAPACITY_IMPLEMENTATION_CALLER;
	}

	/** Replies if the given simple name is the name of the hidden method that is calling a capacity implementation.
	 *
	 * @param simpleName the simple name.
	 * @return {@code true} if the given simple name if for the hidden method for capacuty uses.
	 */
	public static boolean isNameForHiddenCapacityImplementationCallingMethod(String simpleName) {
		return simpleName != null && simpleName.startsWith(PREFIX_CAPACITY_IMPLEMENTATION)
				&& simpleName.endsWith(POSTFIX_CAPACITY_IMPLEMENTATION_CALLER);
	}

	/** Create the name of the hidden local variable.
	 *
	 * @param id the name of the local variable.
	 * @return the variable name.
	 */
	public static String createNameForHiddenLocalVariable(String id) {
		return PREFIX_LOCAL_VARIABLE + fixHiddenMember(id);
	}

	private static StringBuilder buildNameForHiddenEventMethod(JvmParameterizedTypeReference eventId,
			String prefix, boolean appendTypeSimpleName, boolean forceTypeParameterBoundsInName) {
		final var fullName = new StringBuilder(prefix);
		final var fullName0 = new StringBuilder();
		final var result = forEachTypeParameterName(eventId, (name, i) -> {
			fullName0.append(HIDDEN_MEMBER_CHARACTER).append(HIDDEN_MEMBER_CHARACTER);
			if (name == null) {
				fullName0.append(fixHiddenMember(Object.class.getSimpleName()));
			} else {
				fullName0.append(fixHiddenMember(name.getSimpleName()));
			}
		});
		if (result == null) {
			if (appendTypeSimpleName) {
				fullName.append(fixHiddenMember(eventId.getSimpleName()));
			}
		} else {
			if (appendTypeSimpleName) {
				fullName.append(fixHiddenMember(eventId.getType().getSimpleName()));
			}
			if (forceTypeParameterBoundsInName || result.isBoundInFunctionName()) {
				fullName.append(fullName0);
			}
		}
		return fullName;
	}
	
	/** Create the name of the hidden method that is containing the evaluation of all the guards for a given event.
	 *
	 * @param eventId the id of the event.
	 * @return the method name.
	 * @since 0.14
	 */
	public static String createNameForHiddenGuardGeneralEvaluatorMethod(JvmParameterizedTypeReference eventId) {
		return buildNameForHiddenEventMethod(eventId, PREFIX_GUARD_EVALUATOR, true, true).toString();
	}

	/** Create the name of the hidden method that is containing the event guard evaluation.
	 *
	 * @param eventId the id of the event.
	 * @param handlerIndex the index of the handler in the container type.
	 * @return the method name.
	 * @since 0.14
	 */
	public static String createNameForHiddenGuardEvaluatorMethod(JvmParameterizedTypeReference eventId, int handlerIndex) {
		final var fullName = buildNameForHiddenEventMethod(eventId, PREFIX_GUARD, true, false);
		fullName.append(HIDDEN_MEMBER_CHARACTER).append(handlerIndex);
		return fullName.toString();
	}

	/** Create the name of the hidden method that is containing the event handler code.
	 *
	 * @param eventId the id of the event.
	 * @param handlerIndex the index of the handler in the container type.
	 * @return the attribute name.
	 * @since 0.14
	 */
	public static String createNameForHiddenEventHandlerMethod(JvmParameterizedTypeReference eventId, int handlerIndex) {
		final var fullName = buildNameForHiddenEventMethod(eventId, PREFIX_EVENT_HANDLER, true, false);
		fullName.append(HIDDEN_MEMBER_CHARACTER).append(handlerIndex);
		return fullName.toString();
	}

	/** Replies if the given reference is pointing to a class type.
	 *
	 * @param typeRef - the type reference to test.
	 * @return {@code true} if the pointed element is a class type.
	 */
	public static boolean isClass(LightweightTypeReference typeRef) {
		final var t = typeRef.getType();
		if (t instanceof JvmGenericType cvalue) {
			return !cvalue.isInterface();
		}
		return false;
	}

	/** Replies if the given type is a class type.
	 *
	 * @param type - the type to test.
	 * @return {@code true} if the element is a class type.
	 */
	public static boolean isClass(Class<?> type) {
		return !type.isInterface();
	}

	/** Replies if the given reference is referencing a final type.
	 *
	 * @param expressionTypeRef - the type reference to test.
	 * @return {@code true} if the given type is final.
	 */
	public static boolean isFinal(LightweightTypeReference expressionTypeRef) {
		if (expressionTypeRef.isArray()) {
			return isFinal(expressionTypeRef.getComponentType());
		}
		if (expressionTypeRef.isPrimitive()) {
			return true;
		}
		return expressionTypeRef.getType() instanceof JvmDeclaredType cvalue
				&& cvalue.isFinal();
	}

	/** Replies if the given type is a final type.
	 *
	 * @param expressionType - the type to test.
	 * @return {@code true} if the given type is final.
	 */
	public static boolean isFinal(Class<?> expressionType) {
		if (expressionType.isArray()) {
			return isFinal(expressionType.getComponentType());
		}
		if (expressionType.isPrimitive()) {
			return true;
		}
		return expressionType.isEnum()
				|| Modifier.isFinal(expressionType.getModifiers());
	}

	/** Replies if the given type is an interface.
	 *
	 * @param type - the type to test.
	 * @return {@code true} if the given type is an interface.
	 */
	public static boolean isInterface(LightweightTypeReference type) {
		return type.getType() instanceof JvmGenericType cvalue
				&& cvalue.isInterface();
	}

	/** Replies if it is allowed to cast between the given types.
	 *
	 * @param fromType - source type
	 * @param toType - target type
	 * @param enablePrimitiveWidening - indicates if the widening of the primitive types is allowed.
	 * @param enableVoidMatchingNull - indicates if the {@code null} is matching {@code void}.
	 * @param allowSynonyms - indicates if the synonyms are allowed.
	 * @return the state of the cast.
	 */
	public static boolean canCast(
			LightweightTypeReference fromType, LightweightTypeReference toType,
			boolean enablePrimitiveWidening, boolean enableVoidMatchingNull,
			boolean allowSynonyms) {
		if (enableVoidMatchingNull) {
			final var fromVoid = fromType == null || fromType.isPrimitiveVoid();
			final var toVoid = toType == null || toType.isPrimitiveVoid();
			if (fromVoid) {
				return toVoid;
			}
			if (toVoid) {
				return fromVoid;
			}
			assert fromType != null;
			assert toType != null;
		} else if ((fromType == null || toType == null)
				|| (fromType.isPrimitiveVoid() != toType.isPrimitiveVoid())) {
			return false;
		}
		final var conform = new TypeConformanceComputationArgument(
				false, false, true, enablePrimitiveWidening, false, allowSynonyms);
		assert fromType != null;
		if (((fromType.getType() instanceof JvmDeclaredType || fromType.isPrimitive())
				// if one of the types is an interface and the other is a non final class
				// (or interface) there always can be a subtype
				&& (!isInterface(fromType) || isFinal(toType))
				&& (!isInterface(toType) || isFinal(fromType))
				&& (!toType.isAssignableFrom(fromType, conform))
				&& (isFinal(fromType) || isFinal(toType)
						|| isClass(fromType) && isClass(toType))
				// no upcast
				&& (!fromType.isAssignableFrom(toType, conform)))
				|| (toType.isPrimitive() && !(fromType.isPrimitive() || fromType.isWrapper()))) {
			return false;
		}
		return true;
	}

	private static ResourceSet getResoutceSet(EObject object) {
		if (object == null) {
			return null;
		}
		final var r = object.eResource();
		if (r == null) {
			return null;
		}
		return r.getResourceSet();
	}

	/** Convert a type reference to a lightweight type reference.
	 *
	 * @param typeRef - reference to convert.
	 * @param services - services used for the conversion
	 * @return the lightweight type reference.
	 */
	public static LightweightTypeReference toLightweightTypeReference(
			JvmTypeReference typeRef, CommonTypeComputationServices services) {
		return toLightweightTypeReference(typeRef, services, getResoutceSet(typeRef), false);
	}

	/** Convert a type reference to a lightweight type reference.
	 *
	 * @param typeRef - reference to convert.
	 * @param services - services used for the conversion
	 * @param context the context of the reference.
	 * @return the lightweight type reference.
	 * @since 0.12
	 */
	public static LightweightTypeReference toLightweightTypeReference(
			JvmTypeReference typeRef, CommonTypeComputationServices services, ResourceSet context) {
		return toLightweightTypeReference(typeRef, services, context, false);
	}

	/** Convert a type reference to a lightweight type reference.
	 *
	 * @param typeRef - reference to convert.
	 * @param services - services used for the conversion
	 * @param keepUnboundWildcardInformation - indicates if the unbound wild card
	 *        information must be keeped in the lightweight reference.
	 * @return the lightweight type reference.
	 */
	public static LightweightTypeReference toLightweightTypeReference(
			JvmTypeReference typeRef, CommonTypeComputationServices services,
			boolean keepUnboundWildcardInformation) {
		return toLightweightTypeReference(typeRef, services, getResoutceSet(typeRef), keepUnboundWildcardInformation);
	}

	/** Convert a type reference to a lightweight type reference.
	 *
	 * @param typeRef - reference to convert.
	 * @param services - services used for the conversion
	 * @param context the context of the reference.
	 * @param keepUnboundWildcardInformation - indicates if the unbound wild card
	 *        information must be keeped in the lightweight reference.
	 * @return the lightweight type reference.
	 * @since 0.12
	 */
	public static LightweightTypeReference toLightweightTypeReference(
			JvmTypeReference typeRef, CommonTypeComputationServices services,
			ResourceSet context, boolean keepUnboundWildcardInformation) {
		if (typeRef == null) {
			return null;
		}
		final var owner = new StandardTypeReferenceOwner(services, context);
		final var factory = new LightweightTypeReferenceFactory(owner,
				keepUnboundWildcardInformation);
		final var reference = factory.toLightweightReference(typeRef);
		return reference;
	}

	/** Convert a type reference to a lightweight type reference.
	 *
	 * @param type - type to convert.
	 * @param services - services used for the conversion
	 * @return the lightweight type reference.
	 */
	public static LightweightTypeReference toLightweightTypeReference(
			JvmType type, CommonTypeComputationServices services) {
		return toLightweightTypeReference(type, services, false);
	}

	/** Convert a type to a lightweight type reference.
	 *
	 * @param type - type to convert.
	 * @param services - services used for the conversion
	 * @param keepUnboundWildcardInformation - indicates if the unbound wild card
	 *        information must be keeped in the lightweight reference.
	 * @return the lightweight type reference.
	 */
	public static LightweightTypeReference toLightweightTypeReference(
			JvmType type, CommonTypeComputationServices services,
			boolean keepUnboundWildcardInformation) {
		if (type == null) {
			return null;
		}
		final var owner = new StandardTypeReferenceOwner(services, type);
		final var factory = new LightweightTypeReferenceFactory(owner,
				keepUnboundWildcardInformation);
		final var reference = factory.toLightweightReference(type);
		return reference;
	}

	/** Compare the two strings as they are version numbers.
	 *
	 * @param v1 - first version to compare.
	 * @param v2 - second version to compare.
	 * @return Negative integer of {@code v1} is lower than {@code v2};
	 *     positive integer of {@code v1} is greater than {@code v2};
	 *     {@code 0} if they are strictly equal.
	 * @deprecated see {@link #compareMajorMinorVersions(String, String)}
	 */
	@Deprecated(since = "0.13", forRemoval = true)
	public static int compareVersions(String v1, String v2) {
		// Remove the SNAPSHOT version.
		//final String fixedv1 = v1.replaceFirst("-SNAPSHOT$", ""); //$NON-NLS-1$ //$NON-NLS-2$
		//final String fixedv2 = v2.replaceFirst("-SNAPSHOT$", ""); //$NON-NLS-1$ //$NON-NLS-2$
		//final Version vobject1 = parseVersion(fixedv1);
		//final Version vobject2 = parseVersion(fixedv2);
		final var vobject1 = parseVersion(v1);
		final var vobject2 = parseVersion(v2);
		return vobject1.compareTo(vobject2);
	}

