All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.fluxcapacitor.javaclient.common.serialization.upcasting.UpcasterChain Maven / Gradle / Ivy

There is a newer version: 0.1015.0
Show newest version
/*
 * Copyright (c) 2016-2017 Flux Capacitor.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *     http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.fluxcapacitor.javaclient.common.serialization.upcasting;

import io.fluxcapacitor.common.api.Data;
import io.fluxcapacitor.common.api.SerializedObject;
import io.fluxcapacitor.javaclient.common.serialization.SerializationException;
import lombok.AllArgsConstructor;
import lombok.Value;
import lombok.experimental.Wither;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Stream;

import static java.lang.String.format;
import static java.util.function.Function.identity;
import static java.util.stream.Collectors.toMap;

public class UpcasterChain {

    public static  Upcaster> create(Collection upcasters, Converter converter) {
        if (upcasters.isEmpty()) {
            return s -> s;
        }
        Upcaster> upcasterChain = create(upcasters, converter.getDataType());
        return stream -> {
            Stream> converted = stream.map(s -> new ConvertingSerializedObject<>(s, converter));
            Stream> upcasted = upcasterChain.upcast(converted);
            return upcasted.map(ConvertingSerializedObject::getResult);
        };
    }

    protected static > Upcaster create(Collection upcasters, Class dataType) {
        if (upcasters.isEmpty()) {
            return s -> s;
        }
        List> upcasterList = UpcastInspector.inspect(upcasters, dataType);
        UpcasterChain upcasterChain = new UpcasterChain<>(upcasterList);
        return upcasterChain::upcast;
    }

    private final Map> upcasters;

    protected UpcasterChain(Collection> upcasters) {
        this.upcasters =
                upcasters.stream().collect(toMap(u -> new DataRevision(u.getAnnotation()), identity(), (a, b) -> {
                    throw new SerializationException(
                            format("Failed to create upcaster chain. Methods '%s' and '%s' both apply to the same data revision.",
                                   a, b));
                }));
    }

    protected > Stream upcast(Stream input) {
        return input.flatMap(i -> Optional.ofNullable(upcasters.get(new DataRevision(i.data())))
                .map(upcaster -> upcast(upcaster.upcast(i)))
                .orElse(Stream.of(i)));
    }

    @Value
    @AllArgsConstructor
    protected static class DataRevision {
        String type;
        int revision;

        protected DataRevision(Data data) {
            this(data.getType(), data.getRevision());
        }

        protected DataRevision(Upcast annotation) {
            this(annotation.type(), annotation.revision());
        }
    }

    @AllArgsConstructor
    protected static class ConvertingSerializedObject implements SerializedObject> {

        private final SerializedObject source;
        private final Converter converter;
        @Wither
        private Data data;

        public ConvertingSerializedObject(SerializedObject source, Converter converter) {
            this.source = source;
            this.converter = converter;
            this.data = converter.convert(source.data());
        }

        @Override
        public Data data() {
            return data;
        }

        public SerializedObject getResult() {
            return source.withData(converter.convertBack(data));
        }
    }
}