content: /*
   * Copyright 1999-2020 Alibaba Group Holding Ltd.
   *
   * 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 com.alibaba.nacos.client.redo.data;
  
  import java.util.Objects;
  
  /**
   * Nacos naming redo data.
   *
   * @author xiweng.yy
   */
  @SuppressWarnings("PMD.AbstractClassShouldStartWithAbstractNamingRule")
  public abstract class RedoData<T> {
      
      /**
       * Expected states for finally.
       *
       * <ul>
       *     <li>{@code true} meas the cached data expect registered to server finally.</li>
       *     <li>{@code false} means unregistered from server.</li>
       * </ul>
       */
      private volatile boolean expectedRegistered;
      
      /**
       * If {@code true} means cached data has been registered to server successfully.
       */
      private volatile boolean registered;
      
      /**
       * If {@code true} means cached data is unregistering from server.
       */
      private volatile boolean unregistering;
      
      private T data;
      
      protected RedoData() {
          this.expectedRegistered = true;
      }
      
      public void setExpectedRegistered(boolean registered) {
          this.expectedRegistered = registered;
      }
      
      public boolean isExpectedRegistered() {
          return expectedRegistered;
      }
      
      public boolean isRegistered() {
          return registered;
      }
      
      public boolean isUnregistering() {
          return unregistering;
      }
      
      public void setRegistered(boolean registered) {
          this.registered = registered;
      }
      
      public void setUnregistering(boolean unregistering) {
          this.unregistering = unregistering;
      }
      
      public T get() {
          return data;
      }
      
      public void set(T data) {
          this.data = data;
      }
      
      public void registered() {
          this.registered = true;
          this.unregistering = false;
      }
      
      public void unregistered() {
          this.registered = false;
          this.unregistering = true;
      }
      
      public boolean isNeedRedo() {
          return !RedoType.NONE.equals(getRedoType());
      }
      
      /**
       * Get redo type for current redo data without expected state.
       *
       * <ul>
       *     <li>{@code registered=true} & {@code unregistering=false} means data has registered, so redo should not do anything.</li>
       *     <li>{@code registered=true} & {@code unregistering=true} means data has registered and now need unregister.</li>
       *     <li>{@code registered=false} & {@code unregistering=false} means not registered yet, need register again.</li>
       *     <li>{@code registered=false} & {@code unregistering=true} means not registered yet and not continue to register.</li>
       * </ul>
       *
       * @return redo type
       */
      public RedoType getRedoType() {
          if (isRegistered() && !isUnregistering()) {
              return expectedRegistered ? RedoType.NONE : RedoType.UNREGISTER;
          } else if (isRegistered() && isUnregistering()) {
              return RedoType.UNREGISTER;
          } else if (!isRegistered() && !isUnregistering()) {
              return RedoType.REGISTER;
          } else {
              return expectedRegistered ? RedoType.REGISTER : RedoType.REMOVE;
          }
      }
      
      public enum RedoType {
          
          /**
           * Redo register.
           */
          REGISTER,
          
          /**
           * Redo unregister.
           */
          UNREGISTER,
          
          /**
           * Redo nothing.
           */
          NONE,
          
          /**
           * Remove redo data.
           */
          REMOVE;
      }
      
      @Override
      public boolean equals(Object o) {
          if (this == o) {
              return true;
          }
          if (o == null || getClass() != o.getClass()) {
              return false;
          }
          RedoData<?> redoData = (RedoData<?>) o;
          return registered == redoData.registered && unregistering == redoData.unregistering && Objects.equals(data,
                  redoData.data);
      }
      
      @Override
      public int hashCode() {
          return Objects.hash(registered, unregistering, data);
      }
  }
  