/* * Copyright 2000-2013 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.compiler.actions; import com.intellij.notification.NotificationGroup; import com.intellij.notification.NotificationType; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonShortcuts; import com.intellij.openapi.actionSystem.Presentation; import com.intellij.openapi.keymap.KeymapUtil; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.roots.ui.configuration.ProjectSettingsService; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.openapi.ui.popup.ListSeparator; import com.intellij.openapi.ui.popup.MultiSelectionListPopupStep; import com.intellij.openapi.ui.popup.PopupStep; import com.intellij.openapi.ui.popup.util.BaseListPopupStep; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.packaging.artifacts.Artifact; import com.intellij.packaging.impl.artifacts.ArtifactUtil; import com.intellij.packaging.impl.compiler.ArtifactsWorkspaceSettings; import com.intellij.task.ProjectTaskManager; import com.intellij.ui.popup.list.ListPopupImpl; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.ui.EmptyIcon; import gnu.trove.TIntArrayList; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.event.ActionEvent; import java.io.File; import java.util.*; /** * @author nik */ public class BuildArtifactAction extends DumbAwareAction { private static final NotificationGroup NOTIFICATION_GROUP = NotificationGroup.balloonGroup("Clean artifact"); public BuildArtifactAction() { super("Build Artifacts...", "Select and build artifacts configured in the project", null); } @Override public void update(AnActionEvent e) { final Project project = getEventProject(e); final Presentation presentation = e.getPresentation(); presentation.setEnabled(project != null && !ArtifactUtil.getArtifactWithOutputPaths(project).isEmpty()); } @Override public void actionPerformed(AnActionEvent e) { final Project project = getEventProject(e); if (project == null) return; final List<Artifact> artifacts = ArtifactUtil.getArtifactWithOutputPaths(project); if (artifacts.isEmpty()) return; List<ArtifactPopupItem> items = new ArrayList<>(); if (artifacts.size() > 1) { items.add(0, new ArtifactPopupItem(null, "All Artifacts", EmptyIcon.ICON_16)); } Set<Artifact> selectedArtifacts = new HashSet<>(ArtifactsWorkspaceSettings.getInstance(project).getArtifactsToBuild()); TIntArrayList selectedIndices = new TIntArrayList(); if (Comparing.haveEqualElements(artifacts, selectedArtifacts) && selectedArtifacts.size() > 1) { selectedIndices.add(0); selectedArtifacts.clear(); } for (Artifact artifact : artifacts) { final ArtifactPopupItem item = new ArtifactPopupItem(artifact, artifact.getName(), artifact.getArtifactType().getIcon()); if (selectedArtifacts.contains(artifact)) { selectedIndices.add(items.size()); } items.add(item); } final ProjectSettingsService projectSettingsService = ProjectSettingsService.getInstance(project); final ArtifactAwareProjectSettingsService settingsService = projectSettingsService instanceof ArtifactAwareProjectSettingsService ? (ArtifactAwareProjectSettingsService)projectSettingsService : null; final ChooseArtifactStep step = new ChooseArtifactStep(items, artifacts.get(0), project, settingsService); step.setDefaultOptionIndices(selectedIndices.toNativeArray()); final ListPopupImpl popup = (ListPopupImpl)JBPopupFactory.getInstance().createListPopup(step); final KeyStroke editKeyStroke = KeymapUtil.getKeyStroke(CommonShortcuts.getEditSource()); if (settingsService != null && editKeyStroke != null) { popup.registerAction("editArtifact", editKeyStroke, new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { Object[] values = popup.getSelectedValues(); popup.cancel(); settingsService.openArtifactSettings(values.length > 0 ? ((ArtifactPopupItem)values[0]).getArtifact() : null); } }); } popup.showCenteredInCurrentWindow(project); } private static void doBuild(@NotNull Project project, final @NotNull List<ArtifactPopupItem> items, boolean rebuild) { final Artifact[] artifacts = getArtifacts(items, project); if (rebuild) { ProjectTaskManager.getInstance(project).rebuild(artifacts); } else { ProjectTaskManager.getInstance(project).build(artifacts); } } private static Artifact[] getArtifacts(final List<ArtifactPopupItem> items, final Project project) { Set<Artifact> artifacts = new LinkedHashSet<>(); for (ArtifactPopupItem item : items) { artifacts.addAll(item.getArtifacts(project)); } return ContainerUtil.toArray(artifacts, new Artifact[artifacts.size()]); } private static class BuildArtifactItem extends ArtifactActionItem { private BuildArtifactItem(List<ArtifactPopupItem> item, Project project) { super(item, project, "Build"); } @Override public void run() { doBuild(myProject, myArtifactPopupItems, false); } } private static class CleanArtifactItem extends ArtifactActionItem { private CleanArtifactItem(@NotNull List<ArtifactPopupItem> item, @NotNull Project project) { super(item, project, "Clean"); } @Override public void run() { Set<VirtualFile> parents = new HashSet<>(); final VirtualFile[] roots = ProjectRootManager.getInstance(myProject).getContentSourceRoots(); for (VirtualFile root : roots) { VirtualFile parent = root; while (parent != null && !parents.contains(parent)) { parents.add(parent); parent = parent.getParent(); } } Map<String, String> outputPathContainingSourceRoots = new HashMap<>(); final List<Pair<File, Artifact>> toClean = new ArrayList<>(); Artifact[] artifacts = getArtifacts(myArtifactPopupItems, myProject); for (Artifact artifact : artifacts) { String outputPath = artifact.getOutputFilePath(); if (outputPath != null) { toClean.add(Pair.create(new File(FileUtil.toSystemDependentName(outputPath)), artifact)); final VirtualFile outputFile = LocalFileSystem.getInstance().findFileByPath(outputPath); if (parents.contains(outputFile)) { outputPathContainingSourceRoots.put(artifact.getName(), outputPath); } } } if (!outputPathContainingSourceRoots.isEmpty()) { final String message; if (outputPathContainingSourceRoots.size() == 1 && outputPathContainingSourceRoots.values().size() == 1) { final String name = ContainerUtil.getFirstItem(outputPathContainingSourceRoots.keySet()); final String output = outputPathContainingSourceRoots.get(name); message = "The output directory '" + output + "' of '" + name + "' artifact contains source roots of the project. Do you want to continue and clear it?"; } else { StringBuilder info = new StringBuilder(); for (String name : outputPathContainingSourceRoots.keySet()) { info.append(" '").append(name).append("' artifact ('").append(outputPathContainingSourceRoots.get(name)).append("')\n"); } message = "The output directories of the following artifacts contains source roots:\n" + info + "Do you want to continue and clear these directories?"; } final int answer = Messages.showYesNoDialog(myProject, message, "Clean Artifacts", null); if (answer != Messages.YES) { return; } } new Task.Backgroundable(myProject, "Cleaning Artifacts", true) { @Override public void run(@NotNull ProgressIndicator indicator) { List<File> deleted = new ArrayList<>(); for (Pair<File, Artifact> pair : toClean) { indicator.checkCanceled(); File file = pair.getFirst(); if (!FileUtil.delete(file)) { NOTIFICATION_GROUP.createNotification("Cannot clean '" + pair.getSecond().getName() + "' artifact", "cannot delete '" + file.getAbsolutePath() + "'", NotificationType.ERROR, null).notify(myProject); } else { deleted.add(file); } } LocalFileSystem.getInstance().refreshIoFiles(deleted, true, true, null); } }.queue(); } } private static class RebuildArtifactItem extends ArtifactActionItem { private RebuildArtifactItem(List<ArtifactPopupItem> item, Project project) { super(item, project, "Rebuild"); } @Override public void run() { doBuild(myProject, myArtifactPopupItems, true); } } private static class EditArtifactItem extends ArtifactActionItem { private final ArtifactAwareProjectSettingsService mySettingsService; private EditArtifactItem(List<ArtifactPopupItem> item, Project project, final ArtifactAwareProjectSettingsService projectSettingsService) { super(item, project, "Edit..."); mySettingsService = projectSettingsService; } @Override public void run() { mySettingsService.openArtifactSettings(myArtifactPopupItems.get(0).getArtifact()); } } private static abstract class ArtifactActionItem implements Runnable { protected final List<ArtifactPopupItem> myArtifactPopupItems; protected final Project myProject; private String myActionName; protected ArtifactActionItem(@NotNull List<ArtifactPopupItem> item, @NotNull Project project, @NotNull String name) { myArtifactPopupItems = item; myProject = project; myActionName = name; } public String getActionName() { return myActionName; } } private static class ArtifactPopupItem { @Nullable private final Artifact myArtifact; private final String myText; private final Icon myIcon; private ArtifactPopupItem(@Nullable Artifact artifact, String text, Icon icon) { myArtifact = artifact; myText = text; myIcon = icon; } @Nullable public Artifact getArtifact() { return myArtifact; } public String getText() { return myText; } public Icon getIcon() { return myIcon; } public List<Artifact> getArtifacts(Project project) { final Artifact artifact = getArtifact(); return artifact != null ? Collections.singletonList(artifact) : ArtifactUtil.getArtifactWithOutputPaths(project); } } private static class ChooseArtifactStep extends MultiSelectionListPopupStep<ArtifactPopupItem> { private final Artifact myFirst; private final Project myProject; private ArtifactAwareProjectSettingsService mySettingsService; public ChooseArtifactStep(List<ArtifactPopupItem> artifacts, Artifact first, Project project, final ArtifactAwareProjectSettingsService settingsService) { super("Build Artifact", artifacts); myFirst = first; myProject = project; mySettingsService = settingsService; } @Override public boolean isSpeedSearchEnabled() { return true; } @Override public Icon getIconFor(ArtifactPopupItem aValue) { return aValue.getIcon(); } @NotNull @Override public String getTextFor(ArtifactPopupItem value) { return value.getText(); } @Override public boolean hasSubstep(List<ArtifactPopupItem> selectedValues) { return true; } @Override public ListSeparator getSeparatorAbove(ArtifactPopupItem value) { return myFirst.equals(value.getArtifact()) ? new ListSeparator() : null; } @Override public PopupStep<?> onChosen(final List<ArtifactPopupItem> selectedValues, boolean finalChoice) { if (finalChoice) { return doFinalStep(() -> doBuild(myProject, selectedValues, false)); } final List<ArtifactActionItem> actions = new ArrayList<>(); actions.add(new BuildArtifactItem(selectedValues, myProject)); actions.add(new RebuildArtifactItem(selectedValues, myProject)); actions.add(new CleanArtifactItem(selectedValues, myProject)); if (mySettingsService != null) { actions.add(new EditArtifactItem(selectedValues, myProject, mySettingsService)); } return new BaseListPopupStep<ArtifactActionItem>(selectedValues.size() == 1 ? "Action" : "Action for " + selectedValues.size() + " artifacts", actions) { @NotNull @Override public String getTextFor(ArtifactActionItem value) { return value.getActionName(); } @Override public PopupStep onChosen(ArtifactActionItem selectedValue, boolean finalChoice) { return doFinalStep(selectedValue); } }; } } }