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

spoon.metamodel.Metamodel Maven / Gradle / Ivy

Go to download

Spoon is a tool for meta-programming, analysis and transformation of Java programs.

There is a newer version: 11.1.1-beta-14
Show newest version
/*
 * SPDX-License-Identifier: (MIT OR CECILL-C)
 *
 * Copyright (C) 2006-2023 INRIA and contributors
 *
 * Spoon is available either under the terms of the MIT License (see LICENSE-MIT.txt) or the Cecill-C License (see LICENSE-CECILL-C.txt). You as the user are entitled to choose the terms under which to adopt Spoon.
 */
package spoon.metamodel;

import java.io.File;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.jspecify.annotations.Nullable;

import spoon.Launcher;
import spoon.SpoonException;
import spoon.reflect.annotations.PropertyGetter;
import spoon.reflect.annotations.PropertySetter;
import spoon.reflect.declaration.CtAnnotation;
import spoon.reflect.declaration.CtClass;
import spoon.reflect.declaration.CtElement;
import spoon.reflect.declaration.CtInterface;
import spoon.reflect.declaration.CtMethod;
import spoon.reflect.declaration.CtModuleDirective;
import spoon.reflect.declaration.CtPackageExport;
import spoon.reflect.declaration.CtProvidedService;
import spoon.reflect.declaration.CtReceiverParameter;
import spoon.reflect.declaration.CtRecord;
import spoon.reflect.declaration.CtRecordComponent;
import spoon.reflect.declaration.CtType;
import spoon.reflect.factory.Factory;
import spoon.reflect.factory.FactoryImpl;
import spoon.reflect.path.CtRole;
import spoon.reflect.reference.CtTypeReference;
import spoon.reflect.visitor.filter.AllTypeMembersFunction;
import spoon.reflect.visitor.filter.TypeFilter;
import spoon.support.DefaultCoreFactory;
import spoon.support.StandardEnvironment;
import spoon.support.adaption.TypeAdaptor;
import spoon.support.compiler.FileSystemFolder;

/**
 * Represents the Spoon metamodel (incl. at runtime)
 */
public class Metamodel {
	/**
	 * Returns all interfaces of the Spoon metamodel.
	 * This method is stateless for sake of maintenance.
	 * If you need to call it several times, you should store the result.
	 */
	public static Set> getAllMetamodelInterfaces() {
		Set> result = new HashSet<>();
		Factory factory = new FactoryImpl(new DefaultCoreFactory(), new StandardEnvironment());
		//avoid debug messages: Some annotations might be unreachable from the shadow element:
		//which causes bad meta model creation performance
		factory.getEnvironment().setLevel("INFO");
		result.add(factory.Type().get(spoon.reflect.code.BinaryOperatorKind.class));
		result.add(factory.Type().get(spoon.reflect.code.CtAbstractInvocation.class));
		result.add(factory.Type().get(spoon.reflect.code.CtAbstractSwitch.class));
		result.add(factory.Type().get(spoon.reflect.code.CtAnnotationFieldAccess.class));
		result.add(factory.Type().get(spoon.reflect.code.CtArrayAccess.class));
		result.add(factory.Type().get(spoon.reflect.code.CtArrayRead.class));
		result.add(factory.Type().get(spoon.reflect.code.CtArrayWrite.class));
		result.add(factory.Type().get(spoon.reflect.code.CtAssert.class));
		result.add(factory.Type().get(spoon.reflect.code.CtAssignment.class));
		result.add(factory.Type().get(spoon.reflect.code.CtBinaryOperator.class));
		result.add(factory.Type().get(spoon.reflect.code.CtBlock.class));
		result.add(factory.Type().get(spoon.reflect.code.CtBodyHolder.class));
		result.add(factory.Type().get(spoon.reflect.code.CtBreak.class));
		result.add(factory.Type().get(spoon.reflect.code.CtCFlowBreak.class));
		result.add(factory.Type().get(spoon.reflect.code.CtCase.class));
		result.add(factory.Type().get(spoon.reflect.code.CtCasePattern.class));
		result.add(factory.Type().get(spoon.reflect.code.CtCatch.class));
		result.add(factory.Type().get(spoon.reflect.code.CtCatchVariable.class));
		result.add(factory.Type().get(spoon.reflect.code.CtCodeElement.class));
		result.add(factory.Type().get(spoon.reflect.code.CtCodeSnippetExpression.class));
		result.add(factory.Type().get(spoon.reflect.code.CtCodeSnippetStatement.class));
		result.add(factory.Type().get(spoon.reflect.code.CtComment.class));
		result.add(factory.Type().get(spoon.reflect.code.CtConditional.class));
		result.add(factory.Type().get(spoon.reflect.code.CtConstructorCall.class));
		result.add(factory.Type().get(spoon.reflect.code.CtContinue.class));
		result.add(factory.Type().get(spoon.reflect.code.CtDo.class));
		result.add(factory.Type().get(spoon.reflect.code.CtExecutableReferenceExpression.class));
		result.add(factory.Type().get(spoon.reflect.code.CtExpression.class));
		result.add(factory.Type().get(spoon.reflect.code.CtFieldAccess.class));
		result.add(factory.Type().get(spoon.reflect.code.CtFieldRead.class));
		result.add(factory.Type().get(spoon.reflect.code.CtFieldWrite.class));
		result.add(factory.Type().get(spoon.reflect.code.CtFor.class));
		result.add(factory.Type().get(spoon.reflect.code.CtForEach.class));
		result.add(factory.Type().get(spoon.reflect.code.CtIf.class));
		result.add(factory.Type().get(spoon.reflect.code.CtInvocation.class));
		result.add(factory.Type().get(spoon.reflect.code.CtJavaDoc.class));
		result.add(factory.Type().get(spoon.reflect.code.CtJavaDocTag.class));
		result.add(factory.Type().get(spoon.reflect.code.CtLabelledFlowBreak.class));
		result.add(factory.Type().get(spoon.reflect.code.CtLambda.class));
		result.add(factory.Type().get(spoon.reflect.code.CtLiteral.class));
		result.add(factory.Type().get(spoon.reflect.code.CtTextBlock.class));
		result.add(factory.Type().get(spoon.reflect.code.CtLocalVariable.class));
		result.add(factory.Type().get(spoon.reflect.code.CtLoop.class));
		result.add(factory.Type().get(spoon.reflect.code.CtNewArray.class));
		result.add(factory.Type().get(spoon.reflect.code.CtNewClass.class));
		result.add(factory.Type().get(spoon.reflect.code.CtOperatorAssignment.class));
		result.add(factory.Type().get(spoon.reflect.code.CtPattern.class));
		result.add(factory.Type().get(spoon.reflect.code.CtRHSReceiver.class));
		result.add(factory.Type().get(spoon.reflect.code.CtRecordPattern.class));
		result.add(factory.Type().get(spoon.reflect.code.CtResource.class));
		result.add(factory.Type().get(spoon.reflect.code.CtReturn.class));
		result.add(factory.Type().get(spoon.reflect.code.CtStatement.class));
		result.add(factory.Type().get(spoon.reflect.code.CtStatementList.class));
		result.add(factory.Type().get(spoon.reflect.code.CtSuperAccess.class));
		result.add(factory.Type().get(spoon.reflect.code.CtSwitch.class));
		result.add(factory.Type().get(spoon.reflect.code.CtSwitchExpression.class));
		result.add(factory.Type().get(spoon.reflect.code.CtSynchronized.class));
		result.add(factory.Type().get(spoon.reflect.code.CtTargetedExpression.class));
		result.add(factory.Type().get(spoon.reflect.code.CtThisAccess.class));
		result.add(factory.Type().get(spoon.reflect.code.CtThrow.class));
		result.add(factory.Type().get(spoon.reflect.code.CtTry.class));
		result.add(factory.Type().get(spoon.reflect.code.CtTryWithResource.class));
		result.add(factory.Type().get(spoon.reflect.code.CtTypeAccess.class));
		result.add(factory.Type().get(spoon.reflect.code.CtTypePattern.class));
		result.add(factory.Type().get(spoon.reflect.code.CtUnaryOperator.class));
		result.add(factory.Type().get(spoon.reflect.code.CtUnnamedPattern.class));
		result.add(factory.Type().get(spoon.reflect.code.CtVariableAccess.class));
		result.add(factory.Type().get(spoon.reflect.code.CtVariableRead.class));
		result.add(factory.Type().get(spoon.reflect.code.CtVariableWrite.class));
		result.add(factory.Type().get(spoon.reflect.code.CtWhile.class));
		result.add(factory.Type().get(spoon.reflect.code.UnaryOperatorKind.class));
		result.add(factory.Type().get(spoon.reflect.code.LiteralBase.class));
		result.add(factory.Type().get(spoon.reflect.code.CaseKind.class));
		result.add(factory.Type().get(spoon.reflect.code.CtYieldStatement.class));
		result.add(factory.Type().get(spoon.reflect.declaration.CtAnnotatedElementType.class));
		result.add(factory.Type().get(spoon.reflect.declaration.CtAnnotation.class));
		result.add(factory.Type().get(spoon.reflect.declaration.CtAnnotationMethod.class));
		result.add(factory.Type().get(spoon.reflect.declaration.CtAnnotationType.class));
		result.add(factory.Type().get(spoon.reflect.declaration.CtAnonymousExecutable.class));
		result.add(factory.Type().get(spoon.reflect.declaration.CtClass.class));
		result.add(factory.Type().get(spoon.reflect.declaration.CtCodeSnippet.class));
		result.add(factory.Type().get(spoon.reflect.declaration.CtCompilationUnit.class));
		result.add(factory.Type().get(spoon.reflect.declaration.CtConstructor.class));
		result.add(factory.Type().get(spoon.reflect.declaration.CtElement.class));
		result.add(factory.Type().get(spoon.reflect.declaration.CtEnum.class));
		result.add(factory.Type().get(spoon.reflect.declaration.CtEnumValue.class));
		result.add(factory.Type().get(spoon.reflect.declaration.CtExecutable.class));
		result.add(factory.Type().get(spoon.reflect.declaration.CtField.class));
		result.add(factory.Type().get(spoon.reflect.declaration.CtFormalTypeDeclarer.class));
		result.add(factory.Type().get(spoon.reflect.declaration.CtInterface.class));
		result.add(factory.Type().get(spoon.reflect.declaration.CtMethod.class));
		result.add(factory.Type().get(spoon.reflect.declaration.CtModifiable.class));
		result.add(factory.Type().get(spoon.reflect.declaration.CtMultiTypedElement.class));
		result.add(factory.Type().get(spoon.reflect.declaration.CtNamedElement.class));
		result.add(factory.Type().get(spoon.reflect.declaration.CtPackage.class));
		result.add(factory.Type().get(spoon.reflect.declaration.CtParameter.class));
		result.add(factory.Type().get(spoon.reflect.declaration.CtSealable.class));
		result.add(factory.Type().get(spoon.reflect.declaration.CtShadowable.class));
		result.add(factory.Type().get(spoon.reflect.declaration.CtType.class));
		result.add(factory.Type().get(spoon.reflect.declaration.CtTypeInformation.class));
		result.add(factory.Type().get(spoon.reflect.declaration.CtTypeMember.class));
		result.add(factory.Type().get(spoon.reflect.declaration.CtTypeParameter.class));
		result.add(factory.Type().get(spoon.reflect.declaration.CtTypedElement.class));
		result.add(factory.Type().get(spoon.reflect.declaration.CtVariable.class));
		result.add(factory.Type().get(spoon.reflect.declaration.ModifierKind.class));
		result.add(factory.Type().get(spoon.reflect.declaration.ParentNotInitializedException.class));
		result.add(factory.Type().get(spoon.reflect.reference.CtActualTypeContainer.class));
		result.add(factory.Type().get(spoon.reflect.reference.CtArrayTypeReference.class));
		result.add(factory.Type().get(spoon.reflect.reference.CtCatchVariableReference.class));
		result.add(factory.Type().get(spoon.reflect.reference.CtExecutableReference.class));
		result.add(factory.Type().get(spoon.reflect.reference.CtFieldReference.class));
		result.add(factory.Type().get(spoon.reflect.reference.CtIntersectionTypeReference.class));
		result.add(factory.Type().get(spoon.reflect.reference.CtLocalVariableReference.class));
		result.add(factory.Type().get(spoon.reflect.reference.CtPackageReference.class));
		result.add(factory.Type().get(spoon.reflect.reference.CtParameterReference.class));
		result.add(factory.Type().get(spoon.reflect.reference.CtReference.class));
		result.add(factory.Type().get(spoon.reflect.reference.CtTypeParameterReference.class));
		result.add(factory.Type().get(spoon.reflect.reference.CtTypeReference.class));
		result.add(factory.Type().get(spoon.reflect.reference.CtUnboundVariableReference.class));
		result.add(factory.Type().get(spoon.reflect.reference.CtVariableReference.class));
		result.add(factory.Type().get(spoon.reflect.reference.CtWildcardReference.class));
		result.add(factory.Type().get(spoon.reflect.reference.CtTypeMemberWildcardImportReference.class));
		result.add(factory.Type().get(spoon.reflect.declaration.CtImport.class));
		result.add(factory.Type().get(spoon.reflect.declaration.CtImportKind.class));
		result.add(factory.Type().get(spoon.reflect.declaration.CtModule.class));
		result.add(factory.Type().get(spoon.reflect.declaration.CtModuleRequirement.class));
		result.add(factory.Type().get(spoon.reflect.declaration.CtPackageDeclaration.class));
		result.add(factory.Type().get(CtPackageExport.class));
		result.add(factory.Type().get(CtProvidedService.class));
		result.add(factory.Type().get(spoon.reflect.reference.CtModuleReference.class));
		result.add(factory.Type().get(spoon.reflect.declaration.CtUsedService.class));
		result.add(factory.Type().get(CtModuleDirective.class));
		result.add(factory.Type().get(CtRecordComponent.class));
		result.add(factory.Type().get(CtRecord.class));
		result.add(factory.Type().get(CtReceiverParameter.class));
		return result;
	}

	private static final String CLASS_SUFFIX = "Impl";
	/**
	 * qualified names of packages which contain interfaces of spoon model
	 */
	public static final Set MODEL_IFACE_PACKAGES = new HashSet<>(Arrays.asList(
			"spoon.reflect.code",
			"spoon.reflect.declaration",
			"spoon.reflect.reference"));

	/**
	 * qualified names of packages which contain classes (implementations) of spoon model
	 */
	public static final Set MODEL_CLASS_PACKAGES = new HashSet<>(Arrays.asList(
			"spoon.support.reflect.code",
			"spoon.support.reflect.declaration",
			"spoon.support.reflect.reference"));

	/**
	 * {@link MetamodelConcept}s by name
	 */
	private final Map nameToConcept = new HashMap<>();

	private static Metamodel instance;

	/**
	 * @return Spoon {@link Metamodel}, which is built once and then returns cached version
	 */
	public static Metamodel getInstance() {
		if (instance == null) {
			try {
				//this is needed just for CtGenerationTest#testGenerateRoleHandler
				//which must not use RoleHandler at time when RoleHandler is generated and Spoon model doesn't fit to old RoleHandlers
				//to avoid egg/chicken problem
				if ("true".equals(System.getProperty(MetamodelProperty.class.getName() + "-noRoleHandler"))) {
					MetamodelProperty.useRuntimeMethodInvocation = true;
				}
			} catch (SecurityException e) {
				//ignore that
			}
			instance = new Metamodel();
		}
		return instance;
	}

	/**
	 *
	 * Not in the public API.
	 *
	 * Parses spoon sources and creates factory with spoon model.
	 *
	 * @param spoonJavaSourcesDirectory the root directory of java sources of spoon model.
	 * 	The directory must contain "spoon" directory.
	 */
	public Metamodel(File spoonJavaSourcesDirectory) {
		this(createFactory(spoonJavaSourcesDirectory));
	}

	/**
	 * @param factory already loaded factory with all Spoon model types
	 */
	protected Metamodel(Factory factory) {
		for (String apiPackage : MODEL_IFACE_PACKAGES) {
			if (factory.Package().get(apiPackage) == null) {
				throw new SpoonException("Spoon Factory model is missing API package " + apiPackage);
			}
			String implPackage = replaceApiToImplPackage(apiPackage);
			if (factory.Package().get(implPackage) == null) {
				throw new SpoonException("Spoon Factory model is missing implementation package " + implPackage);
			}
		}

		//search for all interfaces of spoon model and create MetamodelConcepts for them
		factory.getModel().filterChildren(new TypeFilter<>(CtInterface.class))
			.forEach((CtInterface iface) -> {
				if (MODEL_IFACE_PACKAGES.contains(iface.getPackage().getQualifiedName())) {
					getOrCreateConcept(iface);
				}
			});
	}

	/**
	 * Creates a {@link Metamodel} in runtime mode when spoon sources are not available.
	 *
	 * See also {@link #getInstance()}.
	 */
	private Metamodel() {
		for (CtType iface : getAllMetamodelInterfaces()) {
			if (iface instanceof CtInterface) {
				getOrCreateConcept(iface);
			}
		}
	}

	/**
	 * @param clazz a {@link Class} of Spoon model
	 * @return {@link MetamodelConcept} which describes the `clazz`
	 */
	public MetamodelConcept getConcept(Class clazz) {
		MetamodelConcept mc = nameToConcept.get(getConceptName(clazz));
		if (mc == null) {
			throw new SpoonException("There is no Spoon metamodel concept for class " + clazz.getName());
		}
		return mc;
	}

	/**
	 * @return all {@link MetamodelConcept}s of spoon meta model
	 */
	public Collection getConcepts() {
		return Collections.unmodifiableCollection(nameToConcept.values());
	}

	/**
	 * @return List of Spoon model interfaces, which represents instantiable leafs of Spoon metamodel
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public List> getAllInstantiableMetamodelInterfaces() {
		List> result = new ArrayList<>();
		for (MetamodelConcept mmConcept : getConcepts()) {
			if (mmConcept.getKind() == ConceptKind.LEAF) {
				result.add((CtType) mmConcept.getMetamodelInterface());
			}
		}
		return result;
	}

	/**
	 * @param type a spoon model class or interface, whose concept name has to be returned
	 * @return name of {@link MetamodelConcept}, which represents Spoon model {@link CtType}
	 */
	public static String getConceptName(CtType type) {
		return getConceptName(type.getSimpleName());
	}

	public static String getConceptName(Class conceptClass) {
		return getConceptName(conceptClass.getSimpleName());
	}

	/**
	 * @param simpleName a spoon model class or interface, whose concept name has to be returned
	 * @return name of {@link MetamodelConcept}, which represents Spoon model {@link CtType}
	 */
	private static String getConceptName(String simpleName) {
		if (simpleName.endsWith(CLASS_SUFFIX)) {
			simpleName = simpleName.substring(0, simpleName.length() - CLASS_SUFFIX.length());
		}
		return simpleName;
	}

	/**
	 * @param iface the interface of spoon model element
	 * @return {@link CtClass} of Spoon model which implements the spoon model interface. null if there is no implementation.
	 */
	public static @Nullable CtClass getImplementationOfInterface(CtInterface iface) {
		String impl = replaceApiToImplPackage(iface.getQualifiedName()) + CLASS_SUFFIX;
		return (CtClass) getType(impl, iface);
	}

	/**
	 * @param impl the implementation class of a Spoon element
	 * @return {@link CtInterface} of Spoon model which represents API of the spoon model class. null if there is no implementation.
	 */
	public static @Nullable CtInterface getInterfaceOfImplementation(CtClass impl) {
		String iface = impl.getQualifiedName();
		if (!iface.endsWith(CLASS_SUFFIX) || !iface.startsWith("spoon.support.reflect.")) {
			throw new SpoonException("Unexpected spoon model implementation class: " + impl.getQualifiedName());
		}
		iface = iface.substring(0, iface.length() - CLASS_SUFFIX.length());
		iface = iface.replace("spoon.support.reflect", "spoon.reflect");
		return (CtInterface) getType(iface, impl);
	}

	/**
	 * @param method to be checked method
	 * @return {@link CtRole} of spoon model method. Looking into all super class/interface implementations of this method
	 */
	public static CtRole getRoleOfMethod(CtMethod method) {
		Factory f = method.getFactory();
		CtAnnotation getter = getInheritedAnnotation(method, f.createCtTypeReference(PropertyGetter.class));
		if (getter != null) {
			return getter.getActualAnnotation().role();
		}
		CtAnnotation setter = getInheritedAnnotation(method, f.createCtTypeReference(PropertySetter.class));
		if (setter != null) {
			return setter.getActualAnnotation().role();
		}
		return null;
	}

	private static CtType getType(String qualifiedName, CtElement anElement) {
		Class aClass;
		try {
			aClass = anElement.getClass().getClassLoader().loadClass(qualifiedName);
		} catch (ClassNotFoundException e) {
			//OK, that interface has no implementation class
			return null;
		}
		return anElement.getFactory().Type().get(aClass);
	}

	private static final String modelApiPackage = "spoon.reflect";
	private static final String modelApiImplPackage = "spoon.support.reflect";

	private static String replaceApiToImplPackage(String modelInterfaceQName) {
		if (!modelInterfaceQName.startsWith(modelApiPackage)) {
			throw new SpoonException("The qualified name " + modelInterfaceQName + " doesn't belong to Spoon model API package: " + modelApiPackage);
		}
		return modelApiImplPackage + modelInterfaceQName.substring(modelApiPackage.length());
	}

	private static Factory createFactory(File spoonJavaSourcesDirectory) {
		final Launcher launcher = new Launcher();
		launcher.getEnvironment().setNoClasspath(true);
		launcher.getEnvironment().setCommentEnabled(true);
		launcher.getEnvironment().setComplianceLevel(17);
//		// Spoon model interfaces
		Arrays.asList("spoon/reflect/code",
				"spoon/reflect/declaration",
				"spoon/reflect/reference",
				"spoon/support/reflect/declaration",
				"spoon/support/reflect/code",
				"spoon/support/reflect/reference").forEach(path ->
			launcher.addInputResource(new FileSystemFolder(new File(spoonJavaSourcesDirectory, path))));
		launcher.buildModel();
		return launcher.getFactory();
	}

	/**
	 * @param type can be class or interface of Spoon model element
	 * @return existing or creates and initializes new {@link MetamodelConcept} which represents the `type`
	 */
	private MetamodelConcept getOrCreateConcept(CtType type) {
		String conceptName = getConceptName(type);
		// computeIfAbsent is not possible here, as the initializeConcept method
		// calls this method recursively -> ConcurrentModificationException
		MetamodelConcept concept = nameToConcept.get(conceptName);
		if (concept == null) {
			concept = new MetamodelConcept(conceptName);
			nameToConcept.put(conceptName, concept);
			initializeConcept(type, concept);
		}
		return concept;
	}

	/**
	 * is called once for each {@link MetamodelConcept}, to initialize it.
	 * @param type a class or inteface of the spoon model element
	 * @param mmConcept to be initialize {@link MetamodelConcept}
	 */
	private void initializeConcept(CtType type, MetamodelConcept mmConcept) {
		//it is not initialized yet. Do it now
		if (type instanceof CtInterface) {
			CtInterface iface = (CtInterface) type;
			mmConcept.setModelClass(getImplementationOfInterface(iface));
			mmConcept.setModelInterface(iface);
		} else if (type instanceof CtClass) {
			CtClass clazz = (CtClass) type;
			mmConcept.setModelClass(clazz);
			mmConcept.setModelInterface(getInterfaceOfImplementation(clazz));
		} else {
			throw new SpoonException("Unexpected spoon model type: " + type.getQualifiedName());
		}

		//add fields of interface
		if (mmConcept.getMetamodelInterface() != null) {
			//add fields of interface too. They are not added by above call of addFieldsOfType, because the MetamodelConcept already exists in nameToConcept
			addFieldsOfType(mmConcept, mmConcept.getMetamodelInterface());
		}
		//initialize all fields
		mmConcept.getRoleToProperty().forEach((role, mmField) -> {
			//if there are more methods for the same field then choose the one which best matches the field type
			mmField.sortByBestMatch();
			//finally initialize value type of this field
			mmField.setValueType(mmField.detectValueType());
		});
	}

	/**
	 * adds all {@link MetamodelProperty}s of `ctType`
	 * @param mmConcept the owner of to be created fields
	 * @param ctType to be scanned {@link CtType}
	 */
	private void addFieldsOfType(MetamodelConcept mmConcept, CtType ctType) {
		ctType.getTypeMembers().forEach(typeMember -> {
			if (typeMember instanceof CtMethod) {
				CtMethod method = (CtMethod) typeMember;
				CtRole role = getRoleOfMethod(method);
				if (role != null) {
					MetamodelProperty field = mmConcept.getOrCreateMMField(role);
					field.addMethod(method);
				} else {
					mmConcept.otherMethods.add(method);
				}
			}
		});
		addFieldsOfSuperType(mmConcept, ctType.getSuperclass());
		ctType.getSuperInterfaces().forEach(superIfaceRef -> addFieldsOfSuperType(mmConcept, superIfaceRef));
	}

	private static Set EXPECTED_TYPES_NOT_IN_CLASSPATH = new HashSet<>(Arrays.asList(
			"java.lang.Cloneable",
			"java.lang.Object",
			"spoon.processing.FactoryAccessor",
			"spoon.reflect.cu.SourcePositionHolder",
			"spoon.reflect.visitor.CtVisitable",
			"spoon.reflect.visitor.chain.CtQueryable",
			"spoon.template.TemplateParameter",
			"java.lang.Iterable",
			"java.io.Serializable"));


	/**
	 * add all fields of `superTypeRef` into `mmConcept`
	 * @param concept sub type
	 * @param superTypeRef super type
	 */
	private void addFieldsOfSuperType(MetamodelConcept concept, CtTypeReference superTypeRef) {
		if (superTypeRef == null) {
			return;
		}
		if (EXPECTED_TYPES_NOT_IN_CLASSPATH.contains(superTypeRef.getQualifiedName())) {
			//ignore classes which are not part of spoon model
			return;
		}
		CtType superType = superTypeRef.getTypeDeclaration();
		if (superType == null) {
			throw new SpoonException("Cannot create spoon meta model. The class " + superTypeRef.getQualifiedName() + " is missing class path");
		}
		//call getOrCreateConcept recursively for super concepts
		MetamodelConcept superConcept = getOrCreateConcept(superType);
		if (superConcept != concept) {
			concept.addSuperConcept(superConcept);
		}
	}

	static  boolean addUniqueObject(Collection col, T o) {
		if (containsObject(col, o)) {
			return false;
		}
		col.add(o);
		return true;
	}
	static boolean containsObject(Iterable iter, Object o) {
		for (Object object : iter) {
			if (object == o) {
				return true;
			}
		}
		return false;
	}


	/**
	 * @param method a start method
	 * @param annotationType a searched annotation type
	 * @return annotation from the first method in superClass and superInterface hierarchy for the method with required annotationType
	 */
	private static  CtAnnotation getInheritedAnnotation(CtMethod method, CtTypeReference annotationType) {
		CtAnnotation annotation = method.getAnnotation(annotationType);
		if (annotation == null) {
			CtType declType = method.getDeclaringType();
			TypeAdaptor typeAdaptor = new TypeAdaptor(declType);
			annotation = declType.map(new AllTypeMembersFunction(CtMethod.class)).map((CtMethod currentMethod) -> {
				if (method == currentMethod) {
					return null;
				}
				if (typeAdaptor.isConflicting(method, currentMethod)) {
					CtAnnotation annotation2 = currentMethod.getAnnotation(annotationType);
					if (annotation2 != null) {
						return annotation2;
					}
				}
				return null;
			}).first();
		}
		return annotation;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy