/* * 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.openapi.vcs.changes; import com.google.common.collect.Sets; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Pair; import com.intellij.openapi.vcs.FilePath; import com.intellij.openapi.vcs.FileStatus; import com.intellij.openapi.vcs.VcsKey; import com.intellij.openapi.vcs.history.VcsRevisionNumber; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.BeforeAfter; import com.intellij.vcsUtil.VcsUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; import static com.intellij.util.containers.ContainerUtil.newHashSet; public class ChangeListsIndexes { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.vcs.changes.ChangeListsIndexes"); private final TreeMap<FilePath, FileStatus> myFileToStatus; private final Map<FilePath, Pair<VcsKey, VcsRevisionNumber>> myFileToVcs; ChangeListsIndexes() { myFileToStatus = new TreeMap<>(HierarchicalFilePathComparator.SYSTEM_CASE_SENSITIVE); myFileToVcs = new HashMap<>(); } ChangeListsIndexes(final ChangeListsIndexes idx) { myFileToStatus = new TreeMap<>(idx.myFileToStatus); myFileToVcs = new HashMap<>(idx.myFileToVcs); } void add(final FilePath file, final FileStatus status, final VcsKey key, VcsRevisionNumber number) { myFileToStatus.put(file, status); myFileToVcs.put(file, Pair.create(key, number)); if (LOG.isDebugEnabled()) { LOG.debug("Set status " + status + " for " + file); } } void remove(final FilePath file) { myFileToStatus.remove(file); myFileToVcs.remove(file); } public FileStatus getStatus(final VirtualFile file) { return myFileToStatus.get(VcsUtil.getFilePath(file)); } public FileStatus getStatus(@NotNull FilePath file) { return myFileToStatus.get(file); } public void changeAdded(final Change change, final VcsKey key) { addChangeToIdx(change, key); } public void changeRemoved(final Change change) { final ContentRevision afterRevision = change.getAfterRevision(); final ContentRevision beforeRevision = change.getBeforeRevision(); if (afterRevision != null) { remove(afterRevision.getFile()); } if (beforeRevision != null) { remove(beforeRevision.getFile()); } } @Nullable public VcsKey getVcsFor(@NotNull Change change) { VcsKey key = getVcsForRevision(change.getAfterRevision()); if (key != null) return key; return getVcsForRevision(change.getBeforeRevision()); } @Nullable private VcsKey getVcsForRevision(@Nullable ContentRevision revision) { if (revision != null) { Pair<VcsKey, VcsRevisionNumber> pair = myFileToVcs.get(revision.getFile()); return pair == null ? null : pair.getFirst(); } return null; } private void addChangeToIdx(final Change change, final VcsKey key) { final ContentRevision afterRevision = change.getAfterRevision(); final ContentRevision beforeRevision = change.getBeforeRevision(); if (afterRevision != null) { add(afterRevision.getFile(), change.getFileStatus(), key, beforeRevision == null ? VcsRevisionNumber.NULL : beforeRevision.getRevisionNumber()); } if (beforeRevision != null) { if (afterRevision != null) { if (! Comparing.equal(beforeRevision.getFile(), afterRevision.getFile())) { add(beforeRevision.getFile(), FileStatus.DELETED, key, beforeRevision.getRevisionNumber()); } } else { add(beforeRevision.getFile(), change.getFileStatus(), key, beforeRevision.getRevisionNumber()); } } } /** * this method is called after each local changes refresh and collects all: * - paths that are new in local changes * - paths that are no more changed locally * - paths that were and are changed, but base revision has changed (ex. external update) * (for RemoteRevisionsCache and annotation listener) */ public void getDelta(final ChangeListsIndexes newIndexes, final Set<BaseRevision> toRemove, Set<BaseRevision> toAdd, Set<BeforeAfter<BaseRevision>> toModify) { // this is old final Set<FilePath> oldKeySet = newHashSet(myFileToVcs.keySet()); final Set<FilePath> toRemoveSet = newHashSet(oldKeySet); final Set<FilePath> newKeySet = newIndexes.myFileToVcs.keySet(); final Set<FilePath> toAddSet = newHashSet(newKeySet); toRemoveSet.removeAll(newKeySet); toAddSet.removeAll(oldKeySet); // those that modified oldKeySet.removeAll(toRemoveSet); for (FilePath s : toRemoveSet) { final Pair<VcsKey, VcsRevisionNumber> pair = myFileToVcs.get(s); toRemove.add(fromPairAndPath(s, pair)); } for (FilePath s : toAddSet) { final Pair<VcsKey, VcsRevisionNumber> pair = newIndexes.myFileToVcs.get(s); toAdd.add(fromPairAndPath(s, pair)); } for (FilePath s : oldKeySet) { final Pair<VcsKey, VcsRevisionNumber> old = myFileToVcs.get(s); final Pair<VcsKey, VcsRevisionNumber> newOne = newIndexes.myFileToVcs.get(s); assert old != null && newOne != null; if (! old.equals(newOne)) { toModify.add(new BeforeAfter<>(fromPairAndPath(s, old), fromPairAndPath(s, newOne))); } } } private static BaseRevision fromPairAndPath(FilePath s, Pair<VcsKey, VcsRevisionNumber> pair) { return new BaseRevision(pair.getFirst(), pair.getSecond(), s); } public List<BaseRevision> getAffectedFilesUnderVcs() { final List<BaseRevision> result = new ArrayList<>(); for (Map.Entry<FilePath, Pair<VcsKey, VcsRevisionNumber>> entry : myFileToVcs.entrySet()) { final Pair<VcsKey, VcsRevisionNumber> value = entry.getValue(); result.add(fromPairAndPath(entry.getKey(), value)); } return result; } @NotNull public NavigableSet<FilePath> getAffectedPaths() { return Sets.unmodifiableNavigableSet(myFileToStatus.navigableKeySet()); } }