/* * Copyright 2016-present Open Networking Laboratory * * 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 org.onosproject.store.primitives; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; import java.util.Objects; import java.util.function.Function; import org.onlab.util.ByteArraySizeHashPrinter; import com.google.common.base.MoreObjects; /** * Map update operation. * * @param <K> map key type * @param <V> map value type */ public final class MapUpdate<K, V> { /** * Type of database update operation. */ public enum Type { /** * Acquires a read lock on a key. * <p> * This record type will check to ensure that the lock version matches the current version for the key * in the map and acquire a read lock on the key for the duration of the transaction. */ LOCK, /** * Checks the version of a key without locking the key. * <p> * This record type will perform a simple version check during the prepare phase of the two-phase commit * protocol to ensure that the key has not changed during a transaction. */ VERSION_MATCH, /** * Updates an entry if the current version matches specified version. */ PUT_IF_VERSION_MATCH, /** * Removes an entry if the current version matches specified version. */ REMOVE_IF_VERSION_MATCH, } private Type type; private K key; private V value; private long version = -1; /** * Returns the type of update operation. * @return type of update. */ public Type type() { return type; } /** * Returns the item key being updated. * @return item key */ public K key() { return key; } /** * Returns the new value. * @return item's target value. */ public V value() { return value; } /** * Returns the expected current version in the database for the key. * @return expected version. */ public long version() { return version; } /** * Transforms this instance into an instance of different paramterized types. * * @param keyMapper transcoder for key type * @param valueMapper transcoder to value type * @return new instance * @param <S> key type of returned instance * @param <T> value type of returned instance */ public <S, T> MapUpdate<S, T> map(Function<K, S> keyMapper, Function<V, T> valueMapper) { return MapUpdate.<S, T>newBuilder() .withType(type) .withKey(keyMapper.apply(key)) .withValue(value == null ? null : valueMapper.apply(value)) .withVersion(version) .build(); } @Override public int hashCode() { return Objects.hash(type, key, value, version); } @Override public boolean equals(Object object) { if (object instanceof MapUpdate) { MapUpdate that = (MapUpdate) object; return this.type == that.type && Objects.equals(this.key, that.key) && Objects.equals(this.value, that.value) && Objects.equals(this.version, that.version); } return false; } @Override public String toString() { return MoreObjects.toStringHelper(this) .add("type", type) .add("key", key) .add("value", value instanceof byte[] ? new ByteArraySizeHashPrinter((byte[]) value) : value) .add("version", version) .toString(); } /** * Creates a new builder instance. * * @param <K> key type * @param <V> value type * @return builder. */ public static <K, V> Builder<K, V> newBuilder() { return new Builder<>(); } /** * MapUpdate builder. * * @param <K> key type * @param <V> value type */ public static final class Builder<K, V> { private MapUpdate<K, V> update = new MapUpdate<>(); public MapUpdate<K, V> build() { validateInputs(); return update; } public Builder<K, V> withType(Type type) { update.type = checkNotNull(type, "type cannot be null"); return this; } public Builder<K, V> withKey(K key) { update.key = checkNotNull(key, "key cannot be null"); return this; } public Builder<K, V> withValue(V value) { update.value = value; return this; } public Builder<K, V> withVersion(long version) { update.version = version; return this; } private void validateInputs() { checkNotNull(update.type, "type must be specified"); switch (update.type) { case VERSION_MATCH: break; case LOCK: checkNotNull(update.key, "key must be specified"); checkState(update.version >= 0, "version must be specified"); break; case PUT_IF_VERSION_MATCH: checkNotNull(update.key, "key must be specified"); checkNotNull(update.value, "value must be specified."); checkState(update.version >= 0, "version must be specified"); break; case REMOVE_IF_VERSION_MATCH: checkNotNull(update.key, "key must be specified"); checkState(update.version >= 0, "version must be specified"); break; default: throw new IllegalStateException("Unknown operation type"); } } } }