001package io.jstach.jstachio.spi;
002
003import java.util.ArrayList;
004import java.util.List;
005import java.util.Optional;
006import java.util.ServiceLoader;
007
008import org.eclipse.jdt.annotation.Nullable;
009
010/**
011 * A container that will hold all resolved {@link JStachioExtension}s and consolidate them
012 * to a single instances of various services.
013 *
014 * @apiNote While this interface looks similar to {@link JStachioExtension} it is not an
015 * extension but rather an immutable bean like container. The methods are purposely java
016 * bean style (which is not the default in JStachio as JStachio prefers newer record like
017 * accessor method names) to support as many frameworks as possible.
018 * @author agentgt
019 */
020public interface JStachioExtensions {
021
022        /**
023         * A marker interface used for JStachio implementations that provide access to
024         * extensions.
025         *
026         * @author agentgt
027         *
028         */
029        public interface Provider {
030
031                /**
032                 * The available extensions.
033                 * @return The avaiable resolved extensions.
034                 */
035                public JStachioExtensions extensions();
036
037        }
038
039        /**
040         * Resolve from an iterable of extensions that usually come from some discovery
041         * mechanism like the {@link ServiceLoader} or a DI framework.
042         * @param extensions found extensions.
043         * @return bean like container of services.
044         */
045        public static JStachioExtensions of(Iterable<JStachioExtension> extensions) {
046                return DefaultJStachioExtensions.of(extensions);
047        }
048
049        /**
050         * Composite Config
051         * @return config
052         */
053        JStachioConfig getConfig();
054
055        /**
056         * Composite Filter
057         * @return filter
058         */
059        JStachioFilter getFilter();
060
061        /**
062         * Composite Template finder
063         * @return template finder
064         */
065        JStachioTemplateFinder getTemplateFinder();
066
067        /**
068         * Services
069         * @return found services
070         */
071        List<JStachioExtension> getExtensions();
072
073        /**
074         * Finds a specific implementation using {@link Class#isAssignableFrom(Class)}.
075         * @param <T> the implementation type
076         * @param c the implementation type.
077         * @return an implementation if found
078         */
079        default <T extends JStachioExtension> Optional<T> findExtension(Class<T> c) {
080                return getExtensions().stream().filter(s -> c.isAssignableFrom(s.getClass())).map(c::cast).findFirst();
081        }
082
083}
084
085class DefaultJStachioExtensions implements JStachioExtensions {
086
087        private final List<JStachioExtension> services;
088
089        private final JStachioConfig config;
090
091        private final JStachioFilter filter;
092
093        private final JStachioTemplateFinder templateFinder;
094
095        private DefaultJStachioExtensions(List<JStachioExtension> services, JStachioConfig config, JStachioFilter filter,
096                        JStachioTemplateFinder templateFinder) {
097                super();
098                this.services = services;
099                this.config = config;
100                this.filter = filter;
101                this.templateFinder = templateFinder;
102        }
103
104        /**
105         * Create a container from service providers.
106         * @param it services
107         * @return bean like container of services.
108         */
109        static JStachioExtensions of(Iterable<JStachioExtension> it) {
110                List<JStachioExtensionProvider> svs = new ArrayList<>();
111                it.forEach(s -> svs.add(JStachioExtensionProvider.of(s)));
112
113                List<JStachioConfig> configs = new ArrayList<>();
114                List<JStachioFilter> filters = new ArrayList<>();
115                List<JStachioTemplateFinder> finders = new ArrayList<>();
116
117                for (var sv : svs) {
118                        var c = sv.provideConfig();
119                        if (c != null) {
120                                configs.add(c);
121                        }
122                }
123                JStachioConfig config = configs.isEmpty() ? SystemPropertyConfig.INSTANCE : new CompositeConfig(configs);
124
125                for (var sv : svs) {
126                        sv.init(config);
127                        @Nullable
128                        JStachioFilter filt = sv.provideFilter();
129                        if (filt != null) {
130                                filters.add(filt);
131                        }
132                        @Nullable
133                        JStachioTemplateFinder find = sv.provideTemplateFinder();
134                        if (find != null) {
135                                finders.add(find);
136                        }
137                }
138                JStachioFilter filter = JStachioFilter.compose(filters);
139                if (finders.isEmpty()) {
140                        finders.add(
141                                        JStachioTemplateFinder.cachedTemplateFinder(JStachioTemplateFinder.defaultTemplateFinder(config)));
142                }
143                JStachioTemplateFinder templateFinder = CompositeTemplateFinder.of(finders);
144                return new DefaultJStachioExtensions(List.copyOf(svs), config, filter, templateFinder);
145        }
146
147        /**
148         * Composite Config
149         * @return config
150         */
151        @Override
152        public JStachioConfig getConfig() {
153                return config;
154        }
155
156        /**
157         * Composite Filter
158         * @return filter
159         */
160        @Override
161        public JStachioFilter getFilter() {
162                return filter;
163        }
164
165        /**
166         * Composite Template finder
167         * @return template finder
168         */
169        @Override
170        public JStachioTemplateFinder getTemplateFinder() {
171                return templateFinder;
172        }
173
174        /**
175         * Services
176         * @return found services
177         */
178        @Override
179        public List<JStachioExtension> getExtensions() {
180                return services;
181        }
182
183}