Cleanup (warnings; formatting)
[idea/community.git] / platform / lvcs-impl / src / com / intellij / history / integration / LocalHistoryImpl.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.history.integration;
17
18 import com.intellij.history.*;
19 import com.intellij.history.core.*;
20 import com.intellij.history.integration.ui.models.DirectoryHistoryDialogModel;
21 import com.intellij.history.integration.ui.models.EntireFileHistoryDialogModel;
22 import com.intellij.history.integration.ui.models.HistoryDialogModel;
23 import com.intellij.history.utils.LocalHistoryLog;
24 import com.intellij.openapi.Disposable;
25 import com.intellij.openapi.application.ApplicationManager;
26 import com.intellij.openapi.application.PathManager;
27 import com.intellij.openapi.application.ReadAction;
28 import com.intellij.openapi.command.CommandProcessor;
29 import com.intellij.openapi.components.ApplicationComponent;
30 import com.intellij.openapi.project.Project;
31 import com.intellij.openapi.util.ShutDownTracker;
32 import com.intellij.openapi.util.io.FileUtil;
33 import com.intellij.openapi.util.registry.Registry;
34 import com.intellij.openapi.vfs.VirtualFile;
35 import com.intellij.openapi.vfs.VirtualFileListener;
36 import com.intellij.openapi.vfs.VirtualFileManager;
37 import com.intellij.util.messages.MessageBus;
38 import com.intellij.util.messages.MessageBusConnection;
39 import org.jetbrains.annotations.NotNull;
40 import org.jetbrains.annotations.Nullable;
41 import org.jetbrains.annotations.TestOnly;
42
43 import java.io.File;
44 import java.io.IOException;
45 import java.util.concurrent.atomic.AtomicBoolean;
46
47 import static com.intellij.history.integration.LocalHistoryUtil.findRevisionIndexToRevert;
48
49 public class LocalHistoryImpl extends LocalHistory implements ApplicationComponent {
50   private final MessageBus myBus;
51   private MessageBusConnection myConnection;
52   private ChangeList myChangeList;
53   private LocalHistoryFacade myVcs;
54   private IdeaGateway myGateway;
55
56   private LocalHistoryEventDispatcher myEventDispatcher;
57
58   private final AtomicBoolean isInitialized = new AtomicBoolean();
59   private Runnable myShutdownTask;
60
61   public static LocalHistoryImpl getInstanceImpl() {
62     return (LocalHistoryImpl)getInstance();
63   }
64
65   public LocalHistoryImpl(@NotNull MessageBus bus) {
66     myBus = bus;
67   }
68
69   @Override
70   public void initComponent() {
71     if (!ApplicationManager.getApplication().isUnitTestMode() && ApplicationManager.getApplication().isHeadlessEnvironment()) return;
72
73     myShutdownTask = () -> disposeComponent();
74     ShutDownTracker.getInstance().registerShutdownTask(myShutdownTask);
75
76     initHistory();
77     isInitialized.set(true);
78   }
79
80   protected void initHistory() {
81     ChangeListStorage storage;
82     try {
83       storage = new ChangeListStorageImpl(getStorageDir());
84     }
85     catch (Throwable e) {
86       LocalHistoryLog.LOG.warn("cannot create storage, in-memory  implementation will be used", e);
87       storage = new InMemoryChangeListStorage();
88     }
89     myChangeList = new ChangeList(storage);
90     myVcs = new LocalHistoryFacade(myChangeList);
91
92     myGateway = new IdeaGateway();
93
94     myEventDispatcher = new LocalHistoryEventDispatcher(myVcs, myGateway);
95
96     CommandProcessor.getInstance().addCommandListener(myEventDispatcher);
97
98     myConnection = myBus.connect();
99     myConnection.subscribe(VirtualFileManager.VFS_CHANGES, myEventDispatcher);
100
101     VirtualFileManager fm = VirtualFileManager.getInstance();
102     fm.addVirtualFileManagerListener(myEventDispatcher);
103   }
104
105   public File getStorageDir() {
106     return new File(getSystemPath(), "LocalHistory");
107   }
108
109   protected String getSystemPath() {
110     return PathManager.getSystemPath();
111   }
112
113   @Override
114   public void disposeComponent() {
115     if (!isInitialized.getAndSet(false)) return;
116
117     long period = Registry.intValue("localHistory.daysToKeep") * 1000L * 60L * 60L * 24L;
118
119     myConnection.disconnect();
120     myConnection = null;
121     VirtualFileManager fm = VirtualFileManager.getInstance();
122     fm.removeVirtualFileManagerListener(myEventDispatcher);
123     CommandProcessor.getInstance().removeCommandListener(myEventDispatcher);
124
125     LocalHistoryLog.LOG.debug("Purging local history...");
126     myChangeList.purgeObsolete(period);
127     myChangeList.close();
128     LocalHistoryLog.LOG.debug("Local history storage successfully closed.");
129
130     ShutDownTracker.getInstance().unregisterShutdownTask(myShutdownTask);
131   }
132
133   @TestOnly
134   public void cleanupForNextTest() {
135     disposeComponent();
136     FileUtil.delete(getStorageDir());
137     initComponent();
138   }
139
140   @Override
141   public LocalHistoryAction startAction(String name) {
142     if (!isInitialized()) return LocalHistoryAction.NULL;
143
144     LocalHistoryActionImpl a = new LocalHistoryActionImpl(myEventDispatcher, name);
145     a.start();
146     return a;
147   }
148
149   @Override
150   public Label putUserLabel(Project p, @NotNull String name) {
151     if (!isInitialized()) return Label.NULL_INSTANCE;
152     myGateway.registerUnsavedDocuments(myVcs);
153     return label(myVcs.putUserLabel(name, getProjectId(p)));
154   }
155
156   private static String getProjectId(Project p) {
157     return p.getLocationHash();
158   }
159
160   @Override
161   public Label putSystemLabel(Project p, @NotNull String name, int color) {
162     if (!isInitialized()) return Label.NULL_INSTANCE;
163     myGateway.registerUnsavedDocuments(myVcs);
164     return label(myVcs.putSystemLabel(name, getProjectId(p), color));
165   }
166
167   public void addVFSListenerAfterLocalHistoryOne(VirtualFileListener virtualFileListener, Disposable disposable) {
168     myEventDispatcher.addVirtualFileListener(virtualFileListener, disposable);
169   }
170
171   private Label label(final LabelImpl impl) {
172     return new Label() {
173       @Override
174       public void revert(@NotNull Project project, @NotNull VirtualFile file) throws LocalHistoryException {
175         revertToLabel(project, file, impl);
176       }
177
178       @Override
179       public ByteContent getByteContent(final String path) {
180         return ReadAction.compute(() -> impl.getByteContent(myGateway.createTransientRootEntryForPathOnly(path), path));
181       }
182     };
183   }
184
185   @Nullable
186   @Override
187   public byte[] getByteContent(final VirtualFile f, final FileRevisionTimestampComparator c) {
188     if (!isInitialized()) return null;
189     if (!myGateway.areContentChangesVersioned(f)) return null;
190     return ReadAction.compute(() -> new ByteContentRetriever(myGateway, myVcs, f, c).getResult());
191   }
192
193   @Override
194   public boolean isUnderControl(VirtualFile f) {
195     return isInitialized() && myGateway.isVersioned(f);
196   }
197
198   private boolean isInitialized() {
199     return isInitialized.get();
200   }
201
202   @Override
203   @NotNull
204   public String getComponentName() {
205     return "Local History";
206   }
207
208   @Nullable
209   public LocalHistoryFacade getFacade() {
210     return myVcs;
211   }
212
213   @Nullable
214   public IdeaGateway getGateway() {
215     return myGateway;
216   }
217
218   private void revertToLabel(@NotNull Project project, @NotNull VirtualFile f, @NotNull LabelImpl impl) throws LocalHistoryException{
219     HistoryDialogModel dirHistoryModel = f.isDirectory()
220                                          ? new DirectoryHistoryDialogModel(project, myGateway, myVcs, f)
221                                          : new EntireFileHistoryDialogModel(project, myGateway, myVcs, f);
222     int leftRev = findRevisionIndexToRevert(dirHistoryModel, impl);
223     if (leftRev < 0) {
224       throw new LocalHistoryException("Couldn't find label revision");
225     }
226     if (leftRev == 0) return; // we shouldn't revert because no changes found to revert;
227     try {
228       dirHistoryModel.selectRevisions(-1, leftRev - 1); //-1 because we should revert all changes up to previous one, but not label-related.
229       dirHistoryModel.createReverter().revert();
230     }
231     catch (IOException e) {
232       throw new LocalHistoryException(String.format("Couldn't revert %s to local history label.", f.getName()), e);
233     }
234   }
235 }