blob: e67f8f91e22abccead5366e78e2a9d3af5d733e3 [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.vcs.log.data;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.ThrowableComputable;
import com.intellij.util.CommonProcessors;
import com.intellij.util.NotNullFunction;
import com.intellij.util.io.IOUtil;
import com.intellij.util.io.KeyDescriptor;
import com.intellij.util.io.Page;
import com.intellij.util.io.PersistentEnumerator;
import com.intellij.vcs.log.Hash;
import com.intellij.vcs.log.impl.HashImpl;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.File;
import java.io.IOException;
/**
* Supports the int <-> Hash persistent mapping.
*/
public class VcsLogHashMap implements Disposable {
private static final File LOG_CACHE_APP_DIR = new File(PathManager.getSystemPath(), "vcs-log");
private static final Logger LOG = Logger.getInstance(VcsLogHashMap.class);
private final PersistentEnumerator<Hash> myPersistentEnumerator;
VcsLogHashMap(@NotNull Project project) throws IOException {
final File myMapFile = new File(LOG_CACHE_APP_DIR, project.getName() + "." + project.getLocationHash());
Disposer.register(project, this);
myPersistentEnumerator = IOUtil.openCleanOrResetBroken(new ThrowableComputable<PersistentEnumerator<Hash>, IOException>() {
@Override
public PersistentEnumerator<Hash> compute() throws IOException {
return new PersistentEnumerator<Hash>(myMapFile, new MyHashKeyDescriptor(), Page.PAGE_SIZE);
}
}, myMapFile);
}
@Nullable
private Hash doGetHash(int index) throws IOException {
return myPersistentEnumerator.valueOf(index);
}
private int getOrPut(@NotNull Hash hash) throws IOException {
return myPersistentEnumerator.enumerate(hash);
}
public int getCommitIndex(@NotNull Hash hash) {
try {
return getOrPut(hash);
}
catch (IOException e) {
throw new RuntimeException(e); // TODO the map is corrupted => need to rebuild
}
}
@NotNull
public Hash getHash(int commitIndex) {
try {
Hash hash = doGetHash(commitIndex);
if (hash == null) {
throw new RuntimeException("Unknown commit index: " + commitIndex); // TODO this shouldn't happen => need to recreate the map
}
return hash;
}
catch (IOException e) {
throw new RuntimeException(e); // TODO map is corrupted => need to recreate it
}
}
@NotNull
public NotNullFunction<Hash, Integer> asIndexGetter() {
return new NotNullFunction<Hash, Integer>() {
@NotNull
@Override
public Integer fun(Hash hash) {
return getCommitIndex(hash);
}
};
}
@NotNull
public NotNullFunction<Integer, Hash> asHashGetter() {
return new NotNullFunction<Integer, Hash>() {
@NotNull
@Override
public Hash fun(Integer commitIndex) {
return getHash(commitIndex);
}
};
}
public void flush() {
myPersistentEnumerator.force();
}
@Override
public void dispose() {
try {
myPersistentEnumerator.close();
}
catch (IOException e) {
LOG.warn(e);
}
}
@Nullable
Hash findHash(@NotNull final Condition<Hash> condition) throws IOException {
final Ref<Hash> hashRef = Ref.create();
myPersistentEnumerator.iterateData(new CommonProcessors.FindProcessor<Hash>() {
@Override
protected boolean accept(Hash hash) {
boolean matches = condition.value(hash);
if (matches) {
hashRef.set(hash);
}
return matches;
}
});
return hashRef.get();
}
private static class MyHashKeyDescriptor implements KeyDescriptor<Hash> {
@Override
public void save(@NotNull DataOutput out, Hash value) throws IOException {
out.writeUTF(value.asString());
}
@Override
public Hash read(@NotNull DataInput in) throws IOException {
return HashImpl.build(in.readUTF());
}
@Override
public int getHashCode(Hash value) {
return value.hashCode();
}
@Override
public boolean isEqual(Hash val1, Hash val2) {
return val1.equals(val2);
}
}
}