blob: 8a888213dd33e49dd9e47f74cf6d813172cdb5fd [file] [log] [blame]
/*
* Copyright 2000-2010 JetBrains s.r.o.
*
* 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.intellij.openapi.vcs;
import com.intellij.util.PairProcessor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
/**
* @author irengrig
*/
public class AreaMap<Key, Val> {
protected final List<Key> myKeys;
protected final Map<Key, Val> myMap;
// [admittedly] parent, [-"-] child
protected final PairProcessor<Key, Key> myKeysResemblance;
private final Comparator<Key> myComparator;
public static<Key extends Comparable<Key>, Val> AreaMap<Key, Val> create(final PairProcessor<Key, Key> keysResemblance) {
return new AreaMap<Key,Val>(keysResemblance, new ComparableComparator<Key>());
}
public static<Key, Val> AreaMap<Key, Val> create(final PairProcessor<Key, Key> keysResemblance, final Comparator<Key> comparator) {
return new AreaMap<Key,Val>(keysResemblance, comparator);
}
protected AreaMap(final PairProcessor<Key, Key> keysResemblance, final Comparator<Key> comparator) {
myKeysResemblance = keysResemblance;
myComparator = comparator;
myKeys = new LinkedList<Key>();
myMap = new HashMap<Key, Val>();
}
public void putAll(final AreaMap<Key, Val> other) {
myKeys.addAll(other.myKeys);
myMap.putAll(other.myMap);
}
public void put(final Key key, final Val val) {
putImpl(key, val);
}
protected int putIfNoParent(final Key key, final Val val) {
if(myMap.put(key, val) != null) {
return -1;
}
if (myKeys.isEmpty()) {
myKeys.add(key);
return 0;
}
final int idx = Collections.binarySearch(myKeys, key, myComparator);
if (idx < 0) {
// insertion, no copy exist
final int insertionIdx = - idx - 1;
// check parent
for (final ListIterator<Key> listIterator = myKeys.listIterator(insertionIdx); listIterator.hasPrevious();) {
final Key previous = listIterator.previous();
if (myKeysResemblance.process(previous, key)) {
myMap.remove(key);
return -1;
}
}
// insertionIdx not necessarily exist
myKeys.add(insertionIdx, key);
return insertionIdx;
}
assert true;
myMap.remove(key);
return -1;
}
protected int putImpl(final Key key, final Val val) {
myMap.put(key, val);
if (myKeys.isEmpty()) {
myKeys.add(key);
return 0;
}
final int idx = Collections.binarySearch(myKeys, key, myComparator);
if (idx < 0) {
// insertion, no copy exist
final int insertionIdx = - idx - 1;
// insertionIdx not necessarily exist
myKeys.add(insertionIdx, key);
return insertionIdx;
}
return idx;
}
public Collection<Val> values() {
return Collections.unmodifiableCollection(myMap.values());
}
public Collection<Key> keySet() {
return Collections.unmodifiableCollection(myKeys);
}
@Nullable
public Val getExact(final Key key) {
return myMap.get(key);
}
public void removeByValue(@NotNull final Val val) {
for (Iterator<Key> iterator = myKeys.iterator(); iterator.hasNext();) {
final Key key = iterator.next();
final Val current = myMap.get(key);
if (val.equals(current)) {
iterator.remove();
myMap.remove(key);
return;
}
}
}
public void remove(final Key key) {
myKeys.remove(key);
myMap.remove(key);
}
public boolean contains(final Key key) {
return myKeys.contains(key);
}
public void getSimiliar(final Key key, final PairProcessor<Key, Val> consumer) {
final int idx = Collections.binarySearch(myKeys, key, myComparator);
if (idx < 0) {
final int insertionIdx = - idx - 1;
// take item before
final int itemBeforeIdx = insertionIdx - 1;
if (itemBeforeIdx >= 0) {
for (ListIterator<Key> iterator = myKeys.listIterator(itemBeforeIdx + 1); iterator.hasPrevious(); ) {
final Key candidate = iterator.previous();
if (! myKeysResemblance.process(candidate, key)) continue;
if (consumer.process(candidate, myMap.get(candidate))) break; // if need only a part of keys
}
}
} else {
consumer.process(key, myMap.get(key));
}
}
public boolean isEmpty() {
return myKeys.isEmpty();
}
public void clear() {
myKeys.clear();
myMap.clear();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
AreaMap areaMap = (AreaMap)o;
if (!myKeys.equals(areaMap.myKeys)) return false;
if (!myMap.equals(areaMap.myMap)) return false;
return true;
}
@Override
public int hashCode() {
int result = myKeys.hashCode();
result = 31 * result + myMap.hashCode();
return result;
}
}