content: /*
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements.  See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership.  The ASF 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 accord.primitives;
  
  import accord.api.Query;
  import accord.api.Read;
  import accord.api.Sliceable;
  import accord.api.Update;
  import accord.utils.Invariants;
  
  import javax.annotation.Nonnull;
  import javax.annotation.Nullable;
  
  import static accord.primitives.Routables.Slice.Minimal;
  import static accord.utils.Invariants.illegalState;
  
  public interface PartialTxn extends Txn
  {
      PartialTxn with(PartialTxn add);
      Txn reconstitute(FullRoute<?> route);
      PartialTxn reconstitutePartial(Participants<?> covering);
  
      default boolean covers(Route<?> route)
      {
          if (kind().isSystemTxn())
              return true;
  
          if (query() == null && route.contains(route.homeKey()))
              return false;
  
          return keys().containsAll(route);
      }
  
      default boolean covers(Unseekables<?> participants)
      {
          if (kind().isSystemTxn())
              return true;
  
          return keys().containsAll(participants);
      }
  
      static PartialTxn merge(@Nullable PartialTxn a, @Nullable PartialTxn b)
      {
          return a == null ? b : b == null ? a : a.with(b);
      }
  
      // TODO (low priority, clarity): override toString
      class InMemory extends Txn.InMemory implements PartialTxn
      {
          public InMemory(Kind kind, Seekables<?, ?> keys, Read read, Query query, Update update)
          {
              super(kind, keys, read, query, update);
          }
  
          public InMemory(Kind kind, Seekables<?, ?> keys, Read read, Query query, Update update, Sliceable implementationDefined)
          {
              super(kind, keys, read, query, update, implementationDefined);
          }
  
          @Override
          public PartialTxn with(PartialTxn add)
          {
              if (!add.kind().equals(kind()))
                  throw new IllegalArgumentException();
  
              Seekables<?, ?> keys = ((Seekables)this.keys()).with(add.keys());
              Read read = this.read().merge(add.read());
              Query query = this.query() == null ? add.query() : this.query();
              Update update = this.update() == null ? null : this.update().merge(add.update());
              Sliceable implementationDefined = null;
              if (this.implementationDefined != null)
              {
                  Invariants.require(add instanceof Txn.InMemory);
                  implementationDefined = this.implementationDefined.merge(((Txn.InMemory)add).implementationDefined);
              }
  
              if (keys == this.keys())
              {
                  if (read == this.read() && query == this.query() && update == this.update() && implementationDefined == this.implementationDefined)
                      return this;
              }
              else if (keys == add.keys())
              {
                  if (read == add.read() && query == add.query() && update == add.update() && (implementationDefined == null || implementationDefined == ((Txn.InMemory)add).implementationDefined))
                      return add;
              }
              return new PartialTxn.InMemory(kind(), keys, read, query, update, implementationDefined);
          }
  
          @Override
          public Txn reconstitute(FullRoute<?> route)
          {
              if (!kind().isSystemTxn() && !covers(route) || query() == null)
                  throw illegalState("Incomplete PartialTxn: " + this + ", route: " + route);
  
              return new Txn.InMemory(kind(), keys(), read(), query(), update(), implementationDefined);
          }
  
          @Override
          public PartialTxn reconstitutePartial(Participants<?> covering)
          {
              if (kind().isSystemTxn() && !covers(covering))
                  throw illegalState("Incomplete PartialTxn: " + this + ", covering: " + covering);
  
              if (this.keys().containsAll(covering))
                  return this;
  
              return new PartialTxn.InMemory(kind(), keys(), read(), query(), update(), implementationDefined);
          }
  
          @Nonnull
          @Override
          public PartialTxn intersecting(Participants<?> participants, boolean includeQuery)
          {
              Seekables<?, ?> keys = keys();
              Seekables<?, ?> intersecting = keys().intersecting(participants, Minimal);
              if (intersecting == keys)
                  return this;
  
              return new PartialTxn.InMemory(
                  kind(), intersecting,
                  read().intersecting(participants), includeQuery ? query() : null,
                  update() == null ? null : update().intersecting(participants),
                  implementationDefined == null ? null : implementationDefined.intersecting(participants)
              );
          }
  
          @Override
          public PartialTxn slice(Ranges ranges, boolean includeQuery)
          {
              return super.slice(ranges, includeQuery);
          }
      }
  }
  