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

com.liveramp.commons.collections.TreeIntervalMap Maven / Gradle / Ivy

package com.liveramp.commons.collections;

import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;

public class TreeIntervalMap implements IntervalMap {

  //  omit max internally since it is already the key
  private class InternalInterval {
    private final long min;
    private final V value;
    private InternalInterval(long min, V value) {
      this.min = min;
      this.value = value;
    }

    @Override
    public String toString() {
      return "InternalInterval{" +
          "min=" + min +
          ", value=" + value +
          '}';
    }
  }

  private TreeMap maxToValue = new TreeMap();

  public static  TreeIntervalMap create(){
    return new TreeIntervalMap();
  }

  @Override
  public Iterator> iterator(){
    final Iterator> internalIter = maxToValue.entrySet().iterator();

    return new Iterator>() {
      @Override
      public boolean hasNext() {
        return internalIter.hasNext();
      }

      @Override
      public Interval next() {
        Map.Entry internal = internalIter.next();
        return new Interval(internal.getValue().min, internal.getKey(), internal.getValue().value);
      }

      @Override
      public void remove() {
        throw new UnsupportedOperationException();
      }
    };
  }

  private void failInsert(long min, long max, V value){
    throw new RuntimeException("Trying to insert value: "+value+" from "+min+" to "+max+" but values already exists for that range!");
  }

  @Override
  public void put(long key, V value){
    put(key, key, value);
  }

  @Override
  public void put(long min, long max, V value){

    if(min > max){
      throw new RuntimeException("cannot insert interval with min "+min+" and max "+max+"!");
    }

    //  check consistency
    Map betweenMinMax = maxToValue.subMap(min, true, max, true);
    if(!betweenMinMax.isEmpty()){
      failInsert(min, max, value);
    }

    Map.Entry existingEntry = getEntry(max);
    if(existingEntry != null){
      failInsert(min, max, value);
    }

    // can we combine it with an existing range?  try both upper and lower

    long insertMin = min;
    long insertMax = max;

    Map.Entry lowerNeighbbor = getEntry(min-1);
    if(lowerNeighbbor != null && lowerNeighbbor.getValue().value.equals(value)){
      maxToValue.remove(lowerNeighbbor.getKey());
      insertMin = lowerNeighbbor.getValue().min;
    }

    Map.Entry upperNeighbbor = getEntry(max+1);
    if(upperNeighbbor != null && upperNeighbbor.getValue().value.equals(value)){
      maxToValue.remove(upperNeighbbor.getKey());
      insertMax = upperNeighbbor.getKey();
    }

    maxToValue.put(insertMax, new InternalInterval(insertMin, value));
  }

  @Override
  public V get(long value){
    Map.Entry entry = getEntry(value);
    if(entry != null){
      return entry.getValue().value;
    }

    return null;
  }

  protected Map.Entry getEntry(long value){
    Map.Entry rightAbove = maxToValue.ceilingEntry(value);
    if(rightAbove != null){
      if(rightAbove.getValue().min <= value){
        return rightAbove;
      }
    }
    return null;
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy