mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-10-01 19:02:13 +07:00
LH: simplified storage
LH: dumb component in headless environment
This commit is contained in:
@@ -21,10 +21,9 @@ import com.intellij.history.core.changes.Change;
|
||||
import com.intellij.history.core.changes.ChangeSet;
|
||||
import com.intellij.history.core.changes.ChangeVisitor;
|
||||
import com.intellij.history.core.storage.Content;
|
||||
import com.intellij.openapi.application.Application;
|
||||
import com.intellij.history.utils.LocalHistoryLog;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.util.Consumer;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -34,24 +33,23 @@ import java.util.List;
|
||||
public class ChangeList {
|
||||
private final ChangeListStorage myStorage;
|
||||
|
||||
private ChangeSetBlock myCurrentBlock;
|
||||
|
||||
private ChangeSet myCurrentChangeSet;
|
||||
private int myChangeSetDepth;
|
||||
private ChangeSet myCurrentChangeSet;
|
||||
|
||||
private int myIntervalBetweenActivities = 12 * 60 * 60 * 1000; // one day
|
||||
|
||||
public ChangeList(ChangeListStorage storage) {
|
||||
myStorage = storage;
|
||||
myCurrentBlock = storage.createNewBlock();
|
||||
}
|
||||
|
||||
public synchronized void save() {
|
||||
flushChanges(true);
|
||||
myStorage.flush();
|
||||
}
|
||||
|
||||
public synchronized void close() {
|
||||
flushChanges(true);
|
||||
if (!ApplicationManager.getApplication().isUnitTestMode()) {
|
||||
LocalHistoryLog.LOG.assertTrue(myCurrentChangeSet == null, "current changes won't be saved: " + myCurrentChangeSet);
|
||||
}
|
||||
myStorage.close();
|
||||
}
|
||||
|
||||
@@ -73,7 +71,6 @@ public class ChangeList {
|
||||
|
||||
private void doBeginChangeSet() {
|
||||
myCurrentChangeSet = new ChangeSet(myStorage.nextId(), Clock.getCurrentTimestamp());
|
||||
myCurrentBlock.add(myCurrentChangeSet);
|
||||
}
|
||||
|
||||
public synchronized boolean forceBeginChangeSet() {
|
||||
@@ -86,7 +83,7 @@ public class ChangeList {
|
||||
}
|
||||
|
||||
public synchronized boolean endChangeSet(String name) {
|
||||
assert myChangeSetDepth > 0;
|
||||
LocalHistoryLog.LOG.assertTrue(myChangeSetDepth > 0, "not balanced 'begin/end-change set' calls");
|
||||
|
||||
myChangeSetDepth--;
|
||||
if (myChangeSetDepth > 0) return false;
|
||||
@@ -96,14 +93,14 @@ public class ChangeList {
|
||||
|
||||
private boolean doEndChangeSet(String name) {
|
||||
if (myCurrentChangeSet.getChanges().isEmpty()) {
|
||||
myCurrentBlock.removeLast();
|
||||
myCurrentChangeSet = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
myCurrentChangeSet.setName(name);
|
||||
myStorage.writeNextSet(myCurrentChangeSet);
|
||||
myCurrentChangeSet = null;
|
||||
|
||||
flushChanges(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -116,13 +113,12 @@ public class ChangeList {
|
||||
return result;
|
||||
}
|
||||
|
||||
// todo synchronization issue: changeset may me modified while being iterated
|
||||
public synchronized Iterable<ChangeSet> iterChanges() {
|
||||
return new Iterable<ChangeSet>() {
|
||||
public Iterator<ChangeSet> iterator() {
|
||||
return new Iterator<ChangeSet>() {
|
||||
private ChangeSetBlock currentBlock;
|
||||
private Iterator<ChangeSet> currentIter;
|
||||
|
||||
private ChangeSetHolder currentBlock;
|
||||
private ChangeSet next = fetchNext();
|
||||
|
||||
public boolean hasNext() {
|
||||
@@ -138,19 +134,21 @@ public class ChangeList {
|
||||
private ChangeSet fetchNext() {
|
||||
if (currentBlock == null) {
|
||||
synchronized (ChangeList.this) {
|
||||
currentBlock = myCurrentBlock;
|
||||
List<ChangeSet> copy = new ArrayList<ChangeSet>(currentBlock.changes);
|
||||
currentIter = ContainerUtil.iterateBackward(copy).iterator();
|
||||
if (myCurrentChangeSet != null) {
|
||||
currentBlock = new ChangeSetHolder(-1, myCurrentChangeSet);
|
||||
}
|
||||
else {
|
||||
currentBlock = myStorage.readPrevious(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
while (!currentIter.hasNext()) {
|
||||
else {
|
||||
synchronized (ChangeList.this) {
|
||||
currentBlock = myStorage.readPrevious(currentBlock);
|
||||
currentBlock = myStorage.readPrevious(currentBlock.id);
|
||||
}
|
||||
if (currentBlock == null) return null;
|
||||
currentIter = ContainerUtil.iterateBackward(currentBlock.changes).iterator();
|
||||
}
|
||||
return currentIter.next();
|
||||
if (currentBlock == null) return null;
|
||||
return currentBlock.changeSet;
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
@@ -161,20 +159,6 @@ public class ChangeList {
|
||||
};
|
||||
}
|
||||
|
||||
private void flushChanges(boolean force) {
|
||||
if (myChangeSetDepth > 0) return;
|
||||
if (myCurrentBlock.shouldFlush(force) || flushEveryChangeSetInTests()) {
|
||||
myStorage.writeNextBlock(myCurrentBlock);
|
||||
myCurrentBlock = myStorage.createNewBlock();
|
||||
}
|
||||
myStorage.flush();
|
||||
}
|
||||
|
||||
private boolean flushEveryChangeSetInTests() {
|
||||
Application app = ApplicationManager.getApplication();
|
||||
return app == null || app.isUnitTestMode();
|
||||
}
|
||||
|
||||
public void accept(ChangeVisitor v) {
|
||||
try {
|
||||
for (ChangeSet change : iterChanges()) {
|
||||
|
||||
@@ -20,21 +20,17 @@ import com.intellij.history.core.changes.ChangeSet;
|
||||
import com.intellij.util.Consumer;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface ChangeListStorage {
|
||||
void close();
|
||||
|
||||
long nextId();
|
||||
|
||||
ChangeSetBlock createNewBlock();
|
||||
|
||||
@Nullable
|
||||
ChangeSetBlock readPrevious(ChangeSetBlock block);
|
||||
ChangeSetHolder readPrevious(int id);
|
||||
|
||||
void purge(long period, int intervalBetweenActivities, Consumer<ChangeSet> processor);
|
||||
|
||||
void writeNextBlock(ChangeSetBlock block);
|
||||
void writeNextSet(ChangeSet changeSet);
|
||||
|
||||
void flush();
|
||||
}
|
||||
@@ -27,21 +27,16 @@ import com.intellij.util.io.storage.AbstractStorage;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ChangeListStorageImpl implements ChangeListStorage {
|
||||
private static final int VERSION = 3;
|
||||
|
||||
private static final int VERSION = 4;
|
||||
private static final String STORAGE_FILE = "changes";
|
||||
|
||||
private final File myStorageDir;
|
||||
|
||||
private final LinkedStorage myStorage;
|
||||
|
||||
public ChangeListStorageImpl(File storageDir) {
|
||||
myStorageDir = storageDir;
|
||||
try {
|
||||
myStorage = createStorage(myStorageDir);
|
||||
myStorage = createStorage(storageDir);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw handleError(e);
|
||||
@@ -90,27 +85,18 @@ public class ChangeListStorageImpl implements ChangeListStorage {
|
||||
return myStorage.nextId();
|
||||
}
|
||||
|
||||
public synchronized ChangeSetBlock createNewBlock() {
|
||||
return new ChangeSetBlock(0);
|
||||
}
|
||||
|
||||
public synchronized ChangeSetBlock readPrevious(ChangeSetBlock block) {
|
||||
int prevId = block.id == 0 ? myStorage.getLastRecord() : myStorage.getPrevRecord(block.id);
|
||||
public synchronized ChangeSetHolder readPrevious(int id) {
|
||||
int prevId = id == -1 ? myStorage.getLastRecord() : myStorage.getPrevRecord(id);
|
||||
if (prevId == 0) return null;
|
||||
assert prevId != block.id;
|
||||
|
||||
return doReadBlock(prevId);
|
||||
}
|
||||
|
||||
private ChangeSetBlock doReadBlock(int id) {
|
||||
private ChangeSetHolder doReadBlock(int id) {
|
||||
try {
|
||||
DataInputStream in = myStorage.readStream(id);
|
||||
try {
|
||||
int size = in.readInt();
|
||||
List<ChangeSet> changes = new ArrayList<ChangeSet>(size);
|
||||
while (size-- > 0) {
|
||||
changes.add(new ChangeSet(in));
|
||||
}
|
||||
return new ChangeSetBlock(id, changes);
|
||||
return new ChangeSetHolder(id, new ChangeSet(in));
|
||||
}
|
||||
finally {
|
||||
in.close();
|
||||
@@ -121,15 +107,12 @@ public class ChangeListStorageImpl implements ChangeListStorage {
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void writeNextBlock(ChangeSetBlock block) {
|
||||
public synchronized void writeNextSet(ChangeSet changeSet) {
|
||||
try {
|
||||
block.id = myStorage.createNextRecord();
|
||||
AbstractStorage.StorageDataOutput out = myStorage.writeStream(block.id);
|
||||
int id = myStorage.createNextRecord();
|
||||
AbstractStorage.StorageDataOutput out = myStorage.writeStream(id);
|
||||
try {
|
||||
out.writeInt(block.changes.size());
|
||||
for (ChangeSet each : block.changes) {
|
||||
each.write(out);
|
||||
}
|
||||
changeSet.write(out);
|
||||
}
|
||||
finally {
|
||||
out.close();
|
||||
@@ -143,10 +126,8 @@ public class ChangeListStorageImpl implements ChangeListStorage {
|
||||
public synchronized void purge(long period, int intervalBetweenActivities, Consumer<ChangeSet> processor) {
|
||||
int eachBlockId = findFirstObsoleteBlock(period, intervalBetweenActivities);
|
||||
try {
|
||||
while(eachBlockId != 0) {
|
||||
for (ChangeSet eachChangeSet : doReadBlock(eachBlockId).changes) {
|
||||
processor.consume(eachChangeSet);
|
||||
}
|
||||
while (eachBlockId != 0) {
|
||||
processor.consume(doReadBlock(eachBlockId).changeSet);
|
||||
myStorage.deleteRecord(eachBlockId);
|
||||
eachBlockId = myStorage.getPrevRecord(eachBlockId);
|
||||
}
|
||||
|
||||
+5
-30
@@ -20,37 +20,12 @@ import com.intellij.history.core.changes.ChangeSet;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ChangeSetBlock {
|
||||
private static final int BLOCK_SIZE = 1000;
|
||||
public int id;
|
||||
public final List<ChangeSet> changes;
|
||||
public class ChangeSetHolder {
|
||||
public final int id;
|
||||
public final ChangeSet changeSet;
|
||||
|
||||
public ChangeSetBlock(int id) {
|
||||
public ChangeSetHolder(int id, ChangeSet changeSet) {
|
||||
this.id = id;
|
||||
this.changes = new ArrayList<ChangeSet>(BLOCK_SIZE);
|
||||
}
|
||||
|
||||
ChangeSetBlock(int id, List<ChangeSet> changes) {
|
||||
this.id = id;
|
||||
this.changes = changes;
|
||||
}
|
||||
|
||||
public void add(ChangeSet changeSet) {
|
||||
changes.add(changeSet);
|
||||
}
|
||||
|
||||
public void removeLast() {
|
||||
changes.remove(changes.size() - 1);
|
||||
}
|
||||
|
||||
public boolean shouldFlush(boolean force) {
|
||||
int count = 0;
|
||||
for (ChangeSet each : changes) {
|
||||
count += each.getChanges().size();
|
||||
|
||||
if (count >= BLOCK_SIZE) return true;
|
||||
if (force && count > 0) return true;
|
||||
}
|
||||
return false;
|
||||
this.changeSet = changeSet;
|
||||
}
|
||||
}
|
||||
@@ -35,6 +35,7 @@ import com.intellij.openapi.vfs.ex.VirtualFileManagerEx;
|
||||
import com.intellij.util.io.storage.HeavyProcessLatch;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
|
||||
import java.io.File;
|
||||
@@ -58,6 +59,8 @@ public class LocalHistoryImpl extends LocalHistory implements ApplicationCompone
|
||||
}
|
||||
|
||||
public void initComponent() {
|
||||
if (!ApplicationManager.getApplication().isUnitTestMode() && ApplicationManager.getApplication().isHeadlessEnvironment()) return;
|
||||
|
||||
myShutdownTask = new Runnable() {
|
||||
public void run() {
|
||||
disposeComponent();
|
||||
@@ -193,10 +196,12 @@ public class LocalHistoryImpl extends LocalHistory implements ApplicationCompone
|
||||
return "Local History";
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public LocalHistoryFacade getFacade() {
|
||||
return myVcs;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public IdeaGateway getGateway() {
|
||||
return myGateway;
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import com.intellij.history.core.changes.ContentChange;
|
||||
import com.intellij.history.core.changes.StructuralChange;
|
||||
import com.intellij.history.integration.IdeaGateway;
|
||||
import com.intellij.history.integration.LocalHistoryImpl;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.command.undo.*;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
@@ -50,11 +51,12 @@ public class FileUndoProvider extends VirtualFileAdapter implements UndoProvider
|
||||
}
|
||||
|
||||
public FileUndoProvider(Project project) {
|
||||
myProject = project;
|
||||
myProject = project;
|
||||
if (myProject == null) return;
|
||||
|
||||
myLocalHistory = LocalHistoryImpl.getInstanceImpl().getFacade();
|
||||
myGateway = LocalHistoryImpl.getInstanceImpl().getGateway();
|
||||
if (myLocalHistory == null || myGateway == null) return; // local history was not initialized (e.g. in headless environment)
|
||||
|
||||
getFileManager().addVirtualFileListener(this, project);
|
||||
myLocalHistory.addListener(new LocalHistoryFacade.Listener() {
|
||||
|
||||
+7
-13
@@ -23,7 +23,7 @@ import java.util.List;
|
||||
|
||||
public class InMemoryChangeListStorage implements ChangeListStorage {
|
||||
private int myCurrentId;
|
||||
private List<ChangeSetBlock> myBlocks = new ArrayList<ChangeSetBlock>();
|
||||
private List<ChangeSet> mySets = new ArrayList<ChangeSet>();
|
||||
|
||||
public void close() {
|
||||
}
|
||||
@@ -32,20 +32,14 @@ public class InMemoryChangeListStorage implements ChangeListStorage {
|
||||
return myCurrentId++;
|
||||
}
|
||||
|
||||
public ChangeSetBlock createNewBlock() {
|
||||
return new ChangeSetBlock(-1);
|
||||
public ChangeSetHolder readPrevious(int id) {
|
||||
if (mySets.isEmpty()) return null;
|
||||
if (id == -1) return new ChangeSetHolder(mySets.size() - 1, mySets.get(mySets.size() - 1));
|
||||
return id == 0 ? null : new ChangeSetHolder(id -1, mySets.get(id - 1));
|
||||
}
|
||||
|
||||
public ChangeSetBlock readPrevious(ChangeSetBlock block) {
|
||||
if (myBlocks.isEmpty()) return null;
|
||||
if (block.id == 0) return null;
|
||||
if (block.id == -1) return myBlocks.get(myBlocks.size() - 1);
|
||||
return myBlocks.get(block.id - 1);
|
||||
}
|
||||
|
||||
public void writeNextBlock(ChangeSetBlock block) {
|
||||
myBlocks.add(block);
|
||||
block.id = myBlocks.size() - 1;
|
||||
public void writeNextSet(ChangeSet changeSet) {
|
||||
mySets.add(changeSet);
|
||||
}
|
||||
|
||||
public void purge(long period, int intervalBetweenActivities, Consumer<ChangeSet> processor) {
|
||||
|
||||
Reference in New Issue
Block a user