/* * Copyright 2000-2012 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.util.keyFMap; import com.intellij.openapi.util.Key; import com.intellij.util.ArrayUtil; import gnu.trove.TIntObjectHashMap; import gnu.trove.TIntObjectProcedure; import org.jetbrains.annotations.NotNull; class MapBackedFMap extends TIntObjectHashMap<Object> implements KeyFMap { private MapBackedFMap(@NotNull MapBackedFMap oldMap, final int exclude) { super(oldMap.size()); oldMap.forEachEntry(new TIntObjectProcedure<Object>() { @Override public boolean execute(int key, Object val) { if (key != exclude) put(key, val); assert key >= 0 : key; return true; } }); assert size() > ArrayBackedFMap.ARRAY_THRESHOLD; } MapBackedFMap(@NotNull int[] keys, int newKey, @NotNull Object[] values, @NotNull Object newValue) { super(keys.length + 1); for (int i = 0; i < keys.length; i++) { int key = keys[i]; Object value = values[i]; put(key, value); assert key >= 0 : key; } put(newKey, newValue); assert newKey >= 0 : newKey; assert size() > ArrayBackedFMap.ARRAY_THRESHOLD; } @NotNull @Override public <V> KeyFMap plus(@NotNull Key<V> key, @NotNull V value) { int keyCode = key.hashCode(); assert keyCode >= 0 : key; @SuppressWarnings("unchecked") V oldValue = (V)get(keyCode); if (value == oldValue) return this; MapBackedFMap newMap = new MapBackedFMap(this, -1); newMap.put(keyCode, value); return newMap; } @NotNull @Override public KeyFMap minus(@NotNull Key<?> key) { int oldSize = size(); int keyCode = key.hashCode(); if (!containsKey(keyCode)) { return this; } if (oldSize == ArrayBackedFMap.ARRAY_THRESHOLD + 1) { int[] keys = keys(); Object[] values = getValues(); int i = ArrayUtil.indexOf(keys, keyCode); keys = ArrayUtil.remove(keys, i); values = ArrayUtil.remove(values, i); return new ArrayBackedFMap(keys, values); } return new MapBackedFMap(this, keyCode); } @Override public <V> V get(@NotNull Key<V> key) { //noinspection unchecked return (V)get(key.hashCode()); } @Override public String toString() { final StringBuilder s = new StringBuilder(); forEachEntry(new TIntObjectProcedure<Object>() { @Override public boolean execute(int key, Object value) { s.append(s.length() == 0 ? "" : ", ").append(Key.getKeyByIndex(key)).append(" -> ").append(value); return true; } }); return "[" + s.toString() + "]"; } }