001/**
002 * Copyright (C) 2006-2022 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.junit;
017
018import static java.lang.Character.toUpperCase;
019import static java.util.Collections.emptyList;
020import static java.util.Collections.emptyMap;
021import static java.util.Collections.singletonList;
022import static java.util.Comparator.comparing;
023import static java.util.Optional.ofNullable;
024import static lombok.AccessLevel.PRIVATE;
025
026import java.io.ByteArrayOutputStream;
027import java.lang.annotation.Annotation;
028import java.lang.reflect.Type;
029import java.nio.charset.StandardCharsets;
030import java.util.ArrayList;
031import java.util.Collection;
032import java.util.Map;
033import java.util.stream.Collectors;
034
035import org.talend.sdk.component.runtime.manager.ParameterMeta;
036import org.talend.sdk.component.runtime.manager.configuration.ConfigurationMapper;
037import org.talend.sdk.component.runtime.manager.reflect.ParameterModelService;
038import org.talend.sdk.component.runtime.manager.reflect.parameterenricher.BaseParameterEnricher;
039import org.talend.sdk.component.runtime.manager.service.LocalConfigurationService;
040import org.talend.sdk.component.runtime.manager.xbean.registry.EnrichedPropertyEditorRegistry;
041
042import lombok.AllArgsConstructor;
043import lombok.NoArgsConstructor;
044
045@NoArgsConstructor(access = PRIVATE)
046public class SimpleFactory {
047
048    public static ByExample configurationByExample() {
049        return new ByExample();
050    }
051
052    public static <T> Map<String, String> configurationByExample(final T instance) {
053        return configurationByExample().forInstance(instance).configured().toMap();
054    }
055
056    public static <T> Map<String, String> configurationByExample(final T instance, final String prefix) {
057        return configurationByExample().forInstance(instance).withPrefix(prefix).configured().toMap();
058    }
059
060    private static class SimpleParameterModelService extends ParameterModelService {
061
062        public SimpleParameterModelService() {
063            super(new EnrichedPropertyEditorRegistry());
064        }
065
066        private ParameterMeta build(final String name, final String prefix, final Type genericType,
067                final Annotation[] annotations, final Collection<String> i18nPackages) {
068            return super.buildParameter(name, prefix, null, genericType, annotations, i18nPackages, false,
069                    new BaseParameterEnricher.Context(new LocalConfigurationService(emptyList(), "test")));
070        }
071    }
072
073    @NoArgsConstructor(access = PRIVATE)
074    public static class ByExample {
075
076        private String prefix;
077
078        private Object instance;
079
080        public ByExample withPrefix(final String prefix) {
081            this.prefix = prefix;
082            return this;
083        }
084
085        public <T> ByExample forInstance(final T instance) {
086            this.instance = instance;
087            return this;
088        }
089
090        public ConfigurationByExample configured() {
091            return new ConfigurationByExample(this);
092        }
093    }
094
095    @AllArgsConstructor(access = PRIVATE)
096    public static class ConfigurationByExample {
097
098        private static final ConfigurationMapper CONFIGURATION_MAPPER = new ConfigurationMapper();
099
100        private final ByExample byExample;
101
102        public Map<String, String> toMap() {
103            if (byExample.instance == null) {
104                return emptyMap();
105            }
106            final String usedPrefix = ofNullable(byExample.prefix).orElse("configuration.");
107            final ParameterMeta params = new SimpleParameterModelService()
108                    .build(usedPrefix, usedPrefix, byExample.instance.getClass(), new Annotation[0],
109                            new ArrayList<>(singletonList(byExample.instance.getClass().getPackage().getName())));
110            return CONFIGURATION_MAPPER.map(params.getNestedParameters(), byExample.instance);
111        }
112
113        public String toQueryString() {
114            return toMap()
115                    .entrySet()
116                    .stream()
117                    .sorted(comparing(Map.Entry::getKey))
118                    .map(entry -> entry.getKey() + "=" + encode(entry.getValue()))
119                    .collect(Collectors.joining("&"));
120        }
121
122        private static String encode(final String source) {
123            final byte[] bytes = source.getBytes(StandardCharsets.UTF_8);
124            final ByteArrayOutputStream bos = new ByteArrayOutputStream(bytes.length);
125            boolean changed = false;
126            for (final byte rawByte : bytes) {
127                byte b = rawByte;
128                if (b < 0) {
129                    b += 256;
130                }
131                if ((b >= 'a' && b <= 'z' || b >= 'A' && b <= 'Z') || (b >= '0' && b <= '9') || '-' == b || '.' == b
132                        || '_' == b || '~' == b) {
133                    bos.write(b);
134                } else {
135                    bos.write('%');
136                    char hex1 = toUpperCase(Character.forDigit((b >> 4) & 0xF, 16));
137                    char hex2 = toUpperCase(Character.forDigit(b & 0xF, 16));
138                    bos.write(hex1);
139                    bos.write(hex2);
140                    changed = true;
141                }
142            }
143            return changed ? new String(bos.toByteArray(), StandardCharsets.UTF_8) : source;
144        }
145    }
146}