memory index storage reworked:
[fedora-idea.git] / platform / lang-impl / src / com / intellij / util / indexing / MemoryIndexStorage.java
blob09eeccd7c4dee23baf8fa83fdef6529f96ff3949
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com.intellij.util.indexing;
19 import com.intellij.util.CommonProcessors;
20 import com.intellij.util.Processor;
21 import com.intellij.util.containers.ContainerUtil;
22 import org.jetbrains.annotations.NotNull;
24 import java.io.IOException;
25 import java.util.*;
26 import java.util.concurrent.atomic.AtomicBoolean;
28 /**
29 * This storage is needed for indexing yet unsaved data without saving those changes to 'main' backend storage
31 * @author Eugene Zhuravlev
32 * Date: Dec 10, 2007
34 public class MemoryIndexStorage<Key, Value> implements IndexStorage<Key, Value> {
35 private final Map<Key, UpdatableValueContainer<Value>> myMap = new HashMap<Key,UpdatableValueContainer<Value>>();
36 private final IndexStorage<Key, Value> myBackendStorage;
37 private final List<BufferingStateListener> myListeners = ContainerUtil.createEmptyCOWList();
38 private final AtomicBoolean myBufferingEnabled = new AtomicBoolean(false);
40 public interface BufferingStateListener {
41 void bufferingStateChanged(boolean newState);
42 void memoryStorageCleared();
45 public MemoryIndexStorage(IndexStorage<Key, Value> backend) {
46 myBackendStorage = backend;
49 public void addBufferingStateListsner(BufferingStateListener listener) {
50 myListeners.add(listener);
53 public void removeBufferingStateListsner(BufferingStateListener listener) {
54 myListeners.remove(listener);
57 public void setBufferingEnabled(boolean enabled) {
58 final boolean wasEnabled = myBufferingEnabled.getAndSet(enabled);
59 if (wasEnabled != enabled) {
60 for (BufferingStateListener listener : myListeners) {
61 listener.bufferingStateChanged(enabled);
66 public void clearMemoryMap() {
67 myMap.clear();
70 public void fireMemoryStorageCleared() {
71 for (BufferingStateListener listener : myListeners) {
72 listener.memoryStorageCleared();
76 public void close() throws StorageException {
77 myBackendStorage.close();
80 public void clear() throws StorageException {
81 clearMemoryMap();
82 myBackendStorage.clear();
85 public void flush() throws IOException {
86 myBackendStorage.flush();
89 public Collection<Key> getKeys() throws StorageException {
90 final Set<Key> keys = new HashSet<Key>();
91 processKeys(new CommonProcessors.CollectProcessor<Key>(keys));
92 return keys;
95 public boolean processKeys(final Processor<Key> processor) throws StorageException {
96 final Set<Key> stopList = new HashSet<Key>();
98 Processor<Key> decoratingProcessor = new Processor<Key>() {
99 public boolean process(final Key key) {
100 if (stopList.contains(key)) return true;
102 final UpdatableValueContainer<Value> container = myMap.get(key);
103 if (container != null && container.size() == 0) return true;
104 return processor.process(key);
108 for (Key key : myMap.keySet()) {
109 if (!decoratingProcessor.process(key)) return false;
110 stopList.add(key);
112 return myBackendStorage.processKeys(decoratingProcessor);
115 public void addValue(final Key key, final int inputId, final Value value) throws StorageException {
116 if (myBufferingEnabled.get()) {
117 getMemValueContainer(key).addValue(inputId, value);
118 return;
120 final UpdatableValueContainer<Value> valueContainer = myMap.get(key);
121 if (valueContainer != null) {
122 valueContainer.addValue(inputId, value);
125 myBackendStorage.addValue(key, inputId, value);
128 public void removeValue(final Key key, final int inputId, final Value value) throws StorageException {
129 if (myBufferingEnabled.get()) {
130 getMemValueContainer(key).removeValue(inputId, value);
131 return;
133 final UpdatableValueContainer<Value> valueContainer = myMap.get(key);
134 if (valueContainer != null) {
135 valueContainer.removeValue(inputId, value);
137 myBackendStorage.removeValue(key, inputId, value);
140 public void removeAllValues(Key key, int inputId) throws StorageException {
141 if (myBufferingEnabled.get()) {
142 getMemValueContainer(key).removeAllValues(inputId);
143 return;
145 final UpdatableValueContainer<Value> valueContainer = myMap.get(key);
146 if (valueContainer != null) {
147 valueContainer.removeAllValues(inputId);
150 myBackendStorage.removeAllValues(key, inputId);
153 private UpdatableValueContainer<Value> getMemValueContainer(final Key key) {
154 UpdatableValueContainer<Value> valueContainer = myMap.get(key);
155 if (valueContainer == null) {
156 valueContainer = new ChangeTrackingValueContainer<Value>(new ChangeTrackingValueContainer.Initializer<Value>() {
157 public Object getLock() {
158 return this;
161 public ValueContainer<Value> compute() {
162 try {
163 return myBackendStorage.read(key);
165 catch (StorageException e) {
166 throw new RuntimeException(e);
170 myMap.put(key, valueContainer);
172 return valueContainer;
175 @NotNull
176 public ValueContainer<Value> read(final Key key) throws StorageException {
177 final ValueContainer<Value> valueContainer = myMap.get(key);
178 if (valueContainer != null) {
179 return valueContainer;
182 return myBackendStorage.read(key);