drop memory caches only when is dirty
[idea/community.git] / jps / model / src / org / jetbrains / ether / dependencyView / PersistentMultiMaplet.java
1 /*
2  * Copyright 2000-2011 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 package org.jetbrains.ether.dependencyView;
17
18 import com.intellij.util.io.DataExternalizer;
19 import com.intellij.util.io.KeyDescriptor;
20 import com.intellij.util.io.PersistentHashMap;
21
22 import java.io.*;
23 import java.util.Collection;
24 import java.util.Collections;
25 import java.util.LinkedList;
26 import java.util.Map;
27
28 /**
29  * Created by IntelliJ IDEA.
30  * User: db
31  * Date: 08.03.11
32  * Time: 15:38
33  * To change this template use File | Settings | File Templates.
34  */
35 class PersistentMultiMaplet<K, V> implements MultiMaplet<K, V> {
36   private final PersistentHashMap<K, Collection<V>> myMap;
37   private final DataExternalizer<V> myValueExternalizer;
38
39   public PersistentMultiMaplet(final File file,
40                                final KeyDescriptor<K> keyExternalizer,
41                                final DataExternalizer<V> valueExternalizer,
42                                final TransientMultiMaplet.CollectionConstructor<V> collectionFactory) throws IOException {
43     myValueExternalizer = valueExternalizer;
44     myMap = new PersistentHashMap<K, Collection<V>>(file, keyExternalizer,
45                                                     new CollectionDataExternalizer<V>(valueExternalizer, collectionFactory));
46   }
47
48
49   @Override
50   public boolean containsKey(final K key) {
51     try {
52       return myMap.containsMapping(key);
53     }
54     catch (IOException e) {
55       throw new RuntimeException(e);
56     }
57   }
58
59   @Override
60   public Collection<V> get(final K key) {
61     try {
62       return myMap.get(key);
63     }
64     catch (IOException e) {
65       throw new RuntimeException(e);
66     }
67   }
68
69   @Override
70   public void put(final K key, final Collection<V> value) {
71     try {
72       myMap.appendData(key, new PersistentHashMap.ValueDataAppender() {
73         public void append(DataOutput out) throws IOException {
74           for (V v : value) {
75             myValueExternalizer.save(out, v);
76           }
77         }
78       });
79     }
80     catch (IOException e) {
81       throw new RuntimeException(e);
82     }
83   }
84
85   @Override
86   public void put(final K key, final V value) {
87     put(key, Collections.singleton(value));
88   }
89
90   @Override
91   public void removeAll(K key, Collection<V> values) {
92     try {
93       final Collection<V> collection = myMap.get(key);
94
95       if (collection != null) {
96         if (collection.removeAll(values)) {
97           if (collection.isEmpty()) {
98             myMap.remove(key);
99           }
100           else {
101             myMap.put(key, collection);
102           }
103         }
104       }
105     }
106     catch (IOException e) {
107       throw new RuntimeException(e);
108     }
109   }
110
111   @Override
112   public void removeFrom(final K key, final V value) {
113     try {
114       final Collection<V> collection = myMap.get(key);
115
116       if (collection != null) {
117         if (collection.remove(value)) {
118           if (collection.isEmpty()) {
119             myMap.remove(key);
120           }
121           else {
122             myMap.put(key, collection);
123           }
124         }
125       }
126     }
127     catch (IOException e) {
128       throw new RuntimeException(e);
129     }
130   }
131
132   @Override
133   public void remove(final K key) {
134     try {
135       myMap.remove(key);
136     }
137     catch (IOException e) {
138       throw new RuntimeException(e);
139     }
140   }
141
142   @Override
143   public void putAll(MultiMaplet<K, V> m) {
144     for (Map.Entry<K, Collection<V>> entry : m.entrySet()) {
145       final K key = entry.getKey();
146       remove(key);
147       put(key, entry.getValue());
148     }
149   }
150
151   public Collection<K> keyCollection() {
152     try {
153       return myMap.getAllKeysWithExistingMapping();
154     }
155     catch (IOException e) {
156       throw new RuntimeException(e);
157     }
158   }
159
160   @Override
161   public void close() {
162     try {
163       myMap.close();
164     }
165     catch (IOException e) {
166       throw new RuntimeException(e);
167     }
168   }
169
170   public void flush(boolean memoryCachesOnly) {
171     if (memoryCachesOnly) {
172       if (myMap.isDirty()) {
173         myMap.dropMemoryCaches();
174       }
175     }
176     else {
177       myMap.force();
178     }
179   }
180
181   @Override
182   public Collection<Map.Entry<K, Collection<V>>> entrySet() {
183     final Collection<Map.Entry<K, Collection<V>>> result = new LinkedList<Map.Entry<K, Collection<V>>>();
184
185     try {
186       for (final K key : myMap.getAllKeysWithExistingMapping()) {
187         final Collection<V> value = myMap.get(key);
188
189         final Map.Entry<K, Collection<V>> entry = new Map.Entry<K, Collection<V>>() {
190           @Override
191           public K getKey() {
192             return key;
193           }
194
195           @Override
196           public Collection<V> getValue() {
197             return value;
198           }
199
200           @Override
201           public Collection<V> setValue(Collection<V> value) {
202             return null;
203           }
204         };
205
206         result.add(entry);
207       }
208
209       return result;
210     }
211     catch (IOException e) {
212       throw new RuntimeException(e);
213     }
214   }
215
216   private static class CollectionDataExternalizer<V> implements DataExternalizer<Collection<V>> {
217     private final DataExternalizer<V> myElementExternalizer;
218     private final TransientMultiMaplet.CollectionConstructor<V> myCollectionFactory;
219
220     public CollectionDataExternalizer(DataExternalizer<V> elementExternalizer,
221                                       TransientMultiMaplet.CollectionConstructor<V> collectionFactory) {
222       myElementExternalizer = elementExternalizer;
223       myCollectionFactory = collectionFactory;
224     }
225
226     @Override
227     public void save(final DataOutput out, final Collection<V> value) throws IOException {
228       for (V x : value) {
229         myElementExternalizer.save(out, x);
230       }
231     }
232
233     @Override
234     public Collection<V> read(final DataInput in) throws IOException {
235       final Collection<V> result = myCollectionFactory.create();
236       final DataInputStream stream = (DataInputStream)in;
237       while (stream.available() > 0) {
238         result.add(myElementExternalizer.read(in));
239       }
240       return result;
241     }
242   }
243 }