<commit_before>package com.structurizr.componentfinder;

import com.google.common.base.Predicates;
import com.structurizr.model.Component;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.NotFoundException;
import org.reflections.ReflectionUtils;
import org.reflections.Reflections;
import org.reflections.scanners.FieldAnnotationsScanner;
import org.reflections.scanners.SubTypesScanner;
import org.reflections.scanners.TypeAnnotationsScanner;
import org.reflections.util.ClasspathHelper;
import org.reflections.util.ConfigurationBuilder;
import org.reflections.util.FilterBuilder;

import java.lang.annotation.Annotation;
import java.util.HashSet;
import java.util.Set;

public abstract class AbstractReflectionsComponentFinderStrategy extends ComponentFinderStrategy {

    protected Reflections reflections;

    public AbstractReflectionsComponentFinderStrategy() {
    }

    @Override
    public void setComponentFinder(ComponentFinder componentFinder) {
        super.setComponentFinder(componentFinder);

        this.reflections = new Reflections(new ConfigurationBuilder()
                  .filterInputsBy(new FilterBuilder().includePackage(componentFinder.getPackageToScan()))
                  .setUrls(ClasspathHelper.forPackage(componentFinder.getPackageToScan()))
                  .setScanners(new TypeAnnotationsScanner(), new SubTypesScanner(false), new FieldAnnotationsScanner()));
    }

    @Override
    public void findDependencies() throws Exception {
        for (Component component : componentFinder.getContainer().getComponents()) {
            if (component.getType() != null) {
                addEfferentDependencies(component, component.getType(), new HashSet<>());

                // and repeat for the first implementation class we can find
                ClassPool pool = ClassPool.getDefault();
                CtClass cc = pool.get(component.getType());
                if (cc.isInterface()) {
                    Class implementationType = getFirstImplementationOfInterface(cc.getName());
                    if (implementationType != null && implementationType.getCanonicalName() != null) {
                        addEfferentDependencies(component, implementationType.getCanonicalName(), new HashSet<>());
                    }
                }
            }
        }
    }

    private void addEfferentDependencies(Component component, String type, Set<String> typesVisited) {
        typesVisited.add(type);

        try {
            ClassPool pool = ClassPool.getDefault();
            CtClass cc = pool.get(type);
            for (Object referencedType : cc.getRefClasses()) {
                String referencedTypeName = (String)referencedType;

                Component destinationComponent = componentFinder.getContainer().getComponentOfType(referencedTypeName);
                if (destinationComponent != null) {
                    if (component != destinationComponent) {
                        component.uses(destinationComponent, "");
                    }
                } else if (!typesVisited.contains(referencedTypeName)) {
                    addEfferentDependencies(component, referencedTypeName, typesVisited);
                }
            }
        } catch (NotFoundException nfe) {
            System.err.println(nfe.getMessage() + " not found");
        } catch (Exception e) {
            System.err.println(e.getMessage());
        }
    }

    protected Set<Class<?>> getTypesAnnotatedWith(Class<? extends Annotation> annotation) {
        return reflections.getTypesAnnotatedWith(annotation);
    }

    protected Set<Class<?>> getAllTypes() {
        return reflections.getSubTypesOf(Object.class);
    }

    protected Set<Class> getInterfacesThatExtend(Class interfaceType) {
        return reflections.getSubTypesOf(interfaceType);
    }

    protected Class getFirstImplementationOfInterface(String interfaceTypeName) throws Exception {
        return getFirstImplementationOfInterface(Class.forName(interfaceTypeName));
    }

    protected Class getFirstImplementationOfInterface(Class interfaceType) throws Exception {
        Set<Class> implementationClasses = reflections.getSubTypesOf(interfaceType);

        if (implementationClasses.isEmpty()) {
            return null;
        } else {<commit_msg>Fixed a performance problem where all Java Platform types were being checked when finding component dependencies.<commit_after>package com.structurizr.componentfinder;

import com.google.common.base.Predicates;
import com.structurizr.model.Component;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.NotFoundException;
import org.reflections.ReflectionUtils;
import org.reflections.Reflections;
import org.reflections.scanners.FieldAnnotationsScanner;
import org.reflections.scanners.SubTypesScanner;
import org.reflections.scanners.TypeAnnotationsScanner;
import org.reflections.util.ClasspathHelper;
import org.reflections.util.ConfigurationBuilder;
import org.reflections.util.FilterBuilder;

import java.lang.annotation.Annotation;
import java.util.HashSet;
import java.util.Set;

public abstract class AbstractReflectionsComponentFinderStrategy extends ComponentFinderStrategy {

    protected Reflections reflections;

    public AbstractReflectionsComponentFinderStrategy() {
    }

    @Override
    public void setComponentFinder(ComponentFinder componentFinder) {
        super.setComponentFinder(componentFinder);

        this.reflections = new Reflections(new ConfigurationBuilder()
                  .filterInputsBy(new FilterBuilder().includePackage(componentFinder.getPackageToScan()))
                  .setUrls(ClasspathHelper.forPackage(componentFinder.getPackageToScan()))
                  .setScanners(new TypeAnnotationsScanner(), new SubTypesScanner(false), new FieldAnnotationsScanner()));
    }

    @Override
    public void findDependencies() throws Exception {
        for (Component component : componentFinder.getContainer().getComponents()) {
            if (component.getType() != null) {
                addEfferentDependencies(component, component.getType(), new HashSet<>());

                // and repeat for the first implementation class we can find
                ClassPool pool = ClassPool.getDefault();
                CtClass cc = pool.get(component.getType());
                if (cc.isInterface()) {
                    Class implementationType = getFirstImplementationOfInterface(cc.getName());
                    if (implementationType != null && implementationType.getCanonicalName() != null) {
                        addEfferentDependencies(component, implementationType.getCanonicalName(), new HashSet<>());
                    }
                }
            }
        }
    }

    private void addEfferentDependencies(Component component, String type, Set<String> typesVisited) {
        typesVisited.add(type);

        try {
            ClassPool pool = ClassPool.getDefault();
            CtClass cc = pool.get(type);
            for (Object referencedType : cc.getRefClasses()) {
                String referencedTypeName = (String)referencedType;

                if (!isAJavaPlatformType(referencedTypeName)) {
                    Component destinationComponent = componentFinder.getContainer().getComponentOfType(referencedTypeName);
                    if (destinationComponent != null) {
                        if (component != destinationComponent) {
                            component.uses(destinationComponent, "");
                        }
                    } else if (!typesVisited.contains(referencedTypeName)) {
                        addEfferentDependencies(component, referencedTypeName, typesVisited);
                    }
                }
            }
        } catch (NotFoundException nfe) {
            System.err.println(nfe.getMessage() + " not found");
        } catch (Exception e) {
            System.err.println(e.getMessage());
        }
    }

    private boolean isAJavaPlatformType(String typeName) {
        return  typeName.startsWith("java.") ||
                typeName.startsWith("javax.") ||
                typeName.startsWith("sun.");
    }

    protected Set<Class<?>> getTypesAnnotatedWith(Class<? extends Annotation> annotation) {
        return reflections.getTypesAnnotatedWith(annotation);
    }

    protected Set<Class<?>> getAllTypes() {
        return reflections.getSubTypesOf(Object.class);
    }

    protected Set<Class> getInterfacesThatExtend(Class interfaceType) {
        return reflections.getSubTypesOf(interfaceType);
    }

    protected Class getFirstImplementationOfInterface(String interfaceTypeName) throws Exception {
        return getFirstImplementationOfInterface(Class.forName(interfaceTypeName));
    }

    protected Class getFirstImplementationOfInterface(Class interfaceType) throws Exception {
        Set<Class> implementationClasses = reflections.getSubTypesOf(interfaceType);

        if (implementationClasses.isEmpty()) {
            return null;
        } else {
{'fraction_non_alphanumeric': 0.08720159151193634, 'fraction_numerical': 0.0, 'mean_word_length': 2.9122351923908343, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 23, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2376420', 'n_tokens_mistral': 2210, 'n_tokens_neox': 2125, 'n_words': 427}