blob: 51c692e420bfef4b3fe8805fa1c048093750380e [file] [log] [blame]
/*
* Copyright 2000-2009 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.history.core.changes;
import com.intellij.history.core.Content;
import com.intellij.history.core.StreamUtil;
import com.intellij.history.utils.LocalHistoryLog;
import com.intellij.util.Producer;
import com.intellij.util.SmartList;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class ChangeSet {
private final long myId;
@Nullable private String myName;
private final long myTimestamp;
private final List<Change> myChanges;
private volatile boolean isLocked = false;
public ChangeSet(long id, long timestamp) {
myId = id;
myTimestamp = timestamp;
myChanges = ContainerUtil.createLockFreeCopyOnWriteList();
}
public ChangeSet(DataInput in) throws IOException {
myId = in.readLong();
myName = StreamUtil.readStringOrNull(in);
myTimestamp = in.readLong();
int count = in.readInt();
List<Change> changes = new ArrayList<Change>(count);
while (count-- > 0) {
changes.add(StreamUtil.readChange(in));
}
myChanges = Collections.unmodifiableList(changes);
isLocked = true;
}
public void write(DataOutput out) throws IOException {
out.writeLong(myId);
StreamUtil.writeStringOrNull(out, myName);
out.writeLong(myTimestamp);
out.writeInt(myChanges.size());
for (Change c : myChanges) {
StreamUtil.writeChange(out, c);
}
}
public void setName(@Nullable String name) {
myName = name;
}
@Nullable
public String getName() {
return myName;
}
public long getTimestamp() {
return myTimestamp;
}
public void lock() {
isLocked = true;
}
@Nullable
public String getLabel() {
return accessChanges(new Producer<String>() {
@Override
public String produce() {
for (Change each : myChanges) {
if (each instanceof PutLabelChange) {
return ((PutLabelChange)each).getName();
}
}
return null;
}
});
}
public int getLabelColor() {
return accessChanges(new Producer<Integer>() {
@Override
public Integer produce() {
for (Change each : myChanges) {
if (each instanceof PutSystemLabelChange) {
return ((PutSystemLabelChange)each).getColor();
}
}
return -1;
}
});
}
public void addChange(final Change c) {
LocalHistoryLog.LOG.assertTrue(!isLocked, "Changset is already locked");
accessChanges(new Runnable() {
@Override
public void run() {
myChanges.add(c);
}
});
}
public List<Change> getChanges() {
return accessChanges(new Producer<List<Change>>() {
@Override
public List<Change> produce() {
if (isLocked) return myChanges;
return Collections.unmodifiableList(new ArrayList<Change>(myChanges));
}
});
}
public boolean isEmpty() {
return accessChanges(new Producer<Boolean>() {
@Override
public Boolean produce() {
return myChanges.isEmpty();
}
});
}
public boolean affectsPath(final String paths) {
return accessChanges(new Producer<Boolean>() {
@Override
public Boolean produce() {
for (Change c : myChanges) {
if (c.affectsPath(paths)) return true;
}
return false;
}
});
}
public boolean isCreationalFor(final String path) {
return accessChanges(new Producer<Boolean>() {
@Override
public Boolean produce() {
for (Change c : myChanges) {
if (c.isCreationalFor(path)) return true;
}
return false;
}
});
}
public List<Content> getContentsToPurge() {
return accessChanges(new Producer<List<Content>>() {
@Override
public List<Content> produce() {
List<Content> result = new ArrayList<Content>();
for (Change c : myChanges) {
result.addAll(c.getContentsToPurge());
}
return result;
}
});
}
public boolean isContentChangeOnly() {
return accessChanges(new Producer<Boolean>() {
@Override
public Boolean produce() {
return myChanges.size() == 1 && getFirstChange() instanceof ContentChange;
}
});
}
public boolean isLabelOnly() {
return accessChanges(new Producer<Boolean>() {
@Override
public Boolean produce() {
return myChanges.size() == 1 && getFirstChange() instanceof PutLabelChange;
}
});
}
public Change getFirstChange() {
return accessChanges(new Producer<Change>() {
@Override
public Change produce() {
return myChanges.get(0);
}
});
}
public Change getLastChange() {
return accessChanges(new Producer<Change>() {
@Override
public Change produce() {
return myChanges.get(myChanges.size() - 1);
}
});
}
public List<String> getAffectedPaths() {
return accessChanges(new Producer<List<String>>() {
@Override
public List<String> produce() {
List<String> result = new SmartList<String>();
for (Change each : myChanges) {
if (each instanceof StructuralChange) {
result.add(((StructuralChange)each).getPath());
}
}
return result;
}
});
}
public String toString() {
return accessChanges(new Producer<String>() {
@Override
public String produce() {
return myChanges.toString();
}
});
}
public long getId() {
return myId;
}
@Override
public final boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ChangeSet change = (ChangeSet)o;
if (myId != change.myId) return false;
return true;
}
@Override
public final int hashCode() {
return (int)(myId ^ (myId >>> 32));
}
public void accept(ChangeVisitor v) throws ChangeVisitor.StopVisitingException {
if (isLocked) {
doAccept(v);
return;
}
synchronized (myChanges) {
doAccept(v);
}
}
private void doAccept(ChangeVisitor v) throws ChangeVisitor.StopVisitingException {
v.begin(this);
for (Change c : ContainerUtil.iterateBackward(myChanges)) {
c.accept(v);
}
v.end(this);
}
private <T> T accessChanges(@NotNull Producer<T> func) {
if (isLocked) {
//noinspection ConstantConditions
return func.produce();
}
synchronized (myChanges) {
//noinspection ConstantConditions
return func.produce();
}
}
private void accessChanges(@NotNull final Runnable func) {
accessChanges(new Producer<Object>() {
@Override
public Object produce() {
func.run();
return null;
}
});
}
}