/* * Copyright 2015 MiLaboratory.com * * 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.milaboratory.util; import com.milaboratory.core.Range; import java.util.*; import static java.util.Map.Entry; public final class RangeMap<T> implements java.io.Serializable { private final TreeMap<Range, T> map = new TreeMap<>(rangeComparator); public RangeMap() { } public Entry<Range, T> findContaining(Range range) { Entry<Range, T> ret = map.floorEntry(range); if (ret != null && ret.getKey().contains(range)) return ret; return null; } public void put(Range range, T value) { if (range.isReverse()) throw new IllegalArgumentException("Don't support inverted ranges."); if (range.isEmpty()) throw new IllegalArgumentException("Don't support empty ranges."); if (containIntersectingRanges(range)) throw new IntersectingRangesException(); map.put(range, value); } public Set<Entry<Range, T>> entrySet() { return map.entrySet(); } public T remove(Range range) { return map.remove(range); } public List<Entry<Range, T>> findAllIntersecting(Range range) { if (range.isEmpty()) return Collections.EMPTY_LIST; List<Entry<Range, T>> result = new ArrayList<>(); Entry<Range, T> tmp = map.floorEntry(range); if (tmp == null) tmp = map.firstEntry(); while (tmp != null && tmp.getKey().getFrom() < range.getTo()) { if (tmp.getKey().intersectsWith(range)) result.add(tmp); tmp = map.higherEntry(tmp.getKey()); } return result; } public List<Entry<Range, T>> findAllIntersectingOrTouching(Range range) { List<Entry<Range, T>> result = new ArrayList<>(); Entry<Range, T> tmp = map.floorEntry(range); if (tmp == null) tmp = map.firstEntry(); while (tmp != null && tmp.getKey().getFrom() <= range.getTo()) { if (tmp.getKey().intersectsWithOrTouches(range)) result.add(tmp); tmp = map.higherEntry(tmp.getKey()); } return result; } public boolean containIntersectingRanges(Range range) { Entry<Range, T> tmp = map.floorEntry(range); if (tmp != null && tmp.getKey().intersectsWith(range)) return true; tmp = map.higherEntry(range); return tmp != null && tmp.getKey().intersectsWith(range); } public Entry<Range, T> findSingleIntersection(Range range) { Entry<Range, T> ret = null, tmp = map.floorEntry(range); if (tmp != null && tmp.getKey().intersectsWith(range)) ret = tmp; tmp = map.higherEntry(range); if (tmp != null && tmp.getKey().intersectsWith(range)) if (ret != null) throw new IllegalArgumentException("Several intersection hits"); else ret = tmp; return ret; } public boolean isOverFragmented() { Range prev = null; for (Range range : map.navigableKeySet()) { if (prev == null) prev = range; else if (prev.getTo() == range.getFrom()) return true; } return false; } public static final class IntersectingRangesException extends IllegalArgumentException { public IntersectingRangesException() { } public IntersectingRangesException(String message) { super(message); } } private static final Comparator<Range> rangeComparator = new Comparator<Range>() { @Override public int compare(Range o1, Range o2) { int cmp; if ((cmp = Integer.compare(o1.getLower(), o2.getLower())) != 0) return cmp; return 0; //Integer.compare(o2.getUpper(), o1.getUpper()); } }; }