| package com.intellij.openapi.vcs.changes; |
| |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.util.Comparing; |
| import com.intellij.openapi.util.Computable; |
| import com.intellij.openapi.util.registry.Registry; |
| import com.intellij.openapi.util.text.StringUtil; |
| import com.intellij.openapi.vcs.ProjectLevelVcsManager; |
| import com.intellij.openapi.vcs.history.VcsRevisionNumber; |
| import com.intellij.openapi.vfs.VirtualFile; |
| import com.intellij.util.containers.OpenTHashSet; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.util.*; |
| |
| /** |
| * @author yole |
| */ |
| public class LocalChangeListImpl extends LocalChangeList { |
| private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.vcs.changes.ChangeList"); |
| |
| private final Project myProject; |
| private Collection<Change> myChanges = new HashSet<Change>(); |
| private Collection<Change> myReadChangesCache = null; |
| private String myId; |
| @NotNull private String myName; |
| private String myComment = ""; |
| @Nullable private Object myData; |
| |
| private boolean myIsDefault = false; |
| private boolean myIsReadOnly = false; |
| private OpenTHashSet<Change> myChangesBeforeUpdate; |
| |
| public static LocalChangeListImpl createEmptyChangeListImpl(Project project, String name) { |
| return new LocalChangeListImpl(project, name); |
| } |
| |
| private LocalChangeListImpl(Project project, final String name) { |
| myProject = project; |
| myId = UUID.randomUUID().toString(); |
| setNameImpl(name); |
| } |
| |
| private LocalChangeListImpl(LocalChangeListImpl origin) { |
| myId = origin.getId(); |
| myProject = origin.myProject; |
| setNameImpl(origin.myName); |
| } |
| |
| public Collection<Change> getChanges() { |
| createReadChangesCache(); |
| return myReadChangesCache; |
| } |
| |
| private void createReadChangesCache() { |
| if (myReadChangesCache == null) { |
| myReadChangesCache = Collections.unmodifiableCollection(new HashSet<Change>(myChanges)); |
| } |
| } |
| |
| @NotNull |
| @Override |
| public String getId() { |
| return myId; |
| } |
| |
| @NotNull |
| public String getName() { |
| return myName; |
| } |
| |
| public void setName(@NotNull final String name) { |
| if (! myName.equals(name)) { |
| setNameImpl(name); |
| } |
| } |
| |
| public String getComment() { |
| return myComment; |
| } |
| |
| // same as for setName() |
| public void setComment(final String comment) { |
| if (! Comparing.equal(comment, myComment)) { |
| myComment = comment != null ? comment : ""; |
| } |
| } |
| |
| void setNameImpl(@NotNull final String name) { |
| if (StringUtil.isEmptyOrSpaces(name) && Registry.is("vcs.log.empty.change.list.creation")) { |
| LOG.info("Creating a changelist with empty name"); |
| } |
| myName = name; |
| } |
| |
| void setCommentImpl(final String comment) { |
| myComment = comment; |
| } |
| |
| public boolean isDefault() { |
| return myIsDefault; |
| } |
| |
| void setDefault(final boolean isDefault) { |
| myIsDefault = isDefault; |
| } |
| |
| public boolean isReadOnly() { |
| return myIsReadOnly; |
| } |
| |
| public void setReadOnly(final boolean isReadOnly) { |
| myIsReadOnly = isReadOnly; |
| } |
| |
| void setData(@Nullable Object data) { |
| myData = data; |
| } |
| |
| @Nullable |
| @Override |
| public Object getData() { |
| return myData; |
| } |
| |
| void addChange(Change change) { |
| myReadChangesCache = null; |
| myChanges.add(change); |
| } |
| |
| Change removeChange(Change change) { |
| for (Change localChange : myChanges) { |
| if (localChange.equals(change)) { |
| myChanges.remove(localChange); |
| myReadChangesCache = null; |
| return localChange; |
| } |
| } |
| return null; |
| } |
| |
| Collection<Change> startProcessingChanges(final Project project, @Nullable final VcsDirtyScope scope) { |
| createReadChangesCache(); |
| final Collection<Change> result = new ArrayList<Change>(); |
| myChangesBeforeUpdate = new OpenTHashSet<Change>(myChanges); |
| for (Change oldBoy : myChangesBeforeUpdate) { |
| final ContentRevision before = oldBoy.getBeforeRevision(); |
| final ContentRevision after = oldBoy.getAfterRevision(); |
| if (scope == null || before != null && scope.belongsTo(before.getFile()) || after != null && scope.belongsTo(after.getFile()) |
| || isIgnoredChange(oldBoy, project)) { |
| result.add(oldBoy); |
| myChanges.remove(oldBoy); |
| myReadChangesCache = null; |
| } |
| } |
| return result; |
| } |
| |
| private static boolean isIgnoredChange(@NotNull Change change, @NotNull Project project) { |
| boolean beforeRevIgnored = change.getBeforeRevision() == null || isIgnoredRevision(change.getBeforeRevision(), project); |
| boolean afterRevIgnored = change.getAfterRevision() == null || isIgnoredRevision(change.getAfterRevision(), project); |
| return beforeRevIgnored && afterRevIgnored; |
| } |
| |
| private static boolean isIgnoredRevision(final @NotNull ContentRevision revision, final @NotNull Project project) { |
| return ApplicationManager.getApplication().runReadAction(new Computable<Boolean>() { |
| @Override |
| public Boolean compute() { |
| if (project.isDisposed()) { |
| return false; |
| } |
| VirtualFile vFile = revision.getFile().getVirtualFile(); |
| return vFile != null && ProjectLevelVcsManager.getInstance(project).isIgnored(vFile); |
| } |
| }); |
| } |
| |
| boolean processChange(Change change) { |
| LOG.debug("[process change] for '" + myName + "' isDefault: " + myIsDefault + " change: " + |
| ChangesUtil.getFilePath(change).getPath()); |
| if (myIsDefault) { |
| LOG.debug("[process change] adding because default"); |
| addChange(change); |
| return true; |
| } |
| |
| for (Change oldChange : myChangesBeforeUpdate) { |
| if (Comparing.equal(oldChange, change)) { |
| LOG.debug("[process change] adding bacuae equal to old: " + ChangesUtil.getFilePath(oldChange).getPath()); |
| addChange(change); |
| return true; |
| } |
| } |
| LOG.debug("[process change] not found"); |
| return false; |
| } |
| |
| boolean doneProcessingChanges(final List<Change> removedChanges, final List<Change> addedChanges) { |
| boolean changesDetected = (myChanges.size() != myChangesBeforeUpdate.size()); |
| |
| for (Change newChange : myChanges) { |
| Change oldChange = findOldChange(newChange); |
| if (oldChange == null) { |
| addedChanges.add(newChange); |
| } |
| } |
| changesDetected |= (! addedChanges.isEmpty()); |
| final List<Change> removed = new ArrayList<Change>(myChangesBeforeUpdate); |
| // since there are SAME objects... |
| removed.removeAll(myChanges); |
| removedChanges.addAll(removed); |
| changesDetected = changesDetected || (! removedChanges.isEmpty()); |
| |
| myReadChangesCache = null; |
| return changesDetected; |
| } |
| |
| @Nullable |
| private Change findOldChange(final Change newChange) { |
| Change oldChange = myChangesBeforeUpdate.get(newChange); |
| if (oldChange != null && sameBeforeRevision(oldChange, newChange) && |
| newChange.getFileStatus().equals(oldChange.getFileStatus())) { |
| return oldChange; |
| } |
| return null; |
| } |
| |
| private static boolean sameBeforeRevision(final Change change1, final Change change2) { |
| final ContentRevision b1 = change1.getBeforeRevision(); |
| final ContentRevision b2 = change2.getBeforeRevision(); |
| if (b1 != null && b2 != null) { |
| final VcsRevisionNumber rn1 = b1.getRevisionNumber(); |
| final VcsRevisionNumber rn2 = b2.getRevisionNumber(); |
| final boolean isBinary1 = (b1 instanceof BinaryContentRevision); |
| final boolean isBinary2 = (b2 instanceof BinaryContentRevision); |
| return rn1 != VcsRevisionNumber.NULL && rn2 != VcsRevisionNumber.NULL && rn1.compareTo(rn2) == 0 && isBinary1 == isBinary2; |
| } |
| return b1 == null && b2 == null; |
| } |
| |
| public boolean equals(final Object o) { |
| if (this == o) return true; |
| if (o == null || getClass() != o.getClass()) return false; |
| final LocalChangeListImpl list = (LocalChangeListImpl)o; |
| return myName.equals(list.myName); |
| } |
| |
| public int hashCode() { |
| return myName.hashCode(); |
| } |
| |
| @Override |
| public String toString() { |
| return myName.trim(); |
| } |
| |
| public LocalChangeList copy() { |
| final LocalChangeListImpl copy = new LocalChangeListImpl(this); |
| copy.myComment = myComment; |
| copy.myIsDefault = myIsDefault; |
| copy.myIsReadOnly = myIsReadOnly; |
| copy.myData = myData; |
| |
| if (myChanges != null) { |
| copy.myChanges = new HashSet<Change>(myChanges); |
| } |
| |
| if (myChangesBeforeUpdate != null) { |
| copy.myChangesBeforeUpdate = new OpenTHashSet<Change>((Collection<Change>)myChangesBeforeUpdate); |
| } |
| |
| if (myReadChangesCache != null) { |
| copy.myReadChangesCache = new HashSet<Change>(myReadChangesCache); |
| } |
| |
| return copy; |
| } |
| |
| @Nullable |
| public ChangeListEditHandler getEditHandler() { |
| return null; |
| } |
| |
| public void setId(String id) { |
| myId = id; |
| } |
| } |