/******************************************************************************* * 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]; } }