code: /*
   * Copyright 2012-2018 Chronicle Map Contributors
   *
   * 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 net.openhft.chronicle.set;
  
  import net.openhft.chronicle.core.Jvm;
  import net.openhft.chronicle.hash.ChronicleHashBuilder;
  import net.openhft.chronicle.hash.ChronicleHashBuilderPrivateAPI;
  import net.openhft.chronicle.hash.ChronicleHashCorruption;
  import net.openhft.chronicle.hash.Data;
  import net.openhft.chronicle.hash.serialization.*;
  import net.openhft.chronicle.map.*;
  import net.openhft.chronicle.map.replication.MapRemoteOperations;
  import org.jetbrains.annotations.NotNull;
  
  import java.io.File;
  import java.io.IOException;
  
  /**
   * {@code ChronicleSetBuilder} manages the whole set of {@link ChronicleSet} configurations, could
   * be used as a classic builder and/or factory.
   * <p>
   * <p>{@code ChronicleMapBuilder} is mutable, see a note in {@link
   * ChronicleHashBuilder} interface documentation.
   *
   * @param <K> element type of the sets, created by this builder
   * @see ChronicleSet
   * @see ChronicleMapBuilder
   */
  public final class ChronicleSetBuilder<K>
          implements ChronicleHashBuilder<K, ChronicleSet<K>, ChronicleSetBuilder<K>> {
  
      private static final ChronicleHashCorruption.Listener defaultChronicleSetCorruptionListener =
              corruption -> Jvm.error().on(ChronicleSetBuilder.class, corruption.message(), corruption.exception());
  
      private ChronicleMapBuilder<K, DummyValue> chronicleMapBuilder;
      private final ChronicleSetBuilderPrivateAPI<K> privateAPI;
  
      ChronicleSetBuilder(Class<K> keyClass) {
          chronicleMapBuilder = ChronicleMapBuilder.of(keyClass, DummyValue.class)
                  .valueReaderAndDataAccess(
                          DummyValueMarshaller.INSTANCE, DummyValueMarshaller.INSTANCE)
                  .valueSizeMarshaller(SizeMarshaller.constant(0));
          //noinspection deprecation,unchecked
          privateAPI = new ChronicleSetBuilderPrivateAPI<>(
                  (ChronicleHashBuilderPrivateAPI<K, MapRemoteOperations<K, DummyValue, ?>>)
                          Jvm.getValue(chronicleMapBuilder, "privateAPI"));
      }
  
      /**
       * Returns a new {@code ChronicleSetBuilder} instance which is able to {@linkplain #create()
       * create} sets with the specified key class.
       *
       * @param keyClass class object used to infer key type and discover it's properties via
       *                 reflection
       * @param <K>      key type of the sets, created by the returned builder
       * @return a new builder for the given key class
       */
      public static <K> ChronicleSetBuilder<K> of(Class<K> keyClass) {
          return new ChronicleSetBuilder<>(keyClass);
      }
  
      @Override
      public ChronicleSetBuilder<K> clone() {
          try {
              @SuppressWarnings("unchecked") final ChronicleSetBuilder<K> result = (ChronicleSetBuilder<K>) super.clone();
              result.chronicleMapBuilder = chronicleMapBuilder.clone();
              return result;
          } catch (CloneNotSupportedException e) {
              throw new AssertionError(e);
          }
      }
  
      @Override
      public ChronicleSetBuilder<K> name(String name) {
          chronicleMapBuilder.name(name);
          return this;
      }
  
      @Override
      public ChronicleSetBuilder<K> actualSegments(int actualSegments) {
          chronicleMapBuilder.actualSegments(actualSegments);
          return this;
      }
  
      @Override
      public ChronicleSetBuilder<K> minSegments(int minSegments) {
          chronicleMapBuilder.minSegments(minSegments);
          return this;
      }
  
      @Override
      public ChronicleSetBuilder<K> entriesPerSegment(long entriesPerSegment) {
          chronicleMapBuilder.entriesPerSegment(entriesPerSegment);
          return this;
      }
  
      @Override
      public ChronicleSetBuilder<K> actualChunksPerSegmentTier(long actualChunksPerSegmentTier) {
          chronicleMapBuilder.actualChunksPerSegmentTier(actualChunksPerSegmentTier);
          return this;
      }
  
      /**
       * {@inheritDoc}
       * <p>
       * <p>Example: if keys in your set(s) are English words in {@link String} form, average English
       * word length is 5.1, configure average key size of 6: <pre>{@code
       * ChronicleSet<String> uniqueWords = ChronicleSetBuilder.of(String.class)
       *     .entries(50000)
       *     .averageKeySize(6)
       *     .create();}</pre>
       * <p>
       * <p>(Note that 6 is chosen as average key size in bytes despite strings in Java are UTF-16
       * encoded (and each character takes 2 bytes on-heap), because default off-heap {@link String}
       * encoding is UTF-8 in {@code ChronicleSet}.)
       *
       * @param averageKeySize the average size in bytes of the key
       * @see #constantKeySizeBySample(Object)
       * @see #actualChunkSize(int)
       */
      @Override
      public ChronicleSetBuilder<K> averageKeySize(double averageKeySize) {
          chronicleMapBuilder.averageKeySize(averageKeySize);
          return this;
      }
  
      @Override
      public ChronicleSetBuilder<K> averageKey(K averageKey) {
          chronicleMapBuilder.averageKey(averageKey);
          return this;
      }
  
      /**
       * {@inheritDoc}
       * <p>
       * <p>For example, if your keys are Git commit hashes:<pre>{@code
       * Set<byte[]> gitCommitsOfInterest = ChronicleSetBuilder.of(byte[].class)
       *     .constantKeySizeBySample(new byte[20])
       *     .create();}</pre>
       *
       * @see ChronicleHashBuilder#averageKeySize(double)
       */
      @Override
      public ChronicleSetBuilder<K> constantKeySizeBySample(K sampleKey) {
          chronicleMapBuilder.constantKeySizeBySample(sampleKey);
          return this;
      }
  
      @Override
      public ChronicleSetBuilder<K> actualChunkSize(int actualChunkSize) {
          chronicleMapBuilder.actualChunkSize(actualChunkSize);
          return this;
      }
  
      @Override
      public ChronicleSetBuilder<K> maxChunksPerEntry(int maxChunksPerEntry) {
          chronicleMapBuilder.maxChunksPerEntry(maxChunksPerEntry);
          return this;
      }
  
      @Override
      public ChronicleSetBuilder<K> entries(long entries) {
          chronicleMapBuilder.entries(entries);
          return this;
      }
  
      @Override
      public ChronicleSetBuilder<K> maxBloatFactor(double maxBloatFactor) {
          chronicleMapBuilder.maxBloatFactor(maxBloatFactor);
          return this;
      }
  
      @Override
      public ChronicleSetBuilder<K> allowSegmentTiering(boolean allowSegmentTiering) {
          chronicleMapBuilder.allowSegmentTiering(allowSegmentTiering);
          return this;
      }
  
      @Override
      public ChronicleSetBuilder<K> nonTieredSegmentsPercentile(double nonTieredSegmentsPercentile) {
          chronicleMapBuilder.nonTieredSegmentsPercentile(nonTieredSegmentsPercentile);
          return this;
      }
  
      @Override
      public String toString() {
          return " ChronicleSetBuilder{" +
                  "chronicleMapBuilder=" + chronicleMapBuilder +
                  '}';
      }
  
      @SuppressWarnings("EqualsWhichDoesntCheckParameterClass")
      @Override
      public boolean equals(Object o) {
          return o instanceof ChronicleSetBuilder &&
                  chronicleMapBuilder.equals(((ChronicleSetBuilder) o).chronicleMapBuilder);
      }
  
      @Override
      public int hashCode() {
          return toString().hashCode();
      }
  
      @Override
      public ChronicleSetBuilder<K> keyReaderAndDataAccess(
              SizedReader<K> keyReader, @NotNull DataAccess<K> keyDataAccess) {
          chronicleMapBuilder.keyReaderAndDataAccess(keyReader, keyDataAccess);
          return this;
      }
  
      @Override
      public ChronicleSetBuilder<K> keyMarshallers(
              @NotNull BytesReader<K> keyReader, @NotNull BytesWriter<? super K> keyWriter) {
          chronicleMapBuilder.keyMarshallers(keyReader, keyWriter);
          return this;
      }
  
      @Override
      public <M extends BytesReader<K> & BytesWriter<? super K>>
      ChronicleSetBuilder<K> keyMarshaller(@NotNull M marshaller) {
          chronicleMapBuilder.keyMarshaller(marshaller);
          return this;
      }
  
      @Override
      public ChronicleSetBuilder<K> keyMarshallers(
              @NotNull SizedReader<K> keyReader, @NotNull SizedWriter<? super K> keyWriter) {
          chronicleMapBuilder.keyMarshallers(keyReader, keyWriter);
          return this;
      }
  
      @Override
      public <M extends SizedReader<K> & SizedWriter<? super K>>
      ChronicleSetBuilder<K> keyMarshaller(@NotNull M sizedMarshaller) {
          chronicleMapBuilder.keyMarshaller(sizedMarshaller);
          return this;
      }
  
      @Override
      public ChronicleSetBuilder<K> keySizeMarshaller(@NotNull SizeMarshaller keySizeMarshaller) {
          chronicleMapBuilder.keySizeMarshaller(keySizeMarshaller);
          return this;
      }
  
      @Override
      public ChronicleSetBuilder<K> aligned64BitMemoryOperationsAtomic(
              boolean aligned64BitMemoryOperationsAtomic) {
          chronicleMapBuilder.aligned64BitMemoryOperationsAtomic(aligned64BitMemoryOperationsAtomic);
          return this;
      }
  
      @Override
      public ChronicleSetBuilder<K> checksumEntries(boolean checksumEntries) {
          chronicleMapBuilder.checksumEntries(checksumEntries);
          return this;
      }
  
      /**
       * Inject your SPI code around basic {@code ChronicleSet}'s operations with entries:
       * removing entries and inserting new entries.
       * <p>
       * <p>This affects behaviour of ordinary set.add(), set.remove(), calls, as well as removes
       * <i>during iterations</i>, updates during <i>remote calls</i> and
       * <i>internal replication operations</i>.
       */
      public ChronicleSetBuilder<K> entryOperations(SetEntryOperations<K, ?> entryOperations) {
          chronicleMapBuilder.entryOperations(new MapEntryOperations<K, DummyValue, Object>() {
              @Override
              public Object remove(@NotNull MapEntry<K, DummyValue> entry) {
                  //noinspection unchecked
                  return entryOperations.remove((SetEntry<K>) entry);
              }
  
              @Override
              public Object insert(
                      @NotNull MapAbsentEntry<K, DummyValue> absentEntry, Data<DummyValue> value) {
                  //noinspection unchecked
                  return entryOperations.insert((SetAbsentEntry<K>) absentEntry);
              }
          });
          return this;
      }
  
      @Override
      public ChronicleSet<K> create() {
          final ChronicleMap<K, DummyValue> map = chronicleMapBuilder.create();
          return new SetFromMap<>((VanillaChronicleMap<K, DummyValue, ?>) map);
      }
  
      @Override
      public ChronicleSet<K> createPersistedTo(File file) throws IOException {
          ChronicleMap<K, DummyValue> map = chronicleMapBuilder.createPersistedTo(file);
          return new SetFromMap<>((VanillaChronicleMap<K, DummyValue, ?>) map);
      }
  
      @Override
      public ChronicleSet<K> createOrRecoverPersistedTo(File file) throws IOException {
          return createOrRecoverPersistedTo(file, true);
      }
  
      @Override
      public ChronicleSet<K> createOrRecoverPersistedTo(File file, boolean sameLibraryVersion)
              throws IOException {
          return createOrRecoverPersistedTo(file, sameLibraryVersion,
                  defaultChronicleSetCorruptionListener);
      }
  
      @Override
      public ChronicleSet<K> createOrRecoverPersistedTo(
              File file, boolean sameLibraryVersion,
              ChronicleHashCorruption.Listener corruptionListener) throws IOException {
          ChronicleMap<K, DummyValue> map = chronicleMapBuilder.createOrRecoverPersistedTo(
                  file, sameLibraryVersion, corruptionListener);
          return new SetFromMap<>((VanillaChronicleMap<K, DummyValue, ?>) map);
      }
  
      @Override
      public ChronicleSet<K> recoverPersistedTo(File file, boolean sameBuilderConfigAndLibraryVersion)
              throws IOException {
          return recoverPersistedTo(file, sameBuilderConfigAndLibraryVersion,
                  defaultChronicleSetCorruptionListener);
      }
  
      @Override
      public ChronicleSet<K> recoverPersistedTo(
              File file, boolean sameBuilderConfigAndLibraryVersion,
              ChronicleHashCorruption.Listener corruptionListener) throws IOException {
          ChronicleMap<K, DummyValue> map = chronicleMapBuilder.recoverPersistedTo(
                  file, sameBuilderConfigAndLibraryVersion, corruptionListener);
          return new SetFromMap<>((VanillaChronicleMap<K, DummyValue, ?>) map);
      }
  
      @Override
      public ChronicleSetBuilder<K> setPreShutdownAction(Runnable preShutdownAction) {
          chronicleMapBuilder.setPreShutdownAction(preShutdownAction);
          return this;
      }
  
      @Override
      public ChronicleSetBuilder<K> skipCloseOnExitHook(boolean skipCloseOnExitHook) {
          chronicleMapBuilder.skipCloseOnExitHook(skipCloseOnExitHook);
          return this;
      }
  
  }
  
  