| /* |
| * 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.openapi.vcs.changes; |
| |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.util.Comparing; |
| import com.intellij.openapi.util.Couple; |
| import com.intellij.openapi.util.Pair; |
| import com.intellij.openapi.util.SystemInfo; |
| import com.intellij.openapi.util.io.FileUtil; |
| import com.intellij.openapi.util.text.StringUtil; |
| import com.intellij.openapi.vcs.*; |
| import com.intellij.openapi.vfs.VirtualFile; |
| import com.intellij.util.Function; |
| import com.intellij.util.IncorrectOperationException; |
| import com.intellij.util.PlusMinusModify; |
| import com.intellij.util.ThreeState; |
| import com.intellij.util.containers.ContainerUtil; |
| import com.intellij.util.containers.MultiMap; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.io.File; |
| import java.util.*; |
| |
| /** should work under _external_ lock |
| * just logic here: do modifications to group of change lists |
| */ |
| public class ChangeListWorker implements ChangeListsWriteOperations { |
| private final static Logger LOG = Logger.getInstance("#com.intellij.openapi.vcs.changes.ChangeListWorker"); |
| |
| private final Project myProject; |
| private final Map<String, LocalChangeList> myMap; |
| // in fact, a kind of local change |
| private final DeletedFilesHolder myLocallyDeleted; |
| private final SwitchedFileHolder mySwitchedHolder; |
| private LocalChangeList myDefault; |
| |
| private ChangeListsIndexes myIdx; |
| private final ChangesDelta myDelta; |
| private final List<String> myListsToDisappear; |
| |
| public ChangeListWorker(final Project project, final PlusMinusModify<BaseRevision> deltaListener) { |
| myProject = project; |
| myMap = new LinkedHashMap<String, LocalChangeList>(); |
| myIdx = new ChangeListsIndexes(); |
| myLocallyDeleted = new DeletedFilesHolder(); |
| mySwitchedHolder = new SwitchedFileHolder(project, FileHolder.HolderType.SWITCHED); |
| |
| myDelta = new ChangesDelta(deltaListener); |
| myListsToDisappear = new LinkedList<String>(); |
| } |
| |
| private ChangeListWorker(final ChangeListWorker worker) { |
| myProject = worker.myProject; |
| myMap = new LinkedHashMap<String, LocalChangeList>(); |
| myIdx = new ChangeListsIndexes(worker.myIdx); |
| myLocallyDeleted = worker.myLocallyDeleted.copy(); |
| mySwitchedHolder = worker.mySwitchedHolder.copy(); |
| myDelta = worker.myDelta; |
| myListsToDisappear = new LinkedList<String>(worker.myListsToDisappear); |
| |
| LocalChangeList defaultList = null; |
| for (LocalChangeList changeList : worker.myMap.values()) { |
| final LocalChangeList copy = changeList.copy(); |
| |
| final String changeListName = copy.getName(); |
| myMap.put(changeListName, copy); |
| if (copy.isDefault()) { |
| defaultList = copy; |
| } |
| } |
| if (defaultList == null) { |
| LOG.info("default list not found when copy"); |
| defaultList = myMap.get(worker.getDefaultListName()); |
| } |
| |
| if (defaultList == null) { |
| LOG.info("default list not found when copy in original object too"); |
| if (! myMap.isEmpty()) { |
| defaultList = myMap.values().iterator().next(); |
| } else { |
| // can be when there's no vcs configured |
| ///LOG.error("no changelists at all"); |
| } |
| } |
| myDefault = defaultList; |
| } |
| |
| public void onAfterWorkerSwitch(@NotNull final ChangeListWorker previous) { |
| checkForMultipleCopiesNotMove(myDelta.step(previous.myIdx, myIdx)); |
| } |
| |
| private void checkForMultipleCopiesNotMove(boolean somethingChanged) { |
| final MultiMap<FilePath, Pair<Change, String>> moves = new MultiMap<FilePath, Pair<Change, String>>() { |
| @NotNull |
| protected Collection<Pair<Change, String>> createCollection() { |
| return new LinkedList<Pair<Change, String>>(); |
| } |
| }; |
| |
| for (LocalChangeList changeList : myMap.values()) { |
| final Collection<Change> changes = changeList.getChanges(); |
| for (Change change : changes) { |
| if (change.isMoved() || change.isRenamed()) { |
| moves.putValue(change.getBeforeRevision().getFile(), Pair.create(change, changeList.getName())); |
| } |
| } |
| } |
| for (FilePath filePath : moves.keySet()) { |
| final List<Pair<Change, String>> copies = (List<Pair<Change, String>>) moves.get(filePath); |
| if (copies.size() == 1) continue; |
| Collections.sort(copies, MyChangesAfterRevisionComparator.getInstance()); |
| for (int i = 0; i < (copies.size() - 1); i++) { |
| somethingChanged = true; |
| final Pair<Change, String> item = copies.get(i); |
| final Change oldChange = item.getFirst(); |
| final Change newChange = new Change(null, oldChange.getAfterRevision()); |
| |
| final LocalChangeListImpl list = (LocalChangeListImpl) myMap.get(item.getSecond()); |
| list.removeChange(oldChange); |
| list.addChange(newChange); |
| |
| final VcsKey key = myIdx.getVcsFor(oldChange); |
| myIdx.changeRemoved(oldChange); |
| myIdx.changeAdded(newChange, key); |
| } |
| } |
| if (somethingChanged) { |
| FileStatusManager.getInstance(myProject).fileStatusesChanged(); |
| } |
| } |
| |
| public ChangeListWorker copy() { |
| return new ChangeListWorker(this); |
| } |
| |
| public boolean findListByName(@NotNull final String name) { |
| return myMap.containsKey(name); |
| } |
| |
| @Nullable |
| public LocalChangeList getCopyByName(final String name) { |
| return myMap.get(name); |
| } |
| |
| @Nullable |
| public LocalChangeList getChangeList(String id) { |
| for (LocalChangeList changeList : myMap.values()) { |
| if (changeList.getId().equals(id)) { |
| return changeList.copy(); |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * @return if list with name exists, return previous default list name or null of there wasn't previous |
| */ |
| @Nullable |
| public String setDefault(final String name) { |
| final LocalChangeList newDefault = myMap.get(name); |
| if (newDefault == null) { |
| return null; |
| } |
| String previousName = null; |
| if (myDefault != null) { |
| ((LocalChangeListImpl) myDefault).setDefault(false); |
| previousName = myDefault.getName(); |
| } |
| |
| ((LocalChangeListImpl) newDefault).setDefault(true); |
| myDefault = newDefault; |
| |
| return previousName; |
| } |
| |
| public boolean setReadOnly(final String name, final boolean value) { |
| final LocalChangeList list = myMap.get(name); |
| if (list != null) { |
| list.setReadOnly(value); |
| } |
| return list != null; |
| } |
| |
| public LocalChangeList addChangeList(@NotNull final String name, @Nullable final String comment, @Nullable Object data) { |
| return addChangeList(null, name, comment, false, data); |
| } |
| |
| LocalChangeList addChangeList(String id, @NotNull final String name, @Nullable final String description, final boolean inUpdate, |
| @Nullable Object data) { |
| final boolean contains = myMap.containsKey(name); |
| LOG.assertTrue(! contains, "Attempt to create duplicate changelist " + name); |
| final LocalChangeListImpl newList = (LocalChangeListImpl) LocalChangeList.createEmptyChangeList(myProject, name); |
| newList.setData(data); |
| |
| if (description != null) { |
| newList.setCommentImpl(description); |
| } |
| if (id != null) { |
| newList.setId(id); |
| } |
| myMap.put(name, newList); |
| if (inUpdate) { |
| // scope is not important: nothing had been added jet, nothing to move to "old state" members |
| newList.startProcessingChanges(myProject, null); // this is executed only when use through GATE |
| } |
| return newList.copy(); |
| } |
| |
| public boolean addChangeToList(@NotNull final String name, final Change change, final VcsKey vcsKey) { |
| LOG.debug("[addChangeToList] name: " + name + " change: " + ChangesUtil.getFilePath(change).getPath() + " vcs: " + |
| (vcsKey == null ? null : vcsKey.getName())); |
| final LocalChangeList changeList = myMap.get(name); |
| if (changeList != null) { |
| ((LocalChangeListImpl) changeList).addChange(change); |
| myIdx.changeAdded(change, vcsKey); |
| } |
| return changeList != null; |
| } |
| |
| public void addChangeToCorrespondingList(final Change change, final VcsKey vcsKey) { |
| final String path = ChangesUtil.getFilePath(change).getPath(); |
| LOG.debug("[addChangeToCorrespondingList] for change " + path + " type: " + change.getType() + " have before revision: " + (change.getBeforeRevision() != null)); |
| assert myDefault != null; |
| for (LocalChangeList list : myMap.values()) { |
| if (list.isDefault()) { |
| LOG.debug("[addChangeToCorrespondingList] skip default list: " + list.getName() + " type: " + change.getType() + " have before revision: " + (change.getBeforeRevision() != null)); |
| continue; |
| } |
| if (((LocalChangeListImpl) list).processChange(change)) { |
| LOG.debug("[addChangeToCorrespondingList] matched: " + list.getName() + " type: " + change.getType() + " have before revision: " + (change.getBeforeRevision() != null)); |
| myIdx.changeAdded(change, vcsKey); |
| return; |
| } |
| } |
| ((LocalChangeListImpl) myDefault).processChange(change); |
| myIdx.changeAdded(change, vcsKey); |
| } |
| |
| public boolean removeChangeList(@NotNull String name) { |
| final LocalChangeList list = myMap.get(name); |
| if (list == null) { |
| return false; |
| } |
| if (list.isDefault()) { |
| throw new RuntimeException(new IncorrectOperationException("Cannot remove default changelist")); |
| } |
| final String listName = list.getName(); |
| |
| for (Change change : list.getChanges()) { |
| ((LocalChangeListImpl) myDefault).addChange(change); |
| } |
| |
| final LocalChangeList removed = myMap.remove(listName); |
| return true; |
| } |
| |
| @Nullable |
| public MultiMap<LocalChangeList, Change> moveChangesTo(final String name, final Change[] changes) { |
| final LocalChangeListImpl changeList = (LocalChangeListImpl) myMap.get(name); |
| if (changeList != null) { |
| final MultiMap<LocalChangeList, Change> result = new MultiMap<LocalChangeList, Change>(); |
| for (LocalChangeList list : myMap.values()) { |
| if (list.equals(changeList)) continue; |
| for (Change change : changes) { |
| final Change removedChange = ((LocalChangeListImpl)list).removeChange(change); |
| if (removedChange != null) { |
| changeList.addChange(removedChange); |
| result.putValue(list, removedChange); |
| } |
| } |
| } |
| return result; |
| } |
| return null; |
| } |
| |
| public boolean editName(@NotNull final String fromName, @NotNull final String toName) { |
| if (fromName.equals(toName)) return false; |
| final LocalChangeList list = myMap.get(fromName); |
| final boolean canEdit = list != null && (!list.isReadOnly()); |
| if (canEdit) { |
| final LocalChangeListImpl listImpl = (LocalChangeListImpl) list; |
| listImpl.setNameImpl(toName); |
| myMap.remove(fromName); |
| myMap.put(toName, list); |
| final ChangeListEditHandler editHandler = listImpl.getEditHandler(); |
| if (editHandler != null) { |
| listImpl.setCommentImpl(editHandler.changeCommentOnChangeName(toName, listImpl.getComment())); |
| } |
| } |
| return canEdit; |
| } |
| |
| @Nullable |
| public String editComment(@NotNull final String fromName, final String newComment) { |
| final LocalChangeList list = myMap.get(fromName); |
| if (list != null) { |
| final String oldComment = list.getComment(); |
| if (! Comparing.equal(oldComment, newComment)) { |
| final LocalChangeListImpl listImpl = (LocalChangeListImpl) list; |
| listImpl.setCommentImpl(newComment); |
| final ChangeListEditHandler editHandler = listImpl.getEditHandler(); |
| if (editHandler != null) { |
| listImpl.setNameImpl(editHandler.changeNameOnChangeComment(listImpl.getName(), listImpl.getComment())); |
| if (! fromName.equals(listImpl.getName())) { |
| myMap.remove(fromName); |
| myMap.put(listImpl.getName(), list); |
| } |
| } |
| } |
| return oldComment; |
| } |
| return null; |
| } |
| |
| public boolean isEmpty() { |
| return myMap.isEmpty(); |
| } |
| |
| @Nullable |
| public LocalChangeList getDefaultListCopy() { |
| return myDefault == null ? null : myDefault.copy(); |
| } |
| |
| public boolean isDefaultList(LocalChangeList list) { |
| return myDefault != null && list.getId().equals(myDefault.getId()); |
| } |
| |
| public Project getProject() { |
| return myProject; |
| } |
| |
| // called NOT under ChangeListManagerImpl lock |
| public void notifyStartProcessingChanges(final VcsModifiableDirtyScope scope) { |
| final Collection<Change> oldChanges = new ArrayList<Change>(); |
| for (LocalChangeList list : myMap.values()) { |
| final Collection<Change> affectedChanges = ((LocalChangeListImpl)list).startProcessingChanges(myProject, scope); |
| if (! affectedChanges.isEmpty()) { |
| oldChanges.addAll(affectedChanges); |
| } |
| } |
| for (Change change : oldChanges) { |
| myIdx.changeRemoved(change); |
| } |
| // scope should be modified for correct moves tracking |
| correctScopeForMoves(scope, oldChanges); |
| |
| myLocallyDeleted.cleanAndAdjustScope(scope); |
| mySwitchedHolder.cleanAndAdjustScope(scope); |
| } |
| |
| private void correctScopeForMoves(final VcsModifiableDirtyScope scope, final Collection<Change> changes) { |
| if (scope == null) return; |
| for (Change change : changes) { |
| if (change.isMoved() || change.isRenamed()) { |
| scope.addDirtyFile(change.getBeforeRevision().getFile()); |
| scope.addDirtyFile(change.getAfterRevision().getFile()); |
| } |
| } |
| } |
| |
| public void notifyDoneProcessingChanges(final ChangeListListener dispatcher) { |
| List<ChangeList> changedLists = new ArrayList<ChangeList>(); |
| final Map<LocalChangeListImpl, List<Change>> removedChanges = new HashMap<LocalChangeListImpl, List<Change>>(); |
| final Map<LocalChangeListImpl, List<Change>> addedChanges = new HashMap<LocalChangeListImpl, List<Change>>(); |
| for (LocalChangeList list : myMap.values()) { |
| final List<Change> removed = new ArrayList<Change>(); |
| final List<Change> added = new ArrayList<Change>(); |
| final LocalChangeListImpl listImpl = (LocalChangeListImpl)list; |
| if (listImpl.doneProcessingChanges(removed, added)) { |
| changedLists.add(list); |
| } |
| if (! removed.isEmpty()) { |
| removedChanges.put(listImpl, removed); |
| } |
| if (! added.isEmpty()) { |
| addedChanges.put(listImpl, added); |
| } |
| } |
| for (Map.Entry<LocalChangeListImpl, List<Change>> entry : removedChanges.entrySet()) { |
| dispatcher.changesRemoved(entry.getValue(), entry.getKey()); |
| } |
| for (Map.Entry<LocalChangeListImpl, List<Change>> entry : addedChanges.entrySet()) { |
| dispatcher.changesAdded(entry.getValue(), entry.getKey()); |
| } |
| for(ChangeList changeList: changedLists) { |
| dispatcher.changeListChanged(changeList); |
| } |
| mySwitchedHolder.calculateChildren(); |
| |
| for (String name : myListsToDisappear) { |
| final LocalChangeList changeList = myMap.get(name); |
| if ((changeList != null) && changeList.getChanges().isEmpty() && (! changeList.isReadOnly()) && (! changeList.isDefault())) { |
| removeChangeList(name); |
| } |
| } |
| myListsToDisappear.clear(); |
| } |
| |
| public List<LocalChangeList> getListsCopy() { |
| final List<LocalChangeList> result = new ArrayList<LocalChangeList>(); |
| for (LocalChangeList list : myMap.values()) { |
| result.add(list.copy()); |
| } |
| return result; |
| } |
| |
| public String getDefaultListName() { |
| return myDefault == null ? null : myDefault.getName(); |
| } |
| |
| public List<File> getAffectedPaths() { |
| final SortedSet<String> set = myIdx.getAffectedPaths(); |
| final List<File> result = new ArrayList<File>(set.size()); |
| for (String path : set) { |
| result.add(new File(path)); |
| } |
| return result; |
| } |
| |
| @NotNull |
| public List<VirtualFile> getAffectedFiles() { |
| final Set<VirtualFile> result = ContainerUtil.newLinkedHashSet(); |
| for (LocalChangeList list : myMap.values()) { |
| for (Change change : list.getChanges()) { |
| final ContentRevision before = change.getBeforeRevision(); |
| final ContentRevision after = change.getAfterRevision(); |
| if (before != null) { |
| final VirtualFile file = before.getFile().getVirtualFile(); |
| if (file != null) { |
| result.add(file); |
| } |
| } |
| if (after != null) { |
| final VirtualFile file = after.getFile().getVirtualFile(); |
| if (file != null) { |
| result.add(file); |
| } |
| } |
| } |
| } |
| return new ArrayList<VirtualFile>(result); |
| } |
| |
| public LocalChangeList getListCopy(@NotNull final VirtualFile file) { |
| for (LocalChangeList list : myMap.values()) { |
| for (Change change : list.getChanges()) { |
| if (change.getAfterRevision() != null && |
| Comparing.equal(change.getAfterRevision().getFile().getVirtualFile(), file)) { |
| return list.copy(); |
| } |
| if (change.getBeforeRevision() != null && |
| Comparing.equal(change.getBeforeRevision().getFile().getVirtualFile(), file)) { |
| return list.copy(); |
| } |
| } |
| } |
| return null; |
| } |
| |
| @Nullable |
| public Change getChangeForPath(final FilePath file) { |
| for (LocalChangeList list : myMap.values()) { |
| for (Change change : list.getChanges()) { |
| final ContentRevision afterRevision = change.getAfterRevision(); |
| if (afterRevision != null && afterRevision.getFile().equals(file)) { |
| return change; |
| } |
| final ContentRevision beforeRevision = change.getBeforeRevision(); |
| if (beforeRevision != null && beforeRevision.getFile().equals(file)) { |
| return change; |
| } |
| } |
| } |
| return null; |
| } |
| |
| public FileStatus getStatus(final VirtualFile file) { |
| return myIdx.getStatus(file); |
| } |
| |
| public FileStatus getStatus(final File file) { |
| return myIdx.getStatus(file); |
| } |
| |
| public DeletedFilesHolder getLocallyDeleted() { |
| return myLocallyDeleted.copy(); |
| } |
| |
| public SwitchedFileHolder getSwitchedHolder() { |
| return mySwitchedHolder.copy(); |
| } |
| |
| public void addSwitched(final VirtualFile file, @NotNull String branchName, final boolean recursive) { |
| mySwitchedHolder.addFile(file, branchName, recursive); |
| } |
| |
| public void removeSwitched(final VirtualFile file) { |
| mySwitchedHolder.removeFile(file); |
| } |
| |
| public String getBranchForFile(final VirtualFile file) { |
| return mySwitchedHolder.getBranchForFile(file); |
| } |
| |
| public boolean isSwitched(final VirtualFile file) { |
| return mySwitchedHolder.containsFile(file); |
| } |
| |
| public void addLocallyDeleted(final LocallyDeletedChange change) { |
| myLocallyDeleted.addFile(change); |
| } |
| |
| public boolean isContainedInLocallyDeleted(final FilePath filePath) { |
| return myLocallyDeleted.isContainedInLocallyDeleted(filePath); |
| } |
| |
| public void notifyVcsStarted(AbstractVcs vcs) { |
| myLocallyDeleted.notifyVcsStarted(vcs); |
| mySwitchedHolder.notifyVcsStarted(vcs); |
| } |
| |
| public Collection<Change> getAllChanges() { |
| final Collection<Change> changes = new HashSet<Change>(); |
| for (LocalChangeList list : myMap.values()) { |
| changes.addAll(list.getChanges()); |
| } |
| return changes; |
| } |
| |
| public int getChangeListsNumber() { |
| return myMap.size(); |
| } |
| |
| private abstract class ExternalVsInternalChangesIntersection { |
| protected final Collection<Change> myInChanges; |
| protected final Map<Couple<String>, LocalChangeList> myInternalMap; |
| protected final LocalChangeList myDefaultCopy; |
| protected final Map<String, LocalChangeList> myIncludedListsCopies; |
| |
| protected ExternalVsInternalChangesIntersection(final Collection<Change> inChanges) { |
| myInChanges = inChanges; |
| myInternalMap = new HashMap<Couple<String>, LocalChangeList>(); |
| myDefaultCopy = myDefault.copy(); |
| myIncludedListsCopies = new HashMap<String, LocalChangeList>(); |
| } |
| |
| private Couple<String> keyForChange(final Change change) { |
| final FilePath beforePath = ChangesUtil.getBeforePath(change); |
| final String beforeKey = beforePath == null ? null : beforePath.getIOFile().getAbsolutePath(); |
| final FilePath afterPath = ChangesUtil.getAfterPath(change); |
| final String afterKey = afterPath == null ? null : afterPath.getIOFile().getAbsolutePath(); |
| return Couple.of(beforeKey, afterKey); |
| } |
| |
| private void preparation() { |
| for (LocalChangeList list : myMap.values()) { |
| final Collection<Change> managerChanges = list.getChanges(); |
| final LocalChangeList copy = list.copy(); |
| for (Change change : managerChanges) { |
| myInternalMap.put(keyForChange(change), copy); |
| } |
| } |
| } |
| |
| protected abstract void processInChange(final Couple<String> key, final Change change); |
| |
| public void run() { |
| preparation(); |
| |
| for (Change change : myInChanges) { |
| final Couple<String> key = keyForChange(change); |
| processInChange(key, change); |
| } |
| } |
| |
| public Map<String, LocalChangeList> getIncludedListsCopies() { |
| return myIncludedListsCopies; |
| } |
| } |
| |
| private class GatherChangesVsListsInfo extends ExternalVsInternalChangesIntersection { |
| private final Map<String, List<Change>> myListToChangesMap; |
| |
| private GatherChangesVsListsInfo(final Collection<Change> inChanges) { |
| super(inChanges); |
| myListToChangesMap = new HashMap<String, List<Change>>(); |
| } |
| |
| protected void processInChange(Couple<String> key, Change change) { |
| LocalChangeList tmpList = myInternalMap.get(key); |
| if (tmpList == null) { |
| tmpList = myDefaultCopy; |
| } |
| final String tmpName = tmpList.getName(); |
| List<Change> list = myListToChangesMap.get(tmpName); |
| if (list == null) { |
| list = new ArrayList<Change>(); |
| myListToChangesMap.put(tmpName, list); |
| myIncludedListsCopies.put(tmpName, tmpList); |
| } |
| list.add(change); |
| } |
| |
| public Map<String, List<Change>> getListToChangesMap() { |
| return myListToChangesMap; |
| } |
| } |
| |
| private class GatherListsFilterValidChanges extends ExternalVsInternalChangesIntersection { |
| private final List<Change> myValidChanges; |
| |
| private GatherListsFilterValidChanges(final Collection<Change> inChanges) { |
| super(inChanges); |
| myValidChanges = new ArrayList<Change>(); |
| } |
| |
| protected void processInChange(Couple<String> key, Change change) { |
| final LocalChangeList list = myInternalMap.get(key); |
| if (list != null) { |
| myIncludedListsCopies.put(list.getName(), list); |
| myValidChanges.add(change); |
| } |
| } |
| |
| public List<Change> getValidChanges() { |
| return myValidChanges; |
| } |
| } |
| |
| @NotNull |
| public Map<String, List<Change>> listsForChanges(final Collection<Change> changes, final Map<String, LocalChangeList> lists) { |
| final GatherChangesVsListsInfo info = new GatherChangesVsListsInfo(changes); |
| info.run(); |
| lists.putAll(info.getIncludedListsCopies()); |
| return info.getListToChangesMap(); |
| } |
| |
| @NotNull |
| public Collection<LocalChangeList> getInvolvedListsFilterChanges(final Collection<Change> changes, final List<Change> validChanges) { |
| final GatherListsFilterValidChanges worker = new GatherListsFilterValidChanges(changes); |
| worker.run(); |
| validChanges.addAll(worker.getValidChanges()); |
| return worker.getIncludedListsCopies().values(); |
| } |
| |
| @Nullable |
| public LocalChangeList listForChange(final Change change) { |
| for (LocalChangeList list : myMap.values()) { |
| if (list.getChanges().contains(change)) return list.copy(); |
| } |
| return null; |
| } |
| |
| @Nullable |
| public String listNameIfOnlyOne(final @Nullable Change[] changes) { |
| if (changes == null || changes.length == 0) { |
| return null; |
| } |
| |
| final Change first = changes[0]; |
| |
| for (LocalChangeList list : myMap.values()) { |
| final Collection<Change> listChanges = list.getChanges(); |
| if (listChanges.contains(first)) { |
| // must contain all other |
| for (int i = 1; i < changes.length; i++) { |
| final Change change = changes[i]; |
| if (! listChanges.contains(change)) { |
| return null; |
| } |
| } |
| return list.getName(); |
| } |
| } |
| return null; |
| } |
| |
| @NotNull |
| public ThreeState haveChangesUnder(@NotNull VirtualFile virtualFile) { |
| final String absolutePath = new File(virtualFile.getPath()).getAbsolutePath(); |
| final SortedSet<String> tailSet = myIdx.getAffectedPaths().tailSet(absolutePath); |
| for (String path : tailSet) { |
| return FileUtil.isAncestorThreeState(absolutePath, path, false); |
| } |
| return ThreeState.NO; |
| } |
| |
| @NotNull |
| public Collection<Change> getChangesIn(final FilePath dirPath) { |
| List<Change> changes = new ArrayList<Change>(); |
| for (ChangeList list : myMap.values()) { |
| for (Change change : list.getChanges()) { |
| final ContentRevision afterRevision = change.getAfterRevision(); |
| if (afterRevision != null && afterRevision.getFile().isUnder(dirPath, false)) { |
| changes.add(change); |
| continue; |
| } |
| |
| final ContentRevision beforeRevision = change.getBeforeRevision(); |
| if (beforeRevision != null && beforeRevision.getFile().isUnder(dirPath, false)) { |
| changes.add(change); |
| } |
| } |
| } |
| return changes; |
| } |
| |
| void setListsToDisappear(final Collection<String> names) { |
| myListsToDisappear.addAll(names); |
| } |
| |
| ChangeListManagerGate createSelfGate() { |
| return new MyGate(this); |
| } |
| |
| private static class MyGate implements ChangeListManagerGate { |
| private final ChangeListWorker myWorker; |
| |
| private MyGate(final ChangeListWorker worker) { |
| myWorker = worker; |
| } |
| |
| public List<LocalChangeList> getListsCopy() { |
| return myWorker.getListsCopy(); |
| } |
| |
| @Nullable |
| public LocalChangeList findChangeList(final String name) { |
| return myWorker.getCopyByName(name); |
| } |
| |
| public LocalChangeList addChangeList(final String name, final String comment) { |
| return myWorker.addChangeList(null, name, comment, true, null); |
| } |
| |
| public LocalChangeList findOrCreateList(final String name, final String comment) { |
| LocalChangeList list = myWorker.getCopyByName(name); |
| if (list == null) { |
| list = addChangeList(name, comment); |
| } |
| return list; |
| } |
| |
| public void editComment(final String name, final String comment) { |
| myWorker.editComment(name, comment); |
| } |
| |
| public void editName(String oldName, String newName) { |
| myWorker.editName(oldName, newName); |
| } |
| |
| // todo usage allowed only when.. |
| public void moveChanges(String toList, Collection<Change> changes) { |
| myWorker.moveChangesTo(toList, changes.toArray(new Change[changes.size()])); |
| } |
| |
| public void setListsToDisappear(final Collection<String> names) { |
| myWorker.setListsToDisappear(names); |
| } |
| |
| @Override |
| public FileStatus getStatus(VirtualFile file) { |
| return myWorker.getStatus(file); |
| } |
| |
| @Override |
| public FileStatus getStatus(File file) { |
| return myWorker.getStatus(file); |
| } |
| |
| @Override |
| public void setDefaultChangeList(@NotNull String list) { |
| myWorker.setDefault(list); |
| } |
| } |
| |
| public void removeRegisteredChangeFor(FilePath path) { |
| myIdx.remove(path); |
| |
| for (LocalChangeList list : myMap.values()) { |
| for (Iterator<Change> iterator = list.getChanges().iterator(); iterator.hasNext(); ) { |
| final Change change = iterator.next(); |
| final ContentRevision afterRevision = change.getAfterRevision(); |
| if (afterRevision != null && afterRevision.getFile().equals(path)) { |
| ((LocalChangeListImpl) list).removeChange(change); |
| return; |
| } |
| final ContentRevision beforeRevision = change.getBeforeRevision(); |
| if (beforeRevision != null && beforeRevision.getFile().equals(path)) { |
| ((LocalChangeListImpl) list).removeChange(change); |
| return; |
| } |
| } |
| } |
| } |
| |
| // assumes after revisions are all not null |
| private static class MyChangesAfterRevisionComparator implements Comparator<Pair<Change, String>> { |
| private static final MyChangesAfterRevisionComparator ourInstance = new MyChangesAfterRevisionComparator(); |
| |
| public static MyChangesAfterRevisionComparator getInstance() { |
| return ourInstance; |
| } |
| |
| public int compare(final Pair<Change, String> o1, final Pair<Change, String> o2) { |
| final String s1 = o1.getFirst().getAfterRevision().getFile().getPresentableUrl(); |
| final String s2 = o2.getFirst().getAfterRevision().getFile().getPresentableUrl(); |
| return SystemInfo.isFileSystemCaseSensitive ? s1.compareTo(s2) : s1.compareToIgnoreCase(s2); |
| } |
| } |
| |
| @Override |
| public String toString() { |
| return "ChangeListWorker{" + "myMap=" + StringUtil.join(myMap.values(), new Function<LocalChangeList, String>() { |
| @Override |
| public String fun(LocalChangeList list) { |
| return "list: " + list.getName() + " changes: " + StringUtil.join(list.getChanges(), new Function<Change, String>() { |
| @Override |
| public String fun(Change change) { |
| return change.toString(); |
| } |
| }, ", "); |
| } |
| }, "\n") + '}'; |
| } |
| } |