001    /*
002     * Copyright 2010-2015 JetBrains s.r.o.
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    package org.jetbrains.kotlin.util.userDataHolder.keyFMap;
017    
018    import com.intellij.openapi.util.Key;
019    import org.jetbrains.annotations.NotNull;
020    
021    public class ArrayBackedFMap implements KeyFMap {
022      static final int ARRAY_THRESHOLD = 8;
023      private final int[] keys;
024      private final Object[] values;
025    
026      ArrayBackedFMap(@NotNull int[] keys, @NotNull Object[] values) {
027        this.keys = keys;
028        this.values = values;
029      }
030    
031      @NotNull
032      @Override
033      public <V> KeyFMap plus(@NotNull Key<V> key, @NotNull V value) {
034        int oldSize = size();
035        int keyCode = key.hashCode();
036        int[] newKeys = null;
037        Object[] newValues = null;
038        int i;
039        for (i = 0; i < oldSize; i++) {
040          int oldKey = keys[i];
041          if (keyCode == oldKey) {
042            if (value == values[i]) return this;
043            newKeys = new int[oldSize];
044            newValues = new Object[oldSize];
045            System.arraycopy(keys, 0, newKeys, 0, oldSize);
046            System.arraycopy(values, 0, newValues, 0, oldSize);
047            newValues[i] = value;
048            break;
049          }
050        }
051        if (i == oldSize) {
052          if (oldSize == ARRAY_THRESHOLD) {
053            return new MapBackedFMap(keys, keyCode, values, value);
054          }
055          int newSize = oldSize + 1;
056          newKeys = new int[newSize];
057          newValues = new Object[newSize];
058          System.arraycopy(keys, 0, newKeys, 0, oldSize);
059          System.arraycopy(values, 0, newValues, 0, oldSize);
060          newKeys[oldSize] = keyCode;
061          newValues[oldSize] = value;
062        }
063        return new ArrayBackedFMap(newKeys, newValues);
064      }
065    
066      private int size() {
067        return keys.length;
068      }
069    
070      @NotNull
071      @Override
072      public KeyFMap minus(@NotNull Key<?> key) {
073        int oldSize = size();
074        int keyCode = key.hashCode();
075        for (int i = 0; i< oldSize; i++) {
076          int oldKey = keys[i];
077          if (keyCode == oldKey) {
078            if (oldSize == 3) {
079              int i1 = (2-i)/2;
080              int i2 = 3 - (i+2)/2;
081              Key<Object> key1 = Key.getKeyByIndex(keys[i1]);
082              Key<Object> key2 = Key.getKeyByIndex(keys[i2]);
083              if (key1 == null && key2 == null) return EMPTY_MAP;
084              if (key1 == null) return new OneElementFMap<Object>(key2, values[i2]);
085              if (key2 == null) return new OneElementFMap<Object>(key1, values[i1]);
086              return new PairElementsFMap(key1, values[i1], key2, values[i2]);
087            }
088            int newSize = oldSize - 1;
089            int[] newKeys = new int[newSize];
090            Object[] newValues = new Object[newSize];
091            System.arraycopy(keys, 0, newKeys, 0, i);
092            System.arraycopy(values, 0, newValues, 0, i);
093            System.arraycopy(keys, i+1, newKeys, i, oldSize-i-1);
094            System.arraycopy(values, i+1, newValues, i, oldSize-i-1);
095            return new ArrayBackedFMap(newKeys, newValues);
096          }
097        }
098        return this;
099        //if (i == oldSize) {
100          //newKeys = new int[oldSize];
101          //newValues = new Object[oldSize];
102          //System.arraycopy(keys, 0, newKeys, 0, oldSize);
103          //System.arraycopy(values, 0, newValues, 0, oldSize);
104        //}
105    
106      }
107    
108      @Override
109      public <V> V get(@NotNull Key<V> key) {
110        int oldSize = size();
111        int keyCode = key.hashCode();
112        for (int i = 0; i < oldSize; i++) {
113          int oldKey = keys[i];
114          if (keyCode == oldKey) {
115            //noinspection unchecked
116            return (V)values[i];
117          }
118        }
119        return null;
120      }
121    
122      @Override
123      public String toString() {
124        String s = "";
125        for (int i = 0; i < keys.length; i++) {
126          int key = keys[i];
127          Object value = values[i];
128          s += (s.isEmpty() ? "" : ", ") + Key.getKeyByIndex(key) + " -> " + value;
129        }
130        return "(" + s + ")";
131      }
132    
133      @Override
134      public boolean isEmpty() {
135        return false;
136      }
137    
138      @NotNull
139      public int[] getKeyIds() {
140        return keys;
141      }
142    
143      @NotNull
144      @Override
145      public Key[] getKeys() {
146        return getKeysByIndices(keys);
147      }
148    
149      @NotNull
150      public Object[] getValues() {
151        return values;
152      }
153    
154      @NotNull
155      static Key[] getKeysByIndices(int[] indexes) {
156        Key[] result = new Key[indexes.length];
157    
158        for (int i =0; i < indexes.length; i++) {
159          result[i] = Key.getKeyByIndex(indexes[i]);
160        }
161    
162        return result;
163      }
164    }