blob: 96bb835bc086736acb182c65d3101f3b88a6d69f [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.
*/
/*
* Created by IntelliJ IDEA.
* User: yole
* Date: 22.11.2006
* Time: 19:59:36
*/
package com.intellij.openapi.vcs.changes.shelf;
import com.intellij.lifecycle.PeriodicalTasksCloser;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.components.AbstractProjectComponent;
import com.intellij.openapi.components.StorageScheme;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.diff.impl.patch.*;
import com.intellij.openapi.diff.impl.patch.apply.ApplyFilePatchBase;
import com.intellij.openapi.diff.impl.patch.formove.CustomBinaryPatchApplier;
import com.intellij.openapi.diff.impl.patch.formove.PatchApplier;
import com.intellij.openapi.progress.AsynchronousExecution;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ex.ProjectEx;
import com.intellij.openapi.util.*;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.io.FileUtilRt;
import com.intellij.openapi.vcs.*;
import com.intellij.openapi.vcs.changes.*;
import com.intellij.openapi.vcs.changes.patch.ApplyPatchDefaultExecutor;
import com.intellij.openapi.vcs.changes.patch.PatchFileType;
import com.intellij.openapi.vcs.changes.patch.PatchNameChecker;
import com.intellij.openapi.vcs.changes.ui.RollbackChangesDialog;
import com.intellij.openapi.vcs.changes.ui.RollbackWorker;
import com.intellij.openapi.vfs.CharsetToolkit;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.Consumer;
import com.intellij.util.PathUtil;
import com.intellij.util.SmartList;
import com.intellij.util.continuation.*;
import com.intellij.util.messages.MessageBus;
import com.intellij.util.messages.Topic;
import com.intellij.util.text.CharArrayCharSequence;
import com.intellij.util.ui.UIUtil;
import com.intellij.vcsUtil.FilesProgress;
import org.jdom.Element;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.io.*;
import java.util.*;
import static com.intellij.openapi.vcs.changes.shelf.CompoundShelfFileProcessor.SHELF_DIR_NAME;
public class ShelveChangesManager extends AbstractProjectComponent implements JDOMExternalizable {
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.vcs.changes.shelf.ShelveChangesManager");
public static ShelveChangesManager getInstance(Project project) {
return PeriodicalTasksCloser.getInstance().safeGetComponent(project, ShelveChangesManager.class);
}
private final MessageBus myBus;
private final List<ShelvedChangeList> myShelvedChangeLists = new ArrayList<ShelvedChangeList>();
private final List<ShelvedChangeList> myRecycledShelvedChangeLists = new ArrayList<ShelvedChangeList>();
@NonNls private static final String ATTRIBUTE_SHOW_RECYCLED = "show_recycled";
private final CompoundShelfFileProcessor myFileProcessor;
public static final Topic<ChangeListener> SHELF_TOPIC = new Topic<ChangeListener>("shelf updates", ChangeListener.class);
private boolean myShowRecycled;
public ShelveChangesManager(final Project project, final MessageBus bus) {
super(project);
myBus = bus;
if (project.isDefault()) {
myFileProcessor = new CompoundShelfFileProcessor(null, PathManager.getConfigPath() + File.separator + SHELF_DIR_NAME);
}
else {
if (project instanceof ProjectEx && ((ProjectEx)project).getStateStore().getStorageScheme() == StorageScheme.DIRECTORY_BASED) {
String shelfBaseDirPath = project.getBaseDir().getPath() + File.separator + Project.DIRECTORY_STORE_FOLDER;
myFileProcessor = new CompoundShelfFileProcessor(shelfBaseDirPath);
}
else {
myFileProcessor = new CompoundShelfFileProcessor();
}
}
}
@Override
@NonNls
@NotNull
public String getComponentName() {
return "ShelveChangesManager";
}
@Override
public void readExternal(Element element) throws InvalidDataException {
//noinspection unchecked
final String showRecycled = element.getAttributeValue(ATTRIBUTE_SHOW_RECYCLED);
if (showRecycled != null) {
myShowRecycled = Boolean.parseBoolean(showRecycled);
} else {
myShowRecycled = true;
}
readExternal(element, myShelvedChangeLists, myRecycledShelvedChangeLists);
}
public static void readExternal(final Element element, final List<ShelvedChangeList> changes, final List<ShelvedChangeList> recycled) throws InvalidDataException {
changes.addAll(ShelvedChangeList.readChanges(element, false, true));
recycled.addAll(ShelvedChangeList.readChanges(element, true, true));
}
@Override
public void writeExternal(Element element) throws WriteExternalException {
element.setAttribute(ATTRIBUTE_SHOW_RECYCLED, Boolean.toString(myShowRecycled));
ShelvedChangeList.writeChanges(myShelvedChangeLists, myRecycledShelvedChangeLists, element);
}
public List<ShelvedChangeList> getShelvedChangeLists() {
return Collections.unmodifiableList(myShelvedChangeLists);
}
public ShelvedChangeList shelveChanges(final Collection<Change> changes, final String commitMessage, final boolean rollback) throws IOException, VcsException {
final List<Change> textChanges = new ArrayList<Change>();
final List<ShelvedBinaryFile> binaryFiles = new ArrayList<ShelvedBinaryFile>();
for(Change change: changes) {
if (ChangesUtil.getFilePath(change).isDirectory()) {
continue;
}
if (change.getBeforeRevision() instanceof BinaryContentRevision || change.getAfterRevision() instanceof BinaryContentRevision) {
binaryFiles.add(shelveBinaryFile(change));
}
else {
textChanges.add(change);
}
}
final ShelvedChangeList changeList;
try {
File patchPath = getPatchPath(commitMessage);
ProgressManager.checkCanceled();
final List<FilePatch> patches = IdeaTextPatchBuilder.buildPatch(myProject, textChanges, myProject.getBaseDir().getPresentableUrl(), false);
ProgressManager.checkCanceled();
CommitContext commitContext = new CommitContext();
baseRevisionsOfDvcsIntoContext(textChanges, commitContext);
myFileProcessor.savePathFile(
new CompoundShelfFileProcessor.ContentProvider(){
@Override
public void writeContentTo(final Writer writer, CommitContext commitContext) throws IOException {
UnifiedDiffWriter.write(myProject, patches, writer, "\n", commitContext);
}
},
patchPath, commitContext);
changeList = new ShelvedChangeList(patchPath.toString(), commitMessage.replace('\n', ' '), binaryFiles);
myShelvedChangeLists.add(changeList);
ProgressManager.checkCanceled();
if (rollback) {
final String operationName = UIUtil.removeMnemonic(RollbackChangesDialog.operationNameByChanges(myProject, changes));
new RollbackWorker(myProject, operationName).doRollback(changes, true, null, VcsBundle.message("shelve.changes.action"));
}
}
finally {
notifyStateChanged();
}
return changeList;
}
private void baseRevisionsOfDvcsIntoContext(List<Change> textChanges, CommitContext commitContext) {
ProjectLevelVcsManager vcsManager = ProjectLevelVcsManager.getInstance(myProject);
if (vcsManager.dvcsUsedInProject() && VcsConfiguration.getInstance(myProject).INCLUDE_TEXT_INTO_SHELF) {
final Set<Change> big = SelectFilesToAddTextsToPatchPanel.getBig(textChanges);
final ArrayList<FilePath> toKeep = new ArrayList<FilePath>();
for (Change change : textChanges) {
if (change.getBeforeRevision() == null || change.getAfterRevision() == null) continue;
if (big.contains(change)) continue;
FilePath filePath = ChangesUtil.getFilePath(change);
final AbstractVcs vcs = vcsManager.getVcsFor(filePath);
if (vcs != null && VcsType.distributed.equals(vcs.getType())) {
toKeep.add(filePath);
}
}
commitContext.putUserData(BaseRevisionTextPatchEP.ourPutBaseRevisionTextKey, true);
commitContext.putUserData(BaseRevisionTextPatchEP.ourBaseRevisionPaths, toKeep);
}
}
public ShelvedChangeList importFilePatches(final String fileName, final List<FilePatch> patches, final PatchEP[] patchTransitExtensions) throws IOException {
try {
final File patchPath = getPatchPath(fileName);
myFileProcessor.savePathFile(
new CompoundShelfFileProcessor.ContentProvider(){
@Override
public void writeContentTo(final Writer writer, CommitContext commitContext) throws IOException {
UnifiedDiffWriter.write(myProject, patches, writer, "\n", patchTransitExtensions, commitContext);
}
},
patchPath, new CommitContext());
final ShelvedChangeList changeList = new ShelvedChangeList(patchPath.toString(), fileName.replace('\n', ' '), new SmartList<ShelvedBinaryFile>());
myShelvedChangeLists.add(changeList);
return changeList;
} finally {
notifyStateChanged();
}
}
public List<VirtualFile> gatherPatchFiles(final Collection<VirtualFile> files) {
final List<VirtualFile> result = new ArrayList<VirtualFile>();
final LinkedList<VirtualFile> filesQueue = new LinkedList<VirtualFile>(files);
while (! filesQueue.isEmpty()) {
ProgressManager.checkCanceled();
final VirtualFile file = filesQueue.removeFirst();
if (file.isDirectory()) {
filesQueue.addAll(Arrays.asList(file.getChildren()));
continue;
}
if (PatchFileType.NAME.equals(file.getFileType().getName())) {
result.add(file);
}
}
return result;
}
public List<ShelvedChangeList> importChangeLists(final Collection<VirtualFile> files,
final Consumer<VcsException> exceptionConsumer) {
final List<ShelvedChangeList> result = new ArrayList<ShelvedChangeList>(files.size());
try {
final FilesProgress filesProgress = new FilesProgress(files.size(), "Processing ");
for (VirtualFile file : files) {
filesProgress.updateIndicator(file);
final String description = file.getNameWithoutExtension().replace('_', ' ');
final File patchPath = getPatchPath(description);
final ShelvedChangeList list = new ShelvedChangeList(patchPath.getPath(), description, new SmartList<ShelvedBinaryFile>(),
file.getTimeStamp());
try {
final List<TextFilePatch> patchesList = loadPatches(myProject, file.getPath(), new CommitContext());
if (! patchesList.isEmpty()) {
FileUtil.copy(new File(file.getPath()), patchPath);
// add only if ok to read patch
myShelvedChangeLists.add(list);
result.add(list);
}
}
catch (IOException e) {
exceptionConsumer.consume(new VcsException(e));
}
catch (PatchSyntaxException e) {
exceptionConsumer.consume(new VcsException(e));
}
}
} finally {
notifyStateChanged();
}
return result;
}
private ShelvedBinaryFile shelveBinaryFile(final Change change) throws IOException {
final ContentRevision beforeRevision = change.getBeforeRevision();
final ContentRevision afterRevision = change.getAfterRevision();
File beforeFile = beforeRevision == null ? null : beforeRevision.getFile().getIOFile();
File afterFile = afterRevision == null ? null : afterRevision.getFile().getIOFile();
String shelvedPath = null;
if (afterFile != null) {
String shelvedName = FileUtil.getNameWithoutExtension(afterFile.getName());
String shelvedExt = FileUtilRt.getExtension(afterFile.getName());
File shelvedFile = FileUtil.findSequentNonexistentFile(myFileProcessor.getBaseIODir(), shelvedName, shelvedExt);
myFileProcessor.saveFile(afterRevision.getFile().getIOFile(), shelvedFile);
shelvedPath = shelvedFile.getPath();
}
String beforePath = ChangesUtil.getProjectRelativePath(myProject, beforeFile);
String afterPath = ChangesUtil.getProjectRelativePath(myProject, afterFile);
return new ShelvedBinaryFile(beforePath, afterPath, shelvedPath);
}
private void notifyStateChanged() {
if (!myProject.isDisposed()) {
myBus.syncPublisher(SHELF_TOPIC).stateChanged(new ChangeEvent(this));
}
}
private File getPatchPath(@NonNls final String commitMessage) {
File file = myFileProcessor.getBaseIODir();
if (!file.exists()) {
//noinspection ResultOfMethodCallIgnored
file.mkdirs();
}
return suggestPatchName(myProject, commitMessage.length() > PatchNameChecker.MAX ? commitMessage.substring(0, PatchNameChecker.MAX) :
commitMessage, file, VcsConfiguration.PATCH);
}
public static File suggestPatchName(Project project, final String commitMessage, final File file, String extension) {
@NonNls String defaultPath = PathUtil.suggestFileName(commitMessage);
if (defaultPath.isEmpty()) {
defaultPath = "unnamed";
}
if (defaultPath.length() > PatchNameChecker.MAX - 10) {
defaultPath = defaultPath.substring(0, PatchNameChecker.MAX - 10);
}
while (true) {
final File nonexistentFile = FileUtil.findSequentNonexistentFile(file, defaultPath,
extension == null
? VcsConfiguration.getInstance(project).getPatchFileExtension()
: extension);
if (nonexistentFile.getName().length() >= PatchNameChecker.MAX) {
defaultPath = defaultPath.substring(0, defaultPath.length() - 1);
continue;
}
return nonexistentFile;
}
}
public void unshelveChangeList(final ShelvedChangeList changeList, @Nullable final List<ShelvedChange> changes,
@Nullable final List<ShelvedBinaryFile> binaryFiles, final LocalChangeList targetChangeList) {
unshelveChangeList(changeList, changes, binaryFiles, targetChangeList, true);
}
@AsynchronousExecution
public void unshelveChangeList(final ShelvedChangeList changeList,
@Nullable final List<ShelvedChange> changes,
@Nullable final List<ShelvedBinaryFile> binaryFiles,
@Nullable final LocalChangeList targetChangeList,
boolean showSuccessNotification) {
final Continuation continuation = Continuation.createForCurrentProgress(myProject, true, "Unshelve changes");
final GatheringContinuationContext initContext = new GatheringContinuationContext();
scheduleUnshelveChangeList(changeList, changes, binaryFiles, targetChangeList, showSuccessNotification, initContext, false,
false, null, null);
continuation.run(initContext.getList());
}
@AsynchronousExecution
public void scheduleUnshelveChangeList(final ShelvedChangeList changeList,
@Nullable final List<ShelvedChange> changes,
@Nullable final List<ShelvedBinaryFile> binaryFiles,
@Nullable final LocalChangeList targetChangeList,
final boolean showSuccessNotification,
final ContinuationContext context,
final boolean systemOperation,
final boolean reverse,
final String leftConflictTitle,
final String rightConflictTitle) {
context.next(new TaskDescriptor("", Where.AWT) {
@Override
public void run(ContinuationContext contextInner) {
final List<FilePatch> remainingPatches = new ArrayList<FilePatch>();
final CommitContext commitContext = new CommitContext();
final List<TextFilePatch> textFilePatches;
try {
textFilePatches = loadTextPatches(myProject, changeList, changes, remainingPatches, commitContext);
}
catch (IOException e) {
LOG.info(e);
PatchApplier.showError(myProject, "Cannot load patch(es): " + e.getMessage(), true);
return;
}
catch (PatchSyntaxException e) {
PatchApplier.showError(myProject, "Cannot load patch(es): " + e.getMessage(), true);
LOG.info(e);
return;
}
final List<FilePatch> patches = new ArrayList<FilePatch>(textFilePatches);
final List<ShelvedBinaryFile> remainingBinaries = new ArrayList<ShelvedBinaryFile>();
final List<ShelvedBinaryFile> binaryFilesToUnshelve = getBinaryFilesToUnshelve(changeList, binaryFiles, remainingBinaries);
for (final ShelvedBinaryFile shelvedBinaryFile : binaryFilesToUnshelve) {
patches.add(new ShelvedBinaryFilePatch(shelvedBinaryFile));
}
final BinaryPatchApplier binaryPatchApplier = new BinaryPatchApplier();
final PatchApplier<ShelvedBinaryFilePatch> patchApplier = new PatchApplier<ShelvedBinaryFilePatch>(myProject, myProject.getBaseDir(),
patches, targetChangeList, binaryPatchApplier, commitContext, reverse, leftConflictTitle, rightConflictTitle);
patchApplier.setIsSystemOperation(systemOperation);
// after patch applier part
contextInner.next(new TaskDescriptor("", Where.AWT) {
@Override
public void run(ContinuationContext context) {
remainingPatches.addAll(patchApplier.getRemainingPatches());
if (remainingPatches.isEmpty() && remainingBinaries.isEmpty()) {
recycleChangeList(changeList);
}
else {
saveRemainingPatches(changeList, remainingPatches, remainingBinaries, commitContext);
}
}
});
patchApplier.scheduleSelf(showSuccessNotification, contextInner, systemOperation);
}
});
}
private static List<TextFilePatch> loadTextPatches(final Project project, final ShelvedChangeList changeList, final List<ShelvedChange> changes, final List<FilePatch> remainingPatches, final CommitContext commitContext)
throws IOException, PatchSyntaxException {
final List<TextFilePatch> textFilePatches = loadPatches(project, changeList.PATH, commitContext);
if (changes != null) {
final Iterator<TextFilePatch> iterator = textFilePatches.iterator();
while (iterator.hasNext()) {
TextFilePatch patch = iterator.next();
if (!needUnshelve(patch, changes)) {
remainingPatches.add(patch);
iterator.remove();
}
}
}
return textFilePatches;
}
private class BinaryPatchApplier implements CustomBinaryPatchApplier<ShelvedBinaryFilePatch> {
private final List<FilePatch> myAppliedPatches;
private BinaryPatchApplier() {
myAppliedPatches = new ArrayList<FilePatch>();
}
@Override
@NotNull
public ApplyPatchStatus apply(final List<Pair<VirtualFile, ApplyFilePatchBase<ShelvedBinaryFilePatch>>> patches) throws IOException {
for (Pair<VirtualFile, ApplyFilePatchBase<ShelvedBinaryFilePatch>> patch : patches) {
final ShelvedBinaryFilePatch shelvedPatch = patch.getSecond().getPatch();
unshelveBinaryFile(shelvedPatch.getShelvedBinaryFile(), patch.getFirst());
myAppliedPatches.add(shelvedPatch);
}
return ApplyPatchStatus.SUCCESS;
}
@Override
@NotNull
public List<FilePatch> getAppliedPatches() {
return myAppliedPatches;
}
}
private static List<ShelvedBinaryFile> getBinaryFilesToUnshelve(final ShelvedChangeList changeList,
final List<ShelvedBinaryFile> binaryFiles,
final List<ShelvedBinaryFile> remainingBinaries) {
if (binaryFiles == null) {
return new ArrayList<ShelvedBinaryFile>(changeList.getBinaryFiles());
}
ArrayList<ShelvedBinaryFile> result = new ArrayList<ShelvedBinaryFile>();
for(ShelvedBinaryFile file: changeList.getBinaryFiles()) {
if (binaryFiles.contains(file)) {
result.add(file);
} else {
remainingBinaries.add(file);
}
}
return result;
}
@Nullable
private FilePath unshelveBinaryFile(final ShelvedBinaryFile file, @NotNull final VirtualFile patchTarget) throws IOException {
final Ref<FilePath> result = new Ref<FilePath>();
final Ref<IOException> ex = new Ref<IOException>();
final Ref<VirtualFile> patchedFileRef = new Ref<VirtualFile>();
final File shelvedFile = file.SHELVED_PATH == null ? null : new File(file.SHELVED_PATH);
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
try {
result.set(new FilePathImpl(patchTarget));
if (shelvedFile == null) {
patchTarget.delete(this);
}
else {
patchTarget.setBinaryContent(FileUtil.loadFileBytes(shelvedFile));
patchedFileRef.set(patchTarget);
}
}
catch (IOException e) {
ex.set(e);
}
}
});
if (!ex.isNull()) {
throw ex.get();
}
return result.get();
}
private static boolean needUnshelve(final FilePatch patch, final List<ShelvedChange> changes) {
for(ShelvedChange change: changes) {
if (Comparing.equal(patch.getBeforeName(), change.getBeforePath())) {
return true;
}
}
return false;
}
private static void writePatchesToFile(final Project project,
final String path,
final List<FilePatch> remainingPatches,
CommitContext commitContext) {
try {
OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(path), CharsetToolkit.UTF8_CHARSET);
try {
UnifiedDiffWriter.write(project, remainingPatches, writer, "\n", commitContext);
}
finally {
writer.close();
}
}
catch (IOException e) {
LOG.error(e);
}
}
void saveRemainingPatches(final ShelvedChangeList changeList, final List<FilePatch> remainingPatches,
final List<ShelvedBinaryFile> remainingBinaries, CommitContext commitContext) {
final File newPath = getPatchPath(changeList.DESCRIPTION);
try {
FileUtil.copy(new File(changeList.PATH), newPath);
}
catch (IOException e) {
// do not delete if cannot recycle
return;
}
final ShelvedChangeList listCopy = new ShelvedChangeList(newPath.getAbsolutePath(), changeList.DESCRIPTION,
new ArrayList<ShelvedBinaryFile>(changeList.getBinaryFiles()));
listCopy.DATE = changeList.DATE == null ? null : new Date(changeList.DATE.getTime());
writePatchesToFile(myProject, changeList.PATH, remainingPatches, commitContext);
changeList.getBinaryFiles().retainAll(remainingBinaries);
changeList.clearLoadedChanges();
recycleChangeList(listCopy, changeList);
notifyStateChanged();
}
public void restoreList(final ShelvedChangeList changeList) {
myShelvedChangeLists.add(changeList);
myRecycledShelvedChangeLists.remove(changeList);
changeList.setRecycled(false);
notifyStateChanged();
}
public List<ShelvedChangeList> getRecycledShelvedChangeLists() {
return myRecycledShelvedChangeLists;
}
public void clearRecycled() {
for (ShelvedChangeList list : myRecycledShelvedChangeLists) {
deleteListImpl(list);
}
myRecycledShelvedChangeLists.clear();
notifyStateChanged();
}
private void recycleChangeList(final ShelvedChangeList listCopy, final ShelvedChangeList newList) {
if (newList != null) {
for (Iterator<ShelvedBinaryFile> shelvedChangeListIterator = listCopy.getBinaryFiles().iterator();
shelvedChangeListIterator.hasNext();) {
final ShelvedBinaryFile binaryFile = shelvedChangeListIterator.next();
for (ShelvedBinaryFile newBinary : newList.getBinaryFiles()) {
if (Comparing.equal(newBinary.BEFORE_PATH, binaryFile.BEFORE_PATH)
&& Comparing.equal(newBinary.AFTER_PATH, binaryFile.AFTER_PATH)) {
shelvedChangeListIterator.remove();
}
}
}
for (Iterator<ShelvedChange> iterator = listCopy.getChanges(myProject).iterator(); iterator.hasNext();) {
final ShelvedChange change = iterator.next();
for (ShelvedChange newChange : newList.getChanges(myProject)) {
if (Comparing.equal(change.getBeforePath(), newChange.getBeforePath()) &&
Comparing.equal(change.getAfterPath(), newChange.getAfterPath())) {
iterator.remove();
}
}
}
// needed only if partial unshelve
try {
final CommitContext commitContext = new CommitContext();
final List<FilePatch> patches = new ArrayList<FilePatch>();
for (ShelvedChange change : listCopy.getChanges(myProject)) {
patches.add(change.loadFilePatch(myProject, commitContext));
}
writePatchesToFile(myProject, listCopy.PATH, patches, commitContext);
}
catch (IOException e) {
LOG.info(e);
// left file as is
}
catch (PatchSyntaxException e) {
LOG.info(e);
// left file as is
}
}
if (! listCopy.getBinaryFiles().isEmpty() || ! listCopy.getChanges(myProject).isEmpty()) {
listCopy.setRecycled(true);
myRecycledShelvedChangeLists.add(listCopy);
notifyStateChanged();
}
}
private void recycleChangeList(final ShelvedChangeList changeList) {
recycleChangeList(changeList, null);
myShelvedChangeLists.remove(changeList);
notifyStateChanged();
}
public void deleteChangeList(final ShelvedChangeList changeList) {
deleteListImpl(changeList);
if (! changeList.isRecycled()) {
myShelvedChangeLists.remove(changeList);
} else {
myRecycledShelvedChangeLists.remove(changeList);
}
notifyStateChanged();
}
private void deleteListImpl(final ShelvedChangeList changeList) {
File file = new File(changeList.PATH);
myFileProcessor.delete(file.getName());
for(ShelvedBinaryFile binaryFile: changeList.getBinaryFiles()) {
final String path = binaryFile.SHELVED_PATH;
if (path != null) {
File binFile = new File(path);
myFileProcessor.delete(binFile.getName());
}
}
}
public void renameChangeList(final ShelvedChangeList changeList, final String newName) {
changeList.DESCRIPTION = newName;
notifyStateChanged();
}
// todo problem: control usage
public static List<TextFilePatch> loadPatches(Project project, final String patchPath, CommitContext commitContext) throws IOException, PatchSyntaxException {
char[] text = FileUtil.loadFileText(new File(patchPath), CharsetToolkit.UTF8);
PatchReader reader = new PatchReader(new CharArrayCharSequence(text));
final List<TextFilePatch> textFilePatches = reader.readAllPatches();
final TransparentlyFailedValueI<Map<String, Map<String, CharSequence>>, PatchSyntaxException> additionalInfo = reader.getAdditionalInfo(
null);
ApplyPatchDefaultExecutor.applyAdditionalInfoBefore(project, additionalInfo, commitContext);
return textFilePatches;
}
public static class ShelvedBinaryFilePatch extends FilePatch {
private final ShelvedBinaryFile myShelvedBinaryFile;
public ShelvedBinaryFilePatch(final ShelvedBinaryFile shelvedBinaryFile) {
myShelvedBinaryFile = shelvedBinaryFile;
setBeforeName(myShelvedBinaryFile.BEFORE_PATH);
setAfterName(myShelvedBinaryFile.AFTER_PATH);
}
@Override
public String getBeforeFileName() {
String[] pathNameComponents = myShelvedBinaryFile.BEFORE_PATH.replace(File.separatorChar, '/').split("/");
return pathNameComponents [pathNameComponents.length-1];
}
@Override
public String getAfterFileName() {
String[] pathNameComponents = myShelvedBinaryFile.AFTER_PATH.replace(File.separatorChar, '/').split("/");
return pathNameComponents [pathNameComponents.length-1];
}
@Override
public boolean isNewFile() {
return myShelvedBinaryFile.BEFORE_PATH == null;
}
@Override
public boolean isDeletedFile() {
return myShelvedBinaryFile.AFTER_PATH == null;
}
public ShelvedBinaryFile getShelvedBinaryFile() {
return myShelvedBinaryFile;
}
}
public boolean isShowRecycled() {
return myShowRecycled;
}
public void setShowRecycled(final boolean showRecycled) {
myShowRecycled = showRecycled;
notifyStateChanged();
}
}