All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.common.collect.TreeRangeSet Maven / Gradle / Ivy

There is a newer version: 3.0.0.Alpha1
Show newest version
/*
 * Copyright (C) 2011 The Guava 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 com.google.common.collect;

import static com.google.common.base.Preconditions.checkNotNull;

import com.google.common.annotations.GwtIncompatible;

import java.util.Collection;
import java.util.Map.Entry;
import java.util.NavigableMap;
import java.util.Set;
import java.util.TreeMap;

import javax.annotation.Nullable;

/**
 * An implementation of {@link RangeSet} backed by a {@link TreeMap}.
 *
 * @author Louis Wasserman
 */
@GwtIncompatible("uses NavigableMap") final class TreeRangeSet
    extends RangeSet {
  // TODO(user): override inefficient defaults

  private final NavigableMap, Range> rangesByLowerCut;

  /**
   * Creates an empty {@code TreeRangeSet} instance.
   */
  public static  TreeRangeSet create() {
    return new TreeRangeSet(new TreeMap, Range>());
  }

  private TreeRangeSet(NavigableMap, Range> rangesByLowerCut) {
    this.rangesByLowerCut = rangesByLowerCut;
  }

  private transient Set> asRanges;

  @Override
  public Set> asRanges() {
    Set> result = asRanges;
    return (result == null) ? asRanges = new AsRanges() : result;
  }

  final class AsRanges extends ForwardingCollection> implements Set> {
    @Override
    protected Collection> delegate() {
      return rangesByLowerCut.values();
    }

    @Override
    public int hashCode() {
      return Sets.hashCodeImpl(this);
    }

    @Override
    public boolean equals(@Nullable Object o) {
      return Sets.equalsImpl(this, o);
    }
  }

  @Override
  @Nullable
  public Range rangeContaining(C value) {
    checkNotNull(value);
    Entry, Range> floorEntry = rangesByLowerCut.floorEntry(Cut.belowValue(value));
    if (floorEntry != null && floorEntry.getValue().contains(value)) {
      return floorEntry.getValue();
    } else {
      // TODO(kevinb): revisit this design choice
      return null;
    }
  }

  @Override
  public boolean encloses(Range range) {
    checkNotNull(range);
    Entry, Range> floorEntry = rangesByLowerCut.floorEntry(range.lowerBound);
    return floorEntry != null && floorEntry.getValue().encloses(range);
  }

  @Override
  public void add(Range rangeToAdd) {
    checkNotNull(rangeToAdd);

    if (rangeToAdd.isEmpty()) {
      return;
    }

    // We will use { } to illustrate ranges currently in the range set, and < >
    // to illustrate rangeToAdd.
    Cut lbToAdd = rangeToAdd.lowerBound;
    Cut ubToAdd = rangeToAdd.upperBound;

    Entry, Range> entryBelowLB = rangesByLowerCut.lowerEntry(lbToAdd);
    if (entryBelowLB != null) {
      // { <
      Range rangeBelowLB = entryBelowLB.getValue();
      if (rangeBelowLB.upperBound.compareTo(lbToAdd) >= 0) {
        // { < }, and we will need to coalesce
        if (rangeBelowLB.upperBound.compareTo(ubToAdd) >= 0) {
          // { < > }
          ubToAdd = rangeBelowLB.upperBound;
          /*
           * TODO(cpovirk): can we just "return;" here? Or, can we remove this if() entirely? If
           * not, add tests to demonstrate the problem with each approach
           */
        }
        lbToAdd = rangeBelowLB.lowerBound;
      }
    }

    Entry, Range> entryBelowUB = rangesByLowerCut.floorEntry(ubToAdd);
    if (entryBelowUB != null) {
      // { >
      Range rangeBelowUB = entryBelowUB.getValue();
      if (rangeBelowUB.upperBound.compareTo(ubToAdd) >= 0) {
        // { > }, and we need to coalesce
        ubToAdd = rangeBelowUB.upperBound;
      }
    }

    // Remove ranges which are strictly enclosed.
    rangesByLowerCut.subMap(lbToAdd, ubToAdd).clear();

    replaceRangeWithSameLowerBound(new Range(lbToAdd, ubToAdd));
  }

  @Override
  public void remove(Range rangeToRemove) {
    checkNotNull(rangeToRemove);

    if (rangeToRemove.isEmpty()) {
      return;
    }

    // We will use { } to illustrate ranges currently in the range set, and < >
    // to illustrate rangeToRemove.

    Entry, Range> entryBelowLB = rangesByLowerCut.lowerEntry(rangeToRemove.lowerBound);
    if (entryBelowLB != null) {
      // { <
      Range rangeBelowLB = entryBelowLB.getValue();
      if (rangeBelowLB.upperBound.compareTo(rangeToRemove.lowerBound) >= 0) {
        // { < }, and we will need to subdivide
        if (rangeBelowLB.upperBound.compareTo(rangeToRemove.upperBound) >= 0) {
          // { < > }
          replaceRangeWithSameLowerBound(
              new Range(rangeToRemove.upperBound, rangeBelowLB.upperBound));
        }
        replaceRangeWithSameLowerBound(
            new Range(rangeBelowLB.lowerBound, rangeToRemove.lowerBound));
      }
    }

    Entry, Range> entryBelowUB = rangesByLowerCut.floorEntry(rangeToRemove.upperBound);
    if (entryBelowUB != null) {
      // { >
      Range rangeBelowUB = entryBelowUB.getValue();
      if (rangeBelowUB.upperBound.compareTo(rangeToRemove.upperBound) >= 0) {
        // { > }
        replaceRangeWithSameLowerBound(
            new Range(rangeToRemove.upperBound, rangeBelowUB.upperBound));
      }
    }

    rangesByLowerCut.subMap(rangeToRemove.lowerBound, rangeToRemove.upperBound).clear();
  }

  private void replaceRangeWithSameLowerBound(Range range) {
    if (range.isEmpty()) {
      rangesByLowerCut.remove(range.lowerBound);
    } else {
      rangesByLowerCut.put(range.lowerBound, range);
    }
  }

  private transient RangeSet complement;

  @Override
  public RangeSet complement() {
    RangeSet result = complement;
    return (result == null) ? complement = createComplement() : result;
  }

  private RangeSet createComplement() {
    return new StandardComplement(this);
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy