/* * Copyright 2000-2020 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 jetbrains.teamcity.ui.actions; import jetbrains.teamcity.Activator; import jetbrains.teamcity.Constants.Preferences; import jetbrains.teamcity.core.Util; import jetbrains.teamcity.core.shelve.IShelvedChangeSet; import jetbrains.teamcity.core.shelve.IShelvedResource; import jetbrains.teamcity.core.shelve.ShelveManager; import jetbrains.teamcity.core.util.Resources; import jetbrains.teamcity.ui.UIUtil; import jetbrains.teamcity.ui.dialog.ShelveAddToChangeSetDialog; import jetbrains.teamcity.vcs.ITCChangeSet; import jetbrains.teamcity.vcs.ui.TCSubscriberView; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.*; import org.eclipse.core.runtime.jobs.IJobChangeEvent; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.core.runtime.jobs.JobChangeAdapter; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.swt.widgets.Display; import org.eclipse.team.core.TeamException; import org.eclipse.team.core.synchronize.SyncInfo; import org.eclipse.ui.PlatformUI; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; public class ShelveAddToChangeSetAction extends ShelveAbstractAction { private Collection myFiles; private int myResult; private IShelvedChangeSet myTargetChangeSet; private JobChangeAdapter myCallback; private final boolean isCollectChangedOnly; public ShelveAddToChangeSetAction() { isCollectChangedOnly = Activator.getDefault().getPreferenceStore().getBoolean(Preferences.SHELVE_CHANGED_ONLY); } public ShelveAddToChangeSetAction(@NotNull IShelvedChangeSet target) { this(); myTargetChangeSet = target; } public ShelveAddToChangeSetAction(@NotNull Collection files) { isCollectChangedOnly = false; myFiles = files; } public void setCallback(JobChangeAdapter jobCallback) { myCallback = jobCallback; } public int getResult() { return myResult; } @Override public void run() { // Collect resources final HashSet files = new HashSet(); // Collect resources according to Preferences final Job collector = createCollectingResourcesJob(getSelection(), myFiles, files); collector.addJobChangeListener(new JobChangeAdapter() { @Override public void done(IJobChangeEvent event) { // Show info if no one file collected if (files.isEmpty()) { Display.getDefault().asyncExec(new Runnable() { public void run() { MessageDialog.openInformation(UIUtil.getWorkbenchShell(), "TeamCity Shelve", "There is no changed file(s) for Shelve."); } }); return; } // TODO: save dirty editors final ShelveAddToChangeSetDialog dlg = new ShelveAddToChangeSetDialog(UIUtil.getWorkbenchShell(), "Add resources to ChangeSet:", ShelveManager.getInstance() .getChangeSets(), myTargetChangeSet, new ShelveAddToChangeSetDialog.IDialogValidator() { @NotNull public IStatus validate(@NotNull final IShelvedChangeSet selected) { final HashSet alreadyShelved = new HashSet(); outer: for (final IFile fileForShelve : files) { for (final IShelvedResource shelved : selected.members()) { if (shelved.getOrigin().equals(fileForShelve.getFullPath())) { alreadyShelved.add(fileForShelve.getFullPath()); continue outer; } } } if (!alreadyShelved.isEmpty()) { return Util.createStatus(IStatus.ERROR, String.format("Resources already shelved to \"%s\": %s", selected.getName(), alreadyShelved)); } return Status.OK_STATUS; } }); //show changeset selector and fire the action Display.getDefault().asyncExec(new Runnable() { public void run() { myResult = dlg.open(); if (IDialogConstants.OK_ID == myResult) { final IShelvedChangeSet target; try { // process requested changeset if (ShelveAddToChangeSetDialog.Mode.CREATE_NEW == dlg.getMode()) { target = ShelveManager.getInstance().createChangeSet(dlg.getName(), dlg.getDescription()); } else { target = dlg.getChangeSet(); } // perform shelve final Job shelver = createShelveJob(target, files); if (myCallback != null) { shelver.addJobChangeListener(new JobChangeAdapter() { @Override public void done(final IJobChangeEvent event) { myCallback.done(event); } }); } PlatformUI.getWorkbench().getProgressService().showInDialog(UIUtil.getWorkbenchShell(), shelver); shelver.schedule(); } catch (CoreException e) { throw new RuntimeException(e); } } else { if (myCallback != null) { myCallback.done(null); } } } }); } }); PlatformUI.getWorkbench().getProgressService().showInDialog(null, collector); collector.schedule(); } @NotNull private HashSet getActionScope(@Nullable final IStructuredSelection selection) throws CoreException { final HashSet files = new HashSet(); // try to detect active viewPart and watch the Preference final boolean collectOutgoingChangesOnly = isLocalChangesInvoked() || isWatchChangedOnly(); for (IResource element : getSelectedResources(selection)) { // IFolder && IProject if (element instanceof IFolder || element instanceof IProject) { final Collection collected; final Resources.IResourceFilter fileFilter = new Resources.IResourceFilter() { public boolean accept(@NotNull IResource resource) { if (resource instanceof IFile && !resource.isDerived() && !resource.isTeamPrivateMember()) { if (!collectOutgoingChangesOnly || isChanged((IFile) resource)) { return true; } } return false; } }; collected = Resources.traverse(IResource.DEPTH_INFINITE, 0, fileFilter, Resources.TO_IFILE, element); files.addAll(collected); } else if (element instanceof IFile && !element.isDerived() && !element.isTeamPrivateMember()) { if (!collectOutgoingChangesOnly || isChanged((IFile) element)) { files.add((IFile) element); } } } return files; } private boolean isChanged(@NotNull final IFile file) { try { final SyncInfo syncinfo = Activator.getDefault().getProvider().getSubscriber().getSyncInfo(file); if (syncinfo != null && SyncInfo.IN_SYNC != syncinfo.getKind()) { return true; } } catch (TeamException e) { Activator.getDefault().getLog().log(e.getStatus()); } return false; } @NotNull private Collection getSelectedResources(@Nullable final IStructuredSelection selection) { if (selection == null || selection.isEmpty()) { return Collections.emptyList(); } final HashSet resources = new HashSet(selection.size()); final Iterator iterator = selection.iterator(); while (iterator.hasNext()) { Object selected = iterator.next(); // change sets if (selected instanceof ITCChangeSet) { for (IResource res : ((ITCChangeSet) selected).getChangeSet().getResources()) { // it's possible a folder be here if (resources instanceof IFile) { resources.add(res); } } continue; } // IResource adapter (for org.eclipse.jdt.core.IPackageFragment, etc) final IResource resource; if (selected instanceof IResource) { resource = (IResource) selected; } else { resource = (IResource) Platform.getAdapterManager().getAdapter(selected, IResource.class); } if (resource != null && resource.getType() != IResource.ROOT) { resources.add(resource); } } return resources; } private Job createShelveJob(final IShelvedChangeSet changeset, final HashSet files) { return new Job("Shelving resources") { @Override protected IStatus run(IProgressMonitor monitor) { try { changeset.shelve(files, monitor); } catch (CoreException e) { return e.getStatus(); } finally { monitor.done(); } return Status.OK_STATUS; } }; } private Job createCollectingResourcesJob(@Nullable final IStructuredSelection fromSelection, @Nullable final Collection fromFiles, @NotNull final Collection out) { final String jobName = isCollectChangedOnly ? "Collecting changed resources for Shelve..." : "Collecting resources for Shelve..."; return new Job(jobName) { @Override protected IStatus run(IProgressMonitor monitor) { if (fromFiles != null) { out.addAll(fromFiles); } else { //dig files from the selection try { out.addAll(getActionScope(fromSelection)); } catch (CoreException e) { Activator.getDefault().getLog().log(e.getStatus()); } } return Status.OK_STATUS; } }; } private boolean isLocalChangesInvoked() { if (UIUtil.getActiveWorkbenchWindow() != null && UIUtil.getActiveWorkbenchWindow().getActivePage() != null && UIUtil.getActiveWorkbenchWindow().getActivePage().getActivePart() != null && UIUtil.getActiveWorkbenchWindow().getActivePage().getActivePart().getSite() != null) { return TCSubscriberView.VIEW_ID.equals(UIUtil.getActiveWorkbenchWindow().getActivePage().getActivePart().getSite().getId()); } return false; } private boolean isWatchChangedOnly() { return Activator.getDefault().getPreferenceStore().getBoolean(Preferences.SHELVE_CHANGED_ONLY); } }