/* * Copyright 2016 the original author or authors. * * 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.springframework.data.redis.core; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; /** * {@link PartialUpdate} allows to issue individual property updates without the need of rewriting the whole entity. It * allows to define {@literal set}, {@literal delete} actions on existing objects while taking care of updating * potential expiration times of the entity itself as well as index structures. * * @author Christoph Strobl * @param <T> * @since 1.8 */ public class PartialUpdate<T> { private final Object id; private final Class<T> target; private final T value; private boolean refreshTtl = false; private final List<PropertyUpdate> propertyUpdates = new ArrayList<PropertyUpdate>(); private PartialUpdate(Object id, Class<T> target, T value, boolean refreshTtl, List<PropertyUpdate> propertyUpdates) { this.id = id; this.target = target; this.value = value; this.refreshTtl = refreshTtl; this.propertyUpdates.addAll(propertyUpdates); } /** * Create new {@link PartialUpdate} for given id and type. * * @param id must not be {@literal null}. * @param targetType must not be {@literal null}. */ @SuppressWarnings("unchecked") public PartialUpdate(Object id, Class<T> targetType) { Assert.notNull(id, "Id must not be null!"); Assert.notNull(targetType, "TargetType must not be null!"); this.id = id; this.target = (Class<T>) ClassUtils.getUserClass(targetType); this.value = null; } /** * Create new {@link PartialUpdate} for given id and object. * * @param id must not be {@literal null}. * @param value must not be {@literal null}. */ @SuppressWarnings("unchecked") public PartialUpdate(Object id, T value) { Assert.notNull(id, "Id must not be null!"); Assert.notNull(value, "Value must not be null!"); this.id = id; this.target = (Class<T>) ClassUtils.getUserClass(value.getClass()); this.value = value; } /** * Create new {@link PartialUpdate} for given id and type. * * @param id must not be {@literal null}. * @param targetType must not be {@literal null}. */ public static <S> PartialUpdate<S> newPartialUpdate(Object id, Class<S> targetType) { return new PartialUpdate<S>(id, targetType); } /** * @return can be {@literal null}. */ public T getValue() { return value; } /** * Set the value of a simple or complex {@literal value} reachable via given {@literal path}. * * @param path must not be {@literal null}. * @param value must not be {@literal null}. If you want to remove a value use {@link #del(String)}. * @return a new {@link PartialUpdate}. */ public PartialUpdate<T> set(String path, Object value) { Assert.hasText(path, "Path to set must not be null or empty!"); PartialUpdate<T> update = new PartialUpdate<T>(this.id, this.target, this.value, this.refreshTtl, this.propertyUpdates); update.propertyUpdates.add(new PropertyUpdate(UpdateCommand.SET, path, value)); return update; } /** * Remove the value reachable via given {@literal path}. * * @param path path must not be {@literal null}. * @return a new {@link PartialUpdate}. */ public PartialUpdate<T> del(String path) { Assert.hasText(path, "Path to remove must not be null or empty!"); PartialUpdate<T> update = new PartialUpdate<T>(this.id, this.target, this.value, this.refreshTtl, this.propertyUpdates); update.propertyUpdates.add(new PropertyUpdate(UpdateCommand.DEL, path)); return update; } /** * Get the target type. * * @return never {@literal null}. */ public Class<T> getTarget() { return target; } /** * Get the id of the element to update. * * @return never {@literal null}. */ public Object getId() { return id; } /** * Get the list of individual property updates. * * @return never {@literal null}. */ public List<PropertyUpdate> getPropertyUpdates() { return Collections.unmodifiableList(propertyUpdates); } /** * @return true if expiration time of target should be updated. */ public boolean isRefreshTtl() { return refreshTtl; } /** * Set indicator for updating expiration time of target. * * @param refreshTtl * @return a new {@link PartialUpdate}. */ public PartialUpdate<T> refreshTtl(boolean refreshTtl) { return new PartialUpdate<T>(this.id, this.target, this.value, refreshTtl, this.propertyUpdates); } /** * @author Christoph Strobl * @since 1.8 */ public static class PropertyUpdate { private final UpdateCommand cmd; private final String propertyPath; private final Object value; private PropertyUpdate(UpdateCommand cmd, String propertyPath) { this(cmd, propertyPath, null); } private PropertyUpdate(UpdateCommand cmd, String propertyPath, Object value) { this.cmd = cmd; this.propertyPath = propertyPath; this.value = value; } /** * Get the associated {@link UpdateCommand}. * * @return never {@literal null}. */ public UpdateCommand getCmd() { return cmd; } /** * Get the target path. * * @return never {@literal null}. */ public String getPropertyPath() { return propertyPath; } /** * Get the value to set. * * @return can be {@literal null}. */ public Object getValue() { return value; } } /** * @author Christoph Strobl * @since 1.8 */ public static enum UpdateCommand { SET, DEL } }