org.elasticsearch.cluster.DiffableUtils Maven / Gradle / Ivy
The 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);
}
}
}
}