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