[vcs-log] minor: remove unused imports
[idea/community.git] / platform / vcs-log / impl / src / com / intellij / vcs / log / data / index / VcsLogPathsIndex.java
1 /*
2  * Copyright 2000-2016 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 com.intellij.vcs.log.data.index;
17
18 import com.intellij.openapi.Disposable;
19 import com.intellij.openapi.diagnostic.Logger;
20 import com.intellij.openapi.util.Couple;
21 import com.intellij.openapi.util.SystemInfo;
22 import com.intellij.openapi.util.io.FileUtil;
23 import com.intellij.openapi.vcs.FilePath;
24 import com.intellij.openapi.vcs.changes.Change;
25 import com.intellij.openapi.vfs.VirtualFile;
26 import com.intellij.util.Consumer;
27 import com.intellij.util.PathUtil;
28 import com.intellij.util.containers.ContainerUtil;
29 import com.intellij.util.indexing.DataIndexer;
30 import com.intellij.util.indexing.StorageException;
31 import com.intellij.util.io.*;
32 import com.intellij.util.text.CaseInsensitiveStringHashingStrategy;
33 import com.intellij.vcs.log.VcsFullCommitDetails;
34 import com.intellij.vcs.log.impl.FatalErrorHandler;
35 import com.intellij.vcs.log.impl.VcsChangesLazilyParsedDetails;
36 import com.intellij.vcs.log.util.PersistentUtil;
37 import gnu.trove.THashMap;
38 import gnu.trove.TIntHashSet;
39 import org.jetbrains.annotations.NotNull;
40
41 import java.io.DataInput;
42 import java.io.DataOutput;
43 import java.io.File;
44 import java.io.IOException;
45 import java.util.Collection;
46 import java.util.Map;
47 import java.util.Set;
48
49 import static com.intellij.util.containers.ContainerUtil.newTroveSet;
50 import static com.intellij.vcs.log.data.index.VcsLogPersistentIndex.getVersion;
51
52 public class VcsLogPathsIndex extends VcsLogFullDetailsIndex<Integer> {
53   private static final Logger LOG = Logger.getInstance(VcsLogPathsIndex.class);
54   public static final String PATHS = "paths";
55   public static final String INDEX_PATHS_IDS = "paths-ids";
56
57   @NotNull private final PathsIndexer myPathsIndexer;
58
59   public VcsLogPathsIndex(@NotNull String logId,
60                           @NotNull Set<VirtualFile> roots,
61                           @NotNull FatalErrorHandler fatalErrorHandler,
62                           @NotNull Disposable disposableParent) throws IOException {
63     super(logId, PATHS, getVersion(), new PathsIndexer(createPathsEnumerator(logId), roots),
64           new NullableIntKeyDescriptor(), fatalErrorHandler, disposableParent);
65
66     myPathsIndexer = (PathsIndexer)myIndexer;
67     myPathsIndexer.setFatalErrorConsumer(e -> fatalErrorHandler.consume(this, e));
68   }
69
70   @NotNull
71   private static PersistentEnumeratorBase<String> createPathsEnumerator(@NotNull String logId) throws IOException {
72     File storageFile = PersistentUtil.getStorageFile(INDEX, INDEX_PATHS_IDS, logId, getVersion(), true);
73     return new PersistentBTreeEnumerator<>(storageFile, SystemInfo.isFileSystemCaseSensitive ? EnumeratorStringDescriptor.INSTANCE
74                                                                                              : new ToLowerCaseStringDescriptor(),
75                                            Page.PAGE_SIZE, null, getVersion());
76   }
77
78   @Override
79   public void flush() throws StorageException {
80     super.flush();
81     myPathsIndexer.getPathsEnumerator().force();
82   }
83
84   public TIntHashSet getCommitsForPaths(@NotNull Collection<FilePath> paths) throws IOException, StorageException {
85     Set<Integer> allPathIds = ContainerUtil.newHashSet();
86     for (FilePath path : paths) {
87       allPathIds.add(myPathsIndexer.myPathsEnumerator.enumerate(path.getPath()));
88     }
89
90     TIntHashSet result = new TIntHashSet();
91     Set<Integer> renames = allPathIds;
92     while (!renames.isEmpty()) {
93       renames = addCommitsAndGetRenames(renames, allPathIds, result);
94       allPathIds.addAll(renames);
95     }
96
97     return result;
98   }
99
100   @NotNull
101   public Set<Integer> addCommitsAndGetRenames(@NotNull Set<Integer> newPathIds,
102                                               @NotNull Set<Integer> allPathIds,
103                                               @NotNull TIntHashSet commits)
104     throws StorageException {
105     Set<Integer> renames = ContainerUtil.newHashSet();
106     for (Integer key : newPathIds) {
107       iterateCommitIdsAndValues(key, (value, commit) -> {
108         commits.add(commit);
109         if (value != null && !allPathIds.contains(value)) {
110           renames.add(value);
111         }
112       });
113     }
114     return renames;
115   }
116
117   @Override
118   public void dispose() {
119     super.dispose();
120     try {
121       myPathsIndexer.getPathsEnumerator().close();
122     }
123     catch (IOException e) {
124       LOG.warn(e);
125     }
126   }
127
128   private static class PathsIndexer implements DataIndexer<Integer, Integer, VcsFullCommitDetails> {
129     @NotNull private final PersistentEnumeratorBase<String> myPathsEnumerator;
130     @NotNull private final Set<String> myRoots;
131     @NotNull private Consumer<Exception> myFatalErrorConsumer = LOG::error;
132
133     private PathsIndexer(@NotNull PersistentEnumeratorBase<String> enumerator, @NotNull Set<VirtualFile> roots) {
134       myPathsEnumerator = enumerator;
135       myRoots = newTroveSet(FileUtil.PATH_HASHING_STRATEGY);
136       for (VirtualFile root : roots) {
137         myRoots.add(root.getPath());
138       }
139     }
140
141     public void setFatalErrorConsumer(@NotNull Consumer<Exception> fatalErrorConsumer) {
142       myFatalErrorConsumer = fatalErrorConsumer;
143     }
144
145     @NotNull
146     @Override
147     public Map<Integer, Integer> map(@NotNull VcsFullCommitDetails inputData) {
148       Map<Integer, Integer> result = new THashMap<>();
149
150
151       Collection<Couple<String>> moves;
152       Collection<String> changedPaths;
153       if (inputData instanceof VcsChangesLazilyParsedDetails) {
154         changedPaths = ((VcsChangesLazilyParsedDetails)inputData).getModifiedPaths();
155         moves = ((VcsChangesLazilyParsedDetails)inputData).getRenamedPaths();
156       }
157       else {
158         moves = ContainerUtil.newHashSet();
159         changedPaths = ContainerUtil.newHashSet();
160         for (Change change : inputData.getChanges()) {
161           if (change.getAfterRevision() != null) changedPaths.add(change.getAfterRevision().getFile().getPath());
162           if (change.getBeforeRevision() != null) changedPaths.add(change.getBeforeRevision().getFile().getPath());
163           if (change.getType().equals(Change.Type.MOVED)) {
164             moves.add(Couple.of(change.getBeforeRevision().getFile().getPath(), change.getAfterRevision().getFile().getPath()));
165           }
166         }
167       }
168
169       getParentPaths(changedPaths).forEach(changedPath -> {
170         try {
171           result.put(myPathsEnumerator.enumerate(changedPath), null);
172         }
173         catch (IOException e) {
174           myFatalErrorConsumer.consume(e);
175         }
176       });
177       moves.forEach(renamedPaths -> {
178         try {
179           int beforeId = myPathsEnumerator.enumerate(renamedPaths.first);
180           int afterId = myPathsEnumerator.enumerate(renamedPaths.second);
181
182           result.put(beforeId, afterId);
183           result.put(afterId, beforeId);
184         }
185         catch (IOException e) {
186           myFatalErrorConsumer.consume(e);
187         }
188       });
189
190       return result;
191     }
192
193     @NotNull
194     private Collection<String> getParentPaths(@NotNull Collection<String> paths) {
195       Set<String> result = ContainerUtil.newHashSet();
196       for (String path : paths) {
197         while (!path.isEmpty() && !result.contains(path)) {
198           result.add(path);
199           if (myRoots.contains(path)) break;
200
201           path = PathUtil.getParentPath(path);
202         }
203       }
204       return result;
205     }
206
207     @NotNull
208     public PersistentEnumeratorBase<String> getPathsEnumerator() {
209       return myPathsEnumerator;
210     }
211   }
212
213   private static class NullableIntKeyDescriptor implements DataExternalizer<Integer> {
214     @Override
215     public void save(@NotNull DataOutput out, Integer value) throws IOException {
216       if (value == null) {
217         out.writeBoolean(false);
218       }
219       else {
220         out.writeBoolean(true);
221         out.writeInt(value);
222       }
223     }
224
225     @Override
226     public Integer read(@NotNull DataInput in) throws IOException {
227       if (in.readBoolean()) {
228         return in.readInt();
229       }
230       return null;
231     }
232   }
233
234   private static class ToLowerCaseStringDescriptor implements KeyDescriptor<String> {
235     @Override
236     public int getHashCode(String value) {
237       return CaseInsensitiveStringHashingStrategy.INSTANCE.computeHashCode(value);
238     }
239
240     @Override
241     public boolean isEqual(String val1, String val2) {
242       return CaseInsensitiveStringHashingStrategy.INSTANCE.equals(val1, val2);
243     }
244
245     @Override
246     public void save(@NotNull DataOutput out, String value) throws IOException {
247       IOUtil.writeUTF(out, value.toLowerCase());
248     }
249
250     @Override
251     public String read(@NotNull DataInput in) throws IOException {
252       return IOUtil.readUTF(in);
253     }
254   }
255 }