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

org.elasticsearch.cluster.DiffableUtils Maven / Gradle / Ivy

There is a newer version: 8.14.1
Show newest version
/*
 * Licensed to Elasticsearch under one or more contributor
 * license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright
 * ownership. Elasticsearch licenses this file to you 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 org.elasticsearch.cluster;

import com.carrotsearch.hppc.cursors.ObjectCursor;
import com.carrotsearch.hppc.cursors.ObjectObjectCursor;
import com.google.common.collect.ImmutableMap;
import org.elasticsearch.common.collect.ImmutableOpenMap;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.google.common.collect.Maps.newHashMap;

public final class DiffableUtils {
    private DiffableUtils() {
    }

    /**
     * Calculates diff between two ImmutableOpenMaps of Diffable objects
     */
    public static > Diff> diff(ImmutableOpenMap before, ImmutableOpenMap after) {
        assert after != null && before != null;
        return new ImmutableOpenMapDiff<>(before, after);
    }

    /**
     * Calculates diff between two ImmutableMaps of Diffable objects
     */
    public static > Diff> diff(ImmutableMap before, ImmutableMap after) {
        assert after != null && before != null;
        return new ImmutableMapDiff<>(before, after);
    }

    /**
     * Loads an object that represents difference between two ImmutableOpenMaps
     */
    public static > Diff> readImmutableOpenMapDiff(StreamInput in, KeyedReader keyedReader) throws IOException {
        return new ImmutableOpenMapDiff<>(in, keyedReader);
    }

    /**
     * Loads an object that represents difference between two ImmutableMaps
     */
    public static > Diff> readImmutableMapDiff(StreamInput in, KeyedReader keyedReader) throws IOException {
        return new ImmutableMapDiff<>(in, keyedReader);
    }

    /**
     * Loads an object that represents difference between two ImmutableOpenMaps
     */
    public static > Diff> readImmutableOpenMapDiff(StreamInput in, T proto) throws IOException {
        return new ImmutableOpenMapDiff<>(in, new PrototypeReader<>(proto));
    }

    /**
     * Loads an object that represents difference between two ImmutableMaps
     */
    public static > Diff> readImmutableMapDiff(StreamInput in, T proto) throws IOException {
        return new ImmutableMapDiff<>(in, new PrototypeReader<>(proto));
    }

    /**
     * A reader that can deserialize an object. The reader can select the deserialization type based on the key. It's
     * used in custom metadata deserialization.
     */
    public interface KeyedReader {

        /**
         * reads an object of the type T from the stream input
         */
        T readFrom(StreamInput in, String key) throws IOException;

        /**
         * reads an object that respresents differences between two objects with the type T from the stream input
         */
        Diff readDiffFrom(StreamInput in, String key) throws IOException;
    }

    /**
     * Implementation of the KeyedReader that is using a prototype object for reading operations
     *
     * Note: this implementation is ignoring the key.
     */
    public static class PrototypeReader> implements KeyedReader {
        private T proto;

        public PrototypeReader(T proto) {
            this.proto = proto;
        }

        @Override
        public T readFrom(StreamInput in, String key) throws IOException {
            return proto.readFrom(in);
        }

        @Override
        public Diff readDiffFrom(StreamInput in, String key) throws IOException {
            return proto.readDiffFrom(in);
        }
    }

    /**
     * Represents differences between two ImmutableMaps of diffable objects
     *
     * @param  the diffable object
     */
    private static class ImmutableMapDiff> extends MapDiff> {

        protected ImmutableMapDiff(StreamInput in, KeyedReader reader) throws IOException {
            super(in, reader);
        }

        public ImmutableMapDiff(ImmutableMap before, ImmutableMap after) {
            assert after != null && before != null;
            for (String key : before.keySet()) {
                if (!after.containsKey(key)) {
                    deletes.add(key);
                }
            }
            for (ImmutableMap.Entry partIter : after.entrySet()) {
                T beforePart = before.get(partIter.getKey());
                if (beforePart == null) {
                    adds.put(partIter.getKey(), partIter.getValue());
                } else if (partIter.getValue().equals(beforePart) == false) {
                    diffs.put(partIter.getKey(), partIter.getValue().diff(beforePart));
                }
            }
        }

        @Override
        public ImmutableMap apply(ImmutableMap map) {
            HashMap builder = newHashMap();
            builder.putAll(map);

            for (String part : deletes) {
                builder.remove(part);
            }

            for (Map.Entry> diff : diffs.entrySet()) {
                builder.put(diff.getKey(), diff.getValue().apply(builder.get(diff.getKey())));
            }

            for (Map.Entry additon : adds.entrySet()) {
                builder.put(additon.getKey(), additon.getValue());
            }
            return ImmutableMap.copyOf(builder);
        }
    }

    /**
     * Represents differences between two ImmutableOpenMap of diffable objects
     *
     * @param  the diffable object
     */
    private static class ImmutableOpenMapDiff> extends MapDiff> {

        protected ImmutableOpenMapDiff(StreamInput in, KeyedReader reader) throws IOException {
            super(in, reader);
        }

        public ImmutableOpenMapDiff(ImmutableOpenMap before, ImmutableOpenMap after) {
            assert after != null && before != null;
            for (ObjectCursor key : before.keys()) {
                if (!after.containsKey(key.value)) {
                    deletes.add(key.value);
                }
            }
            for (ObjectObjectCursor partIter : after) {
                T beforePart = before.get(partIter.key);
                if (beforePart == null) {
                    adds.put(partIter.key, partIter.value);
                } else if (partIter.value.equals(beforePart) == false) {
                    diffs.put(partIter.key, partIter.value.diff(beforePart));
                }
            }
        }

        @Override
        public ImmutableOpenMap apply(ImmutableOpenMap map) {
            ImmutableOpenMap.Builder builder = ImmutableOpenMap.builder();
            builder.putAll(map);

            for (String part : deletes) {
                builder.remove(part);
            }

            for (Map.Entry> diff : diffs.entrySet()) {
                builder.put(diff.getKey(), diff.getValue().apply(builder.get(diff.getKey())));
            }

            for (Map.Entry additon : adds.entrySet()) {
                builder.put(additon.getKey(), additon.getValue());
            }
            return builder.build();
        }
    }

    /**
     * Represents differences between two maps of diffable objects
     *
     * This class is used as base class for different map implementations
     *
     * @param  the diffable object
     */
    private static abstract class MapDiff, M> implements Diff {

        protected final List deletes;
        protected final Map> diffs;
        protected final Map adds;

        protected MapDiff() {
            deletes = new ArrayList<>();
            diffs = newHashMap();
            adds = newHashMap();
        }

        protected MapDiff(StreamInput in, KeyedReader reader) throws IOException {
            deletes = new ArrayList<>();
            diffs = newHashMap();
            adds = newHashMap();
            int deletesCount = in.readVInt();
            for (int i = 0; i < deletesCount; i++) {
                deletes.add(in.readString());
            }

            int diffsCount = in.readVInt();
            for (int i = 0; i < diffsCount; i++) {
                String key = in.readString();
                Diff diff = reader.readDiffFrom(in, key);
                diffs.put(key, diff);
            }

            int addsCount = in.readVInt();
            for (int i = 0; i < addsCount; i++) {
                String key = in.readString();
                T part = reader.readFrom(in, key);
                adds.put(key, part);
            }
        }
        @Override
        public void writeTo(StreamOutput out) throws IOException {
            out.writeVInt(deletes.size());
            for (String delete : deletes) {
                out.writeString(delete);
            }

            out.writeVInt(diffs.size());
            for (Map.Entry> entry : diffs.entrySet()) {
                out.writeString(entry.getKey());
                entry.getValue().writeTo(out);
            }

            out.writeVInt(adds.size());
            for (Map.Entry entry : adds.entrySet()) {
                out.writeString(entry.getKey());
                entry.getValue().writeTo(out);
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy