Package fr.adrienbrault.idea.symfony2plugin.stubs

Source Code of fr.adrienbrault.idea.symfony2plugin.stubs.ContainerCollectionResolver$ParameterCollector

package fr.adrienbrault.idea.symfony2plugin.stubs;

import com.intellij.ide.highlighter.XmlFileType;
import com.intellij.openapi.project.Project;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.util.indexing.FileBasedIndexImpl;
import fr.adrienbrault.idea.symfony2plugin.config.component.parser.ParameterServiceParser;
import fr.adrienbrault.idea.symfony2plugin.dic.ContainerParameter;
import fr.adrienbrault.idea.symfony2plugin.dic.ContainerService;
import fr.adrienbrault.idea.symfony2plugin.dic.XmlServiceParser;
import fr.adrienbrault.idea.symfony2plugin.stubs.indexes.ContainerParameterStubIndex;
import fr.adrienbrault.idea.symfony2plugin.stubs.indexes.ServicesDefinitionStubIndex;
import fr.adrienbrault.idea.symfony2plugin.util.service.ServiceXmlParserFactory;
import org.apache.commons.lang.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.yaml.YAMLFileType;

import java.util.*;

public class ContainerCollectionResolver {

    public static enum Source {
        INDEX, COMPILER
    }

    public static Collection<String> getServiceNames(Project project) {
        return new ServiceCollector(project, ContainerCollectionResolver.Source.COMPILER, ContainerCollectionResolver.Source.INDEX).getNames();
    }

    public static boolean hasServiceNames(Project project, String serviceName) {
        // @TODO: we dont need a collection here; stop on first match
        return new ServiceCollector(project, ContainerCollectionResolver.Source.COMPILER, ContainerCollectionResolver.Source.INDEX).getNames().contains(serviceName);
    }

    @Nullable
    public static ContainerService getService(Project project, String serviceName) {
        Map<String, ContainerService> services = getServices(project, Source.COMPILER, Source.INDEX);
        return services.containsKey(serviceName) ? services.get(serviceName) : null;
    }

    public static Map<String, ContainerService> getServices(Project project) {
        return getServices(project, ContainerCollectionResolver.Source.COMPILER, ContainerCollectionResolver.Source.INDEX);
    }

    public static Map<String, ContainerService> getServices(Project project, Source... collectorSources) {
        return new ServiceCollector(project, collectorSources).getServices();
    }

    @Nullable
    public static String resolveService(Project project, String serviceName) {
        return resolveService(project, serviceName, ContainerCollectionResolver.Source.COMPILER, ContainerCollectionResolver.Source.INDEX);
    }

    @Nullable
    public static String resolveService(Project project, String serviceName, ContainerCollectionResolver.Source... collectorSources) {
        return new ServiceCollector(project, collectorSources).resolve(serviceName);
    }


    private static GlobalSearchScope getSearchScope(Project project) {
        return GlobalSearchScope.allScope(project);
    }

    /**
     *
     * Resolve service class name which can be a class name or parameter, unknown parameter returns null
     *
     * @param project project
     * @param paramOrClassName any raw class name or parameter name
     * @return class name or unchanged item
     */
    @Nullable
    public static String resolveParameter(Project project, String paramOrClassName) {
        return new ParameterCollector(project, Source.COMPILER, Source.INDEX).resolve(paramOrClassName);
    }

    public static Map<String, ContainerParameter> getParameters(Project project) {
        return getParameters(project, ContainerCollectionResolver.Source.COMPILER, ContainerCollectionResolver.Source.INDEX);
    }

    public static Map<String, ContainerParameter> getParameters(Project project, ContainerCollectionResolver.Source... collectorSources) {
        return new ParameterCollector(project, collectorSources).getParameters();
    }

    public static Set<String> getParameterNames(Project project) {
        return getParameterNames(project, ContainerCollectionResolver.Source.COMPILER, ContainerCollectionResolver.Source.INDEX);
    }

    public static Set<String> getParameterNames(Project project, ContainerCollectionResolver.Source... collectorSources) {
        return new ParameterCollector(project, collectorSources).getNames();
    }

    public static class ServiceCollector {

        private Set<Source> sources = new HashSet<Source>();
        private Project project;
        private ParameterCollector parameterCollector;
        private Map<String, ContainerService> services;

        public ServiceCollector(Project project, Source... sources) {
            this(project);
            this.sources = new HashSet<Source>(Arrays.asList(sources));
        }

        public ServiceCollector(Project project) {
            this.project = project;
        }

        public void addCollectorSource(Source source) {
            this.sources.add(source);
        }

        public Collection<ContainerService> collect() {
            return this.getServices().values();
        }

        @Nullable
        public String resolve(String serviceName) {

            if(this.getServices().containsKey(serviceName)) {

                // service can be a parameter, resolve if necessary
                String className = this.getServices().get(serviceName).getClassName();
                if(className != null && className.startsWith("%") && className.endsWith("%")) {
                    return getParameterCollector().resolve(className);
                } else {
                    return className;
                }
            }

            return null;
        }

        public Map<String, ContainerService> getServices() {

            if(this.services != null) {
                return this.services;
            }

            this.services = new HashMap<String, ContainerService>();

            if(this.sources.contains(Source.COMPILER)) {
                for(Map.Entry<String, String> entry: ServiceXmlParserFactory.getInstance(project, XmlServiceParser.class).getServiceMap().getMap().entrySet()) {
                    services.put(entry.getKey(), new ContainerService(entry.getKey(), entry.getValue()));
                }
            }

            if(this.sources.contains(Source.INDEX)) {
                SymfonyProcessors.CollectProjectUniqueKeysStrong projectUniqueKeysStrong = new SymfonyProcessors.CollectProjectUniqueKeysStrong(project, ServicesDefinitionStubIndex.KEY, this.services.keySet());
                FileBasedIndexImpl.getInstance().processAllKeys(ServicesDefinitionStubIndex.KEY, projectUniqueKeysStrong, project);

                for(String serviceName: projectUniqueKeysStrong.getResult()) {

                    // we have higher priority on compiler, which already has safe value
                    if(!this.services.containsKey(serviceName)) {

                        List<String[]> serviceDefinitions = FileBasedIndexImpl.getInstance().getValues(ServicesDefinitionStubIndex.KEY, serviceName, GlobalSearchScope.getScopeRestrictedByFileTypes(GlobalSearchScope.allScope(project), XmlFileType.INSTANCE, YAMLFileType.YML));

                        if(serviceDefinitions.size() == 0) {
                            this.services.put(serviceName, new ContainerService(serviceName, null, true));
                        } else {
                            this.services.putAll(convertIndexToService(serviceName, serviceDefinitions));
                        }


                    }

                }
            }


            return this.services;
        }

        private Map<String, ContainerService> convertIndexToService(String serviceName, List<String[]> serviceDefinitions) {

            Map<String, ContainerService> serviceMap = new HashMap<String, ContainerService>();

            for(String[] serviceDefinitionArray: serviceDefinitions) {

                // 0: class name
                // 1: private: (String) "true" if presented
                if(serviceDefinitionArray.length == 0) {
                    // just a fallback should not happen, but provide at least a service name
                    serviceMap.put(serviceName, new ContainerService(serviceName, null, true));
                } else {

                    // resolve class value, it can be null or a parameter
                    String classValue = serviceDefinitionArray[0];
                    if(classValue != null && !classValue.equals("")) {
                        classValue = getParameterCollector().resolve(serviceDefinitionArray[0]);
                    }

                    if(serviceDefinitionArray.length == 1) {
                        serviceMap.put(serviceName, new ContainerService(serviceName, classValue, true));
                    }

                    if(serviceDefinitionArray.length == 2) {
                        serviceMap.put(serviceName, new ContainerService(serviceName, classValue, true, "true".equals(serviceDefinitionArray[1])));
                    }

                }

            }

            return serviceMap;
        }

        public Set<String> convertClassNameToServices(@NotNull String fqnClassName) {

            Set<String> serviceNames = new HashSet<String>();

            // normalize class name; prepend "\"
            if(!fqnClassName.startsWith("\\")) {
                fqnClassName = "\\" + fqnClassName;
            }

            for(Map.Entry<String, ContainerService> entry: this.getServices().entrySet()) {
                if(entry.getValue().getClassName() != null) {
                    String indexedClassName = this.getParameterCollector().resolve(entry.getValue().getClassName());
                    if(indexedClassName != null) {

                        // also normalize user input string inside container
                        if(!indexedClassName.startsWith("\\")) {
                            indexedClassName = "\\" + indexedClassName;
                        }

                        if(indexedClassName.equals(fqnClassName)) {
                            serviceNames.add(entry.getKey());
                        }
                    }

                }

            }

            return serviceNames;
        }

        private Set<String> getNames() {

            Set<String> serviceNames = new HashSet<String>();

            if(this.sources.contains(Source.COMPILER)) {
                serviceNames.addAll(ServiceXmlParserFactory.getInstance(project, XmlServiceParser.class).getServiceMap().getMap().keySet());
            }

            if(this.sources.contains(Source.INDEX)) {
                SymfonyProcessors.CollectProjectUniqueKeysStrong projectUniqueKeysStrong = new SymfonyProcessors.CollectProjectUniqueKeysStrong(project, ServicesDefinitionStubIndex.KEY, serviceNames);
                FileBasedIndexImpl.getInstance().processAllKeys(ServicesDefinitionStubIndex.KEY, projectUniqueKeysStrong, project);
                serviceNames.addAll(projectUniqueKeysStrong.getResult());
            }

            return serviceNames;

        }


        private ParameterCollector getParameterCollector() {
            return (this.parameterCollector != null) ? this.parameterCollector : (this.parameterCollector = new ParameterCollector(this.project, this.sources.toArray(new Source[this.sources.size()])));
        }

    }

    public static class ParameterCollector {

        private Set<Source> sources = new HashSet<Source>();
        private Project project;
        private Map<String, ContainerParameter> containerParameterMap;

        public ParameterCollector(Project project) {
            this.project = project;
        }

        public ParameterCollector(Project project, Source... sources) {
            this(project);
            this.sources = new HashSet<Source>(Arrays.asList(sources));
        }

        /**
         *
         * Resolve service class name which can be a class name or parameter, unknown parameter returns null
         *
         */
        @Nullable
        private String resolve(@Nullable String paramOrClassName) {

            if(paramOrClassName == null) {
                return null;
            }

            // strip "%" to get the parameter name
            if(paramOrClassName.length() > 1 && paramOrClassName.startsWith("%") && paramOrClassName.endsWith("%")) {

                paramOrClassName = paramOrClassName.substring(1, paramOrClassName.length() - 1);

                // parameter is always lower see #179
                paramOrClassName = paramOrClassName.toLowerCase();

                if(this.getParameters().containsKey(paramOrClassName)) {
                    return getParameters().get(paramOrClassName).getValue();
                }

                return null;
            }

            return paramOrClassName;
        }


        private Map<String, ContainerParameter> getParameters() {

            if(this.containerParameterMap != null) {
                return this.containerParameterMap;
            }

            this.containerParameterMap = new HashMap<String, ContainerParameter>();

            if(this.sources.contains(Source.COMPILER)) {
                for(Map.Entry<String, String> Entry: ServiceXmlParserFactory.getInstance(project, ParameterServiceParser.class).getParameterMap().entrySet()) {

                    // user input here; secure nullable values
                    String key = Entry.getKey();
                    if(key != null) {
                        this.containerParameterMap.put(key, new ContainerParameter(key, Entry.getValue()));
                    }

                }
            }

            if(this.sources.contains(Source.INDEX)) {
                SymfonyProcessors.CollectProjectUniqueKeysStrong projectUniqueKeysStrong = new SymfonyProcessors.CollectProjectUniqueKeysStrong(project, ContainerParameterStubIndex.KEY, this.containerParameterMap.keySet());
                FileBasedIndexImpl.getInstance().processAllKeys(ContainerParameterStubIndex.KEY, projectUniqueKeysStrong, project);

                for(String parameterName: projectUniqueKeysStrong.getResult()) {

                    // just for secure
                    if(parameterName == null) {
                        continue;
                    }

                    // indexes is weak stuff, dont overwrite compiled ones
                    if(!this.containerParameterMap.containsKey(parameterName)) {
                        List<String> parameterValues = FileBasedIndexImpl.getInstance().getValues(ContainerParameterStubIndex.KEY, parameterName, getSearchScope(project));
                        this.containerParameterMap.put(parameterName, new ContainerParameter(parameterName, parameterValues, true));
                    }
                }

            }

            return this.containerParameterMap;

        }

        private Set<String> getNames() {

            // use overall map if already generated
            if(this.containerParameterMap != null) {
                return this.containerParameterMap.keySet();
            }

            Set<String> parameterNames = new HashSet<String>();

            if(this.sources.contains(Source.COMPILER)) {
                parameterNames.addAll(ServiceXmlParserFactory.getInstance(project, ParameterServiceParser.class).getParameterMap().keySet());
            }

            if(this.sources.contains(Source.INDEX)) {
                SymfonyProcessors.CollectProjectUniqueKeysStrong projectUniqueKeysStrong = new SymfonyProcessors.CollectProjectUniqueKeysStrong(project, ContainerParameterStubIndex.KEY, parameterNames);
                FileBasedIndexImpl.getInstance().processAllKeys(ContainerParameterStubIndex.KEY, projectUniqueKeysStrong, project);
                parameterNames.addAll(projectUniqueKeysStrong.getResult());
            }

            return parameterNames;


        }

    }

}
TOP

Related Classes of fr.adrienbrault.idea.symfony2plugin.stubs.ContainerCollectionResolver$ParameterCollector

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.