blob: 3070193d84c4824276ce72506bbf9c36c8dac0e2 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2016 Google, Inc and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Stefan Xenos (Google) - Initial implementation
*******************************************************************************/
package org.eclipse.jdt.internal.core.hierarchy;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
/**
* Maps a {@link TypeBinding} onto values. Two {@link TypeBinding}s are considered equivalent
* if their IDs are the same or if they have TypeIds.NoId and they are identical objects.
* <p>
* Takes into account the fact that a ReferenceBinding may have its ID change from NoId
* to a real ID at any time without notice. (This is a behavior that was observed in
* TypeHierarchyTests.testAnonymousType01 -- if type IDs could be made invariant then it
* would be possible to implement a more efficient map that never needs to perform an
* exhaustive search.)
*/
public class BindingMap<V> {
private Map<TypeBinding, V> identityMap = new IdentityHashMap<>();
private Object[] mapIdToValue = new Object[0];
private Set<TypeBinding> bindingsWithoutAnId = new HashSet<>();
public void put(TypeBinding key, V value) {
this.identityMap.put(key, value);
if (key.id != TypeIds.NoId) {
int targetId = key.id;
insertIntoIdMap(targetId, value);
} else {
this.bindingsWithoutAnId.add(key);
}
}
@SuppressWarnings("unchecked")
public V get(TypeBinding key) {
// Check if we can find this binding by identity
V value = this.identityMap.get(key);
if (value != null) {
return value;
}
int targetId = key.id;
if (targetId != TypeIds.NoId) {
// Check if we can find this binding by value
if (targetId < this.mapIdToValue.length) {
value = (V)this.mapIdToValue[targetId];
}
if (value != null) {
return value;
}
// Check if there are any bindings that previously had no ID that have
// subsequently been assigned one.
for (Iterator<TypeBinding> bindingIter = this.bindingsWithoutAnId.iterator(); bindingIter.hasNext();) {
TypeBinding nextBinding = bindingIter.next();
if (nextBinding.id != TypeIds.NoId) {
insertIntoIdMap(nextBinding.id, this.identityMap.get(nextBinding));
bindingIter.remove();
}
}
// Now look again to see if this binding can be found
if (targetId < this.mapIdToValue.length) {
value = (V)this.mapIdToValue[targetId];
}
}
return value;
}
private void insertIntoIdMap(int targetId, V value) {
int requiredSize = targetId + 1;
if (this.mapIdToValue.length < requiredSize) {
int newSize = requiredSize * 2;
Object[] newArray = new Object[newSize];
System.arraycopy(this.mapIdToValue, 0, newArray, 0, this.mapIdToValue.length);
this.mapIdToValue = newArray;
}
this.mapIdToValue[targetId] = value;
}
public void clear() {
this.identityMap.clear();
this.bindingsWithoutAnId.clear();
this.mapIdToValue = new Object[0];
}
}