001/* 002 * Copyright (C) 2012 The Guava Authors 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package com.google.common.collect; 018 019import static com.google.common.base.Preconditions.checkArgument; 020import static com.google.common.base.Preconditions.checkNotNull; 021import static com.google.common.base.Predicates.compose; 022import static com.google.common.base.Predicates.in; 023import static com.google.common.base.Predicates.not; 024 025import com.google.common.annotations.Beta; 026import com.google.common.annotations.GwtIncompatible; 027import com.google.common.base.MoreObjects; 028import com.google.common.base.Predicate; 029 030import java.util.AbstractMap; 031import java.util.AbstractSet; 032import java.util.Collection; 033import java.util.Collections; 034import java.util.Iterator; 035import java.util.List; 036import java.util.Map; 037import java.util.Map.Entry; 038import java.util.NavigableMap; 039import java.util.NoSuchElementException; 040import java.util.Set; 041 042import javax.annotation.Nullable; 043 044/** 045 * An implementation of {@code RangeMap} based on a {@code TreeMap}, supporting 046 * all optional operations. 047 * 048 * <p>Like all {@code RangeMap} implementations, this supports neither null 049 * keys nor null values. 050 * 051 * @author Louis Wasserman 052 * @since 14.0 053 */ 054@Beta 055@GwtIncompatible("NavigableMap") 056public final class TreeRangeMap<K extends Comparable, V> implements RangeMap<K, V> { 057 058 private final NavigableMap<Cut<K>, RangeMapEntry<K, V>> entriesByLowerBound; 059 060 public static <K extends Comparable, V> TreeRangeMap<K, V> create() { 061 return new TreeRangeMap<K, V>(); 062 } 063 064 private TreeRangeMap() { 065 this.entriesByLowerBound = Maps.newTreeMap(); 066 } 067 068 private static final class RangeMapEntry<K extends Comparable, V> 069 extends AbstractMapEntry<Range<K>, V> { 070 private final Range<K> range; 071 private final V value; 072 073 RangeMapEntry(Cut<K> lowerBound, Cut<K> upperBound, V value) { 074 this(Range.create(lowerBound, upperBound), value); 075 } 076 077 RangeMapEntry(Range<K> range, V value) { 078 this.range = range; 079 this.value = value; 080 } 081 082 @Override 083 public Range<K> getKey() { 084 return range; 085 } 086 087 @Override 088 public V getValue() { 089 return value; 090 } 091 092 public boolean contains(K value) { 093 return range.contains(value); 094 } 095 096 Cut<K> getLowerBound() { 097 return range.lowerBound; 098 } 099 100 Cut<K> getUpperBound() { 101 return range.upperBound; 102 } 103 } 104 105 @Override 106 @Nullable 107 public V get(K key) { 108 Entry<Range<K>, V> entry = getEntry(key); 109 return (entry == null) ? null : entry.getValue(); 110 } 111 112 @Override 113 @Nullable 114 public Entry<Range<K>, V> getEntry(K key) { 115 Map.Entry<Cut<K>, RangeMapEntry<K, V>> mapEntry = 116 entriesByLowerBound.floorEntry(Cut.belowValue(key)); 117 if (mapEntry != null && mapEntry.getValue().contains(key)) { 118 return mapEntry.getValue(); 119 } else { 120 return null; 121 } 122 } 123 124 @Override 125 public void put(Range<K> range, V value) { 126 if (!range.isEmpty()) { 127 checkNotNull(value); 128 remove(range); 129 entriesByLowerBound.put(range.lowerBound, new RangeMapEntry<K, V>(range, value)); 130 } 131 } 132 133 @Override 134 public void putAll(RangeMap<K, V> rangeMap) { 135 for (Map.Entry<Range<K>, V> entry : rangeMap.asMapOfRanges().entrySet()) { 136 put(entry.getKey(), entry.getValue()); 137 } 138 } 139 140 @Override 141 public void clear() { 142 entriesByLowerBound.clear(); 143 } 144 145 @Override 146 public Range<K> span() { 147 Entry<Cut<K>, RangeMapEntry<K, V>> firstEntry = entriesByLowerBound.firstEntry(); 148 Entry<Cut<K>, RangeMapEntry<K, V>> lastEntry = entriesByLowerBound.lastEntry(); 149 if (firstEntry == null) { 150 throw new NoSuchElementException(); 151 } 152 return Range.create( 153 firstEntry.getValue().getKey().lowerBound, 154 lastEntry.getValue().getKey().upperBound); 155 } 156 157 private void putRangeMapEntry(Cut<K> lowerBound, Cut<K> upperBound, V value) { 158 entriesByLowerBound.put(lowerBound, new RangeMapEntry<K, V>(lowerBound, upperBound, value)); 159 } 160 161 @Override 162 public void remove(Range<K> rangeToRemove) { 163 if (rangeToRemove.isEmpty()) { 164 return; 165 } 166 167 /* 168 * The comments for this method will use [ ] to indicate the bounds of rangeToRemove and ( ) to 169 * indicate the bounds of ranges in the range map. 170 */ 171 Map.Entry<Cut<K>, RangeMapEntry<K, V>> mapEntryBelowToTruncate = 172 entriesByLowerBound.lowerEntry(rangeToRemove.lowerBound); 173 if (mapEntryBelowToTruncate != null) { 174 // we know ( [ 175 RangeMapEntry<K, V> rangeMapEntry = mapEntryBelowToTruncate.getValue(); 176 if (rangeMapEntry.getUpperBound().compareTo(rangeToRemove.lowerBound) > 0) { 177 // we know ( [ ) 178 if (rangeMapEntry.getUpperBound().compareTo(rangeToRemove.upperBound) > 0) { 179 // we know ( [ ] ), so insert the range ] ) back into the map -- 180 // it's being split apart 181 putRangeMapEntry(rangeToRemove.upperBound, rangeMapEntry.getUpperBound(), 182 mapEntryBelowToTruncate.getValue().getValue()); 183 } 184 // overwrite mapEntryToTruncateBelow with a truncated range 185 putRangeMapEntry(rangeMapEntry.getLowerBound(), rangeToRemove.lowerBound, 186 mapEntryBelowToTruncate.getValue().getValue()); 187 } 188 } 189 190 Map.Entry<Cut<K>, RangeMapEntry<K, V>> mapEntryAboveToTruncate = 191 entriesByLowerBound.lowerEntry(rangeToRemove.upperBound); 192 if (mapEntryAboveToTruncate != null) { 193 // we know ( ] 194 RangeMapEntry<K, V> rangeMapEntry = mapEntryAboveToTruncate.getValue(); 195 if (rangeMapEntry.getUpperBound().compareTo(rangeToRemove.upperBound) > 0) { 196 // we know ( ] ), and since we dealt with truncating below already, 197 // we know [ ( ] ) 198 putRangeMapEntry(rangeToRemove.upperBound, rangeMapEntry.getUpperBound(), 199 mapEntryAboveToTruncate.getValue().getValue()); 200 entriesByLowerBound.remove(rangeToRemove.lowerBound); 201 } 202 } 203 entriesByLowerBound.subMap(rangeToRemove.lowerBound, rangeToRemove.upperBound).clear(); 204 } 205 206 @Override 207 public Map<Range<K>, V> asMapOfRanges() { 208 return new AsMapOfRanges(); 209 } 210 211 private final class AsMapOfRanges extends AbstractMap<Range<K>, V> { 212 213 @Override 214 public boolean containsKey(@Nullable Object key) { 215 return get(key) != null; 216 } 217 218 @Override 219 public V get(@Nullable Object key) { 220 if (key instanceof Range) { 221 Range<?> range = (Range<?>) key; 222 RangeMapEntry<K, V> rangeMapEntry = entriesByLowerBound.get(range.lowerBound); 223 if (rangeMapEntry != null && rangeMapEntry.getKey().equals(range)) { 224 return rangeMapEntry.getValue(); 225 } 226 } 227 return null; 228 } 229 230 @Override 231 public Set<Entry<Range<K>, V>> entrySet() { 232 return new AbstractSet<Entry<Range<K>, V>>() { 233 234 @SuppressWarnings("unchecked") // it's safe to upcast iterators 235 @Override 236 public Iterator<Entry<Range<K>, V>> iterator() { 237 return (Iterator) entriesByLowerBound.values().iterator(); 238 } 239 240 @Override 241 public int size() { 242 return entriesByLowerBound.size(); 243 } 244 }; 245 } 246 } 247 248 @Override 249 public RangeMap<K, V> subRangeMap(Range<K> subRange) { 250 if (subRange.equals(Range.all())) { 251 return this; 252 } else { 253 return new SubRangeMap(subRange); 254 } 255 } 256 257 @SuppressWarnings("unchecked") 258 private RangeMap<K, V> emptySubRangeMap() { 259 return EMPTY_SUB_RANGE_MAP; 260 } 261 262 private static final RangeMap EMPTY_SUB_RANGE_MAP = 263 new RangeMap() { 264 @Override 265 @Nullable 266 public Object get(Comparable key) { 267 return null; 268 } 269 270 @Override 271 @Nullable 272 public Entry<Range, Object> getEntry(Comparable key) { 273 return null; 274 } 275 276 @Override 277 public Range span() { 278 throw new NoSuchElementException(); 279 } 280 281 @Override 282 public void put(Range range, Object value) { 283 checkNotNull(range); 284 throw new IllegalArgumentException( 285 "Cannot insert range " + range + " into an empty subRangeMap"); 286 } 287 288 @Override 289 public void putAll(RangeMap rangeMap) { 290 if (!rangeMap.asMapOfRanges().isEmpty()) { 291 throw new IllegalArgumentException( 292 "Cannot putAll(nonEmptyRangeMap) into an empty " + "subRangeMap"); 293 } 294 } 295 296 @Override 297 public void clear() {} 298 299 @Override 300 public void remove(Range range) { 301 checkNotNull(range); 302 } 303 304 @Override 305 public Map<Range, Object> asMapOfRanges() { 306 return Collections.emptyMap(); 307 } 308 309 @Override 310 public RangeMap subRangeMap(Range range) { 311 checkNotNull(range); 312 return this; 313 } 314 }; 315 316 private class SubRangeMap implements RangeMap<K, V> { 317 318 private final Range<K> subRange; 319 320 SubRangeMap(Range<K> subRange) { 321 this.subRange = subRange; 322 } 323 324 @Override 325 @Nullable 326 public V get(K key) { 327 return subRange.contains(key) 328 ? TreeRangeMap.this.get(key) 329 : null; 330 } 331 332 @Override 333 @Nullable 334 public Entry<Range<K>, V> getEntry(K key) { 335 if (subRange.contains(key)) { 336 Entry<Range<K>, V> entry = TreeRangeMap.this.getEntry(key); 337 if (entry != null) { 338 return Maps.immutableEntry(entry.getKey().intersection(subRange), entry.getValue()); 339 } 340 } 341 return null; 342 } 343 344 @Override 345 public Range<K> span() { 346 Cut<K> lowerBound; 347 Entry<Cut<K>, RangeMapEntry<K, V>> lowerEntry = 348 entriesByLowerBound.floorEntry(subRange.lowerBound); 349 if (lowerEntry != null && 350 lowerEntry.getValue().getUpperBound().compareTo(subRange.lowerBound) > 0) { 351 lowerBound = subRange.lowerBound; 352 } else { 353 lowerBound = entriesByLowerBound.ceilingKey(subRange.lowerBound); 354 if (lowerBound == null || lowerBound.compareTo(subRange.upperBound) >= 0) { 355 throw new NoSuchElementException(); 356 } 357 } 358 359 Cut<K> upperBound; 360 Entry<Cut<K>, RangeMapEntry<K, V>> upperEntry = 361 entriesByLowerBound.lowerEntry(subRange.upperBound); 362 if (upperEntry == null) { 363 throw new NoSuchElementException(); 364 } else if (upperEntry.getValue().getUpperBound().compareTo(subRange.upperBound) >= 0) { 365 upperBound = subRange.upperBound; 366 } else { 367 upperBound = upperEntry.getValue().getUpperBound(); 368 } 369 return Range.create(lowerBound, upperBound); 370 } 371 372 @Override 373 public void put(Range<K> range, V value) { 374 checkArgument(subRange.encloses(range), 375 "Cannot put range %s into a subRangeMap(%s)", range, subRange); 376 TreeRangeMap.this.put(range, value); 377 } 378 379 @Override 380 public void putAll(RangeMap<K, V> rangeMap) { 381 if (rangeMap.asMapOfRanges().isEmpty()) { 382 return; 383 } 384 Range<K> span = rangeMap.span(); 385 checkArgument(subRange.encloses(span), 386 "Cannot putAll rangeMap with span %s into a subRangeMap(%s)", span, subRange); 387 TreeRangeMap.this.putAll(rangeMap); 388 } 389 390 @Override 391 public void clear() { 392 TreeRangeMap.this.remove(subRange); 393 } 394 395 @Override 396 public void remove(Range<K> range) { 397 if (range.isConnected(subRange)) { 398 TreeRangeMap.this.remove(range.intersection(subRange)); 399 } 400 } 401 402 @Override 403 public RangeMap<K, V> subRangeMap(Range<K> range) { 404 if (!range.isConnected(subRange)) { 405 return emptySubRangeMap(); 406 } else { 407 return TreeRangeMap.this.subRangeMap(range.intersection(subRange)); 408 } 409 } 410 411 @Override 412 public Map<Range<K>, V> asMapOfRanges() { 413 return new SubRangeMapAsMap(); 414 } 415 416 @Override 417 public boolean equals(@Nullable Object o) { 418 if (o instanceof RangeMap) { 419 RangeMap<?, ?> rangeMap = (RangeMap<?, ?>) o; 420 return asMapOfRanges().equals(rangeMap.asMapOfRanges()); 421 } 422 return false; 423 } 424 425 @Override 426 public int hashCode() { 427 return asMapOfRanges().hashCode(); 428 } 429 430 @Override 431 public String toString() { 432 return asMapOfRanges().toString(); 433 } 434 435 class SubRangeMapAsMap extends AbstractMap<Range<K>, V> { 436 437 @Override 438 public boolean containsKey(Object key) { 439 return get(key) != null; 440 } 441 442 @Override 443 public V get(Object key) { 444 try { 445 if (key instanceof Range) { 446 @SuppressWarnings("unchecked") // we catch ClassCastExceptions 447 Range<K> r = (Range<K>) key; 448 if (!subRange.encloses(r) || r.isEmpty()) { 449 return null; 450 } 451 RangeMapEntry<K, V> candidate = null; 452 if (r.lowerBound.compareTo(subRange.lowerBound) == 0) { 453 // r could be truncated on the left 454 Entry<Cut<K>, RangeMapEntry<K, V>> entry = 455 entriesByLowerBound.floorEntry(r.lowerBound); 456 if (entry != null) { 457 candidate = entry.getValue(); 458 } 459 } else { 460 candidate = entriesByLowerBound.get(r.lowerBound); 461 } 462 463 if (candidate != null && candidate.getKey().isConnected(subRange) 464 && candidate.getKey().intersection(subRange).equals(r)) { 465 return candidate.getValue(); 466 } 467 } 468 } catch (ClassCastException e) { 469 return null; 470 } 471 return null; 472 } 473 474 @Override 475 public V remove(Object key) { 476 V value = get(key); 477 if (value != null) { 478 @SuppressWarnings("unchecked") // it's definitely in the map, so safe 479 Range<K> range = (Range<K>) key; 480 TreeRangeMap.this.remove(range); 481 return value; 482 } 483 return null; 484 } 485 486 @Override 487 public void clear() { 488 SubRangeMap.this.clear(); 489 } 490 491 private boolean removeEntryIf(Predicate<? super Entry<Range<K>, V>> predicate) { 492 List<Range<K>> toRemove = Lists.newArrayList(); 493 for (Entry<Range<K>, V> entry : entrySet()) { 494 if (predicate.apply(entry)) { 495 toRemove.add(entry.getKey()); 496 } 497 } 498 for (Range<K> range : toRemove) { 499 TreeRangeMap.this.remove(range); 500 } 501 return !toRemove.isEmpty(); 502 } 503 504 @Override 505 public Set<Range<K>> keySet() { 506 return new Maps.KeySet<Range<K>, V>(SubRangeMapAsMap.this) { 507 @Override 508 public boolean remove(@Nullable Object o) { 509 return SubRangeMapAsMap.this.remove(o) != null; 510 } 511 512 @Override 513 public boolean retainAll(Collection<?> c) { 514 return removeEntryIf(compose(not(in(c)), Maps.<Range<K>>keyFunction())); 515 } 516 }; 517 } 518 519 @Override 520 public Set<Entry<Range<K>, V>> entrySet() { 521 return new Maps.EntrySet<Range<K>, V>() { 522 @Override 523 Map<Range<K>, V> map() { 524 return SubRangeMapAsMap.this; 525 } 526 527 @Override 528 public Iterator<Entry<Range<K>, V>> iterator() { 529 if (subRange.isEmpty()) { 530 return Iterators.emptyIterator(); 531 } 532 Cut<K> cutToStart = MoreObjects.firstNonNull( 533 entriesByLowerBound.floorKey(subRange.lowerBound), subRange.lowerBound); 534 final Iterator<RangeMapEntry<K, V>> backingItr = 535 entriesByLowerBound.tailMap(cutToStart, true).values().iterator(); 536 return new AbstractIterator<Entry<Range<K>, V>>() { 537 538 @Override 539 protected Entry<Range<K>, V> computeNext() { 540 while (backingItr.hasNext()) { 541 RangeMapEntry<K, V> entry = backingItr.next(); 542 if (entry.getLowerBound().compareTo(subRange.upperBound) >= 0) { 543 break; 544 } else if (entry.getUpperBound().compareTo(subRange.lowerBound) > 0) { 545 // this might not be true e.g. at the start of the iteration 546 return Maps.immutableEntry( 547 entry.getKey().intersection(subRange), entry.getValue()); 548 } 549 } 550 return endOfData(); 551 } 552 }; 553 } 554 555 @Override 556 public boolean retainAll(Collection<?> c) { 557 return removeEntryIf(not(in(c))); 558 } 559 560 @Override 561 public int size() { 562 return Iterators.size(iterator()); 563 } 564 565 @Override 566 public boolean isEmpty() { 567 return !iterator().hasNext(); 568 } 569 }; 570 } 571 572 @Override 573 public Collection<V> values() { 574 return new Maps.Values<Range<K>, V>(this) { 575 @Override 576 public boolean removeAll(Collection<?> c) { 577 return removeEntryIf(compose(in(c), Maps.<V>valueFunction())); 578 } 579 580 @Override 581 public boolean retainAll(Collection<?> c) { 582 return removeEntryIf(compose(not(in(c)), Maps.<V>valueFunction())); 583 } 584 }; 585 } 586 } 587 } 588 589 @Override 590 public boolean equals(@Nullable Object o) { 591 if (o instanceof RangeMap) { 592 RangeMap<?, ?> rangeMap = (RangeMap<?, ?>) o; 593 return asMapOfRanges().equals(rangeMap.asMapOfRanges()); 594 } 595 return false; 596 } 597 598 @Override 599 public int hashCode() { 600 return asMapOfRanges().hashCode(); 601 } 602 603 @Override 604 public String toString() { 605 return entriesByLowerBound.values().toString(); 606 } 607}