001/**
002 * Copyright (C) 2006-2025 Talend Inc. - www.talend.com
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.talend.sdk.component.design.extension.repository;
017
018import static java.util.Collections.singletonList;
019import static java.util.function.Function.identity;
020import static java.util.stream.Collectors.toList;
021import static java.util.stream.Collectors.toMap;
022import static org.talend.sdk.component.runtime.manager.util.Lazy.lazy;
023
024import java.lang.reflect.Type;
025import java.util.ArrayList;
026import java.util.Collection;
027import java.util.LinkedHashMap;
028import java.util.List;
029import java.util.Map;
030import java.util.stream.Stream;
031
032import org.talend.sdk.component.api.component.MigrationHandler;
033import org.talend.sdk.component.api.component.Version;
034import org.talend.sdk.component.design.extension.RepositoryModel;
035import org.talend.sdk.component.runtime.manager.ComponentFamilyMeta;
036import org.talend.sdk.component.runtime.manager.ComponentManager;
037import org.talend.sdk.component.runtime.manager.ParameterMeta;
038import org.talend.sdk.component.runtime.manager.reflect.MigrationHandlerFactory;
039import org.talend.sdk.component.runtime.manager.util.IdGenerator;
040
041/**
042 * Copyright (C) 2006-2025 Talend Inc. - www.talend.com
043 * <p>
044 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
045 * use this file except in compliance with the License. You may obtain a copy of
046 * the License at
047 * <p>
048 * http://www.apache.org/licenses/LICENSE-2.0
049 * <p>
050 * Unless required by applicable law or agreed to in writing, software
051 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
052 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
053 * License for the specific language governing permissions and limitations under
054 * the License.
055 */
056public class RepositoryModelBuilder {
057
058    public RepositoryModel create(final ComponentManager.AllServices services,
059            final Collection<ComponentFamilyMeta> familyMetas, final MigrationHandlerFactory migrationHandlerFactory) {
060        final List<Family> families = familyMetas
061                .stream()
062                .map(familyMeta -> createConfigForFamily(services, migrationHandlerFactory, familyMeta))
063                .collect(toList());
064        return new RepositoryModel(families);
065    }
066
067    private Family createConfigForFamily(final ComponentManager.AllServices services,
068            final MigrationHandlerFactory migrationHandlerFactory, final ComponentFamilyMeta familyMeta) {
069        final Family family = new Family();
070        family.setId(familyMeta.getId());
071        family.setMeta(familyMeta);
072        family
073                .setConfigs(lazy(() -> extractConfigurations(services, migrationHandlerFactory, familyMeta)
074                        .values()
075                        .stream()
076                        .collect(ArrayList::new, (aggregator, item) -> {
077                            final Collection<Config> configs = aggregator
078                                    .stream()
079                                    .filter(c -> toParamStream(item.getMeta().getNestedParameters())
080                                            .anyMatch(p -> p.getJavaType() == c.getMeta().getJavaType()))
081                                    .findFirst()
082                                    .map(Config::getChildConfigs)
083                                    .orElse(aggregator);
084                            if (configs
085                                    .stream()
086                                    .noneMatch(c -> c.getMeta().getJavaType() == item.getMeta().getJavaType())) {
087                                configs.add(item);
088                            }
089                        }, List::addAll)));
090        return family;
091    }
092
093    private Map<Type, Config> extractConfigurations(final ComponentManager.AllServices services,
094            final MigrationHandlerFactory migrationHandlerFactory, final ComponentFamilyMeta familyMeta) {
095        return Stream
096                .of(familyMeta.getPartitionMappers().values().stream(), familyMeta.getProcessors().values().stream(),
097                        familyMeta.getDriverRunners().values().stream())
098                .flatMap(t -> t)
099                .flatMap(b -> b.getParameterMetas().get().stream())
100                .flatMap(this::flatten)
101                .filter(RepositoryModelBuilder::isConfiguration)
102                .map(p -> createConfig(familyMeta.getPlugin(), services, p, familyMeta.getName(), familyMeta.getIcon(),
103                        migrationHandlerFactory))
104                .collect(toMap(c -> c.getMeta().getJavaType(), identity(), (config1, config2) -> config1,
105                        LinkedHashMap::new));
106    }
107
108    private Stream<ParameterMeta> toParamStream(final Collection<ParameterMeta> params) {
109        if (params.isEmpty()) {
110            return Stream.empty();
111        }
112        return Stream.concat(params.stream(), params.stream().flatMap(p -> toParamStream(p.getNestedParameters())));
113    }
114
115    private Stream<ParameterMeta> flatten(final ParameterMeta meta) {
116        if (meta.getNestedParameters() == null || meta.getNestedParameters().isEmpty()) {
117            return Stream.of(meta);
118        }
119        return Stream.concat(meta.getNestedParameters().stream().flatMap(this::flatten), Stream.of(meta));
120    }
121
122    private Config createConfig(final String plugin, final ComponentManager.AllServices services,
123            final ParameterMeta config, final String familyName, final String familyIcon,
124            final MigrationHandlerFactory migrationHandlerFactory) {
125        final Config c = new Config();
126        c.setIcon(familyIcon);
127        c.setKey(getKey(familyName, config.getMetadata()));
128        c.setMeta(translate(config, config.getPath().length(), "configuration"));
129        c
130                .setId(IdGenerator
131                        .get(plugin, c.getKey().getFamily(), c.getKey().getConfigType(), c.getKey().getConfigName()));
132
133        if (Class.class.isInstance(config.getJavaType())) {
134            final Class<?> clazz = Class.class.cast(config.getJavaType());
135            final Version version = clazz.getAnnotation(Version.class);
136            if (version != null) {
137                c.setVersion(version.value());
138                if (version.migrationHandler() != MigrationHandler.class) {
139                    c
140                            .setMigrationHandler(migrationHandlerFactory
141                                    .findMigrationHandler(() -> singletonList(c.getMeta()), clazz, services));
142                }
143            } else {
144                c.setVersion(-1);
145            }
146        }
147        if (c.getMigrationHandler() == null) {
148            c.setMigrationHandler((v, d) -> d);
149        }
150
151        return c;
152    }
153
154    private ConfigKey getKey(final String family, final Map<String, String> meta) {
155        final String configName = meta.get("tcomp::configurationtype::name");
156        final String configType = meta.get("tcomp::configurationtype::type");
157        return new ConfigKey(family, configName, configType);
158    }
159
160    private static boolean isConfiguration(final ParameterMeta parameterMeta) {
161        return parameterMeta.getMetadata().keySet().stream().anyMatch(m -> m.startsWith("tcomp::configurationtype::"));
162    }
163
164    private ParameterMeta translate(final ParameterMeta config, final int replacedPrefixLen, final String newPrefix) {
165        return new ParameterMeta(config.getSource(), config.getJavaType(), config.getType(),
166                newPrefix + config.getPath().substring(replacedPrefixLen),
167                config.getPath().length() == replacedPrefixLen ? newPrefix : config.getName(), config.getI18nPackages(),
168                config
169                        .getNestedParameters()
170                        .stream()
171                        .map(it -> translate(it, replacedPrefixLen, newPrefix))
172                        .collect(toList()),
173                config.getProposals(), config.getMetadata(), config.isLogMissingResourceBundle());
174    }
175}