blob: 2f38609ed93e8ed1be3f5d65b7b761712220c054 [file] [log] [blame]
/*
* 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.openapi.roots.ui.configuration.projectRoot;
import com.intellij.CommonBundle;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.impl.libraries.LibraryEx;
import com.intellij.openapi.roots.impl.libraries.LibraryImpl;
import com.intellij.openapi.roots.libraries.Library;
import com.intellij.openapi.roots.libraries.LibraryTable;
import com.intellij.openapi.roots.libraries.LibraryTablePresentation;
import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar;
import com.intellij.openapi.roots.ui.configuration.libraries.LibraryEditingUtil;
import com.intellij.openapi.roots.ui.configuration.libraryEditor.CreateNewLibraryAction;
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.LibraryProjectStructureElement;
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureDaemonAnalyzer;
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureElement;
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureElementUsage;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.ui.NamedConfigurable;
import com.intellij.openapi.ui.NonEmptyInputValidator;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.containers.MultiMap;
import com.intellij.util.ui.tree.TreeUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import java.util.*;
public abstract class BaseLibrariesConfigurable extends BaseStructureConfigurable {
protected String myLevel;
protected BaseLibrariesConfigurable(final @NotNull Project project) {
super(project);
}
public static BaseLibrariesConfigurable getInstance(@NotNull Project project, @NotNull String tableLevel) {
if (tableLevel.equals(LibraryTablesRegistrar.PROJECT_LEVEL)) {
return ProjectLibrariesConfigurable.getInstance(project);
}
else {
return GlobalLibrariesConfigurable.getInstance(project);
}
}
public abstract LibraryTablePresentation getLibraryTablePresentation();
@Override
protected void processRemovedItems() {
}
@Override
protected boolean wasObjectStored(final Object editableObject) {
return false;
}
@Override
@Nullable
public Runnable enableSearch(final String option) {
return null;
}
@Override
@Nullable
@NonNls
public String getHelpTopic() {
return "reference.settingsdialog.project.structure.library";
}
@Override
public boolean isModified() {
boolean isModified = false;
for (final LibrariesModifiableModel provider : myContext.myLevel2Providers.values()) {
isModified |= provider.isChanged();
}
return isModified || super.isModified();
}
@Override
public void checkCanApply() throws ConfigurationException {
super.checkCanApply();
for (LibraryConfigurable configurable : getLibraryConfigurables()) {
if (configurable.getDisplayName().isEmpty()) {
((LibraryProjectStructureElement)configurable.getProjectStructureElement()).navigate();
throw new ConfigurationException("Library name is not specified");
}
}
}
@Override
public void reset() {
super.reset();
myTree.setRootVisible(false);
}
@Override
protected void loadTree() {
createLibrariesNode(myContext.createModifiableModelProvider(myLevel));
}
@NotNull
@Override
protected Collection<? extends ProjectStructureElement> getProjectStructureElements() {
final List<ProjectStructureElement> result = new ArrayList<ProjectStructureElement>();
for (LibraryConfigurable libraryConfigurable : getLibraryConfigurables()) {
result.add(new LibraryProjectStructureElement(myContext, libraryConfigurable.getEditableObject()));
}
return result;
}
private List<LibraryConfigurable> getLibraryConfigurables() {
//todo[nik] improve
List<LibraryConfigurable> libraryConfigurables = new ArrayList<LibraryConfigurable>();
for (int i = 0; i < myRoot.getChildCount(); i++) {
final TreeNode node = myRoot.getChildAt(i);
if (node instanceof MyNode) {
final NamedConfigurable configurable = ((MyNode)node).getConfigurable();
if (configurable instanceof LibraryConfigurable) {
libraryConfigurables.add((LibraryConfigurable)configurable);
}
}
}
return libraryConfigurables;
}
private void createLibrariesNode(final StructureLibraryTableModifiableModelProvider modelProvider) {
final Library[] libraries = modelProvider.getModifiableModel().getLibraries();
for (Library library : libraries) {
myRoot.add(new MyNode(new LibraryConfigurable(modelProvider, library, myContext, TREE_UPDATER)));
}
TreeUtil.sort(myRoot, new Comparator() {
@Override
public int compare(final Object o1, final Object o2) {
MyNode node1 = (MyNode)o1;
MyNode node2 = (MyNode)o2;
return node1.getDisplayName().compareToIgnoreCase(node2.getDisplayName());
}
});
((DefaultTreeModel)myTree.getModel()).reload(myRoot);
}
@Override
public void apply() throws ConfigurationException {
super.apply();
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
for (final LibrariesModifiableModel provider : myContext.myLevel2Providers.values()) {
provider.deferredCommit();
}
}
});
}
public String getLevel() {
return myLevel;
}
public void createLibraryNode(Library library) {
final LibraryTable table = library.getTable();
if (table != null) {
final String level = table.getTableLevel();
final LibraryConfigurable configurable =
new LibraryConfigurable(myContext.createModifiableModelProvider(level), library, myContext, TREE_UPDATER);
final MyNode node = new MyNode(configurable);
addNode(node, myRoot);
final ProjectStructureDaemonAnalyzer daemonAnalyzer = myContext.getDaemonAnalyzer();
daemonAnalyzer.queueUpdate(new LibraryProjectStructureElement(myContext, library));
daemonAnalyzer.queueUpdateForAllElementsWithErrors();
}
}
@Override
public void dispose() {
if (myContext != null) {
for (final LibrariesModifiableModel provider : myContext.myLevel2Providers.values()) {
provider.disposeUncommittedLibraries();
}
}
}
@Override
@NotNull
protected List<? extends AnAction> createCopyActions(boolean fromPopup) {
final ArrayList<AnAction> actions = new ArrayList<AnAction>();
actions.add(new CopyLibraryAction());
if (fromPopup) {
final BaseLibrariesConfigurable targetGroup = getOppositeGroup();
actions.add(new ChangeLibraryLevelAction(myProject, myTree, this, targetGroup));
actions.add(new AddLibraryToModuleDependenciesAction(myProject, this));
}
return actions;
}
@Override
protected AbstractAddGroup createAddAction() {
return new AbstractAddGroup(getAddText()) {
@Override
@NotNull
public AnAction[] getChildren(@Nullable final AnActionEvent e) {
return CreateNewLibraryAction.createActionOrGroup(getAddText(), BaseLibrariesConfigurable.this, myProject);
}
};
}
protected abstract String getAddText();
public abstract StructureLibraryTableModifiableModelProvider getModelProvider();
public abstract BaseLibrariesConfigurable getOppositeGroup();
@Override
protected void updateSelection(@Nullable NamedConfigurable configurable) {
boolean selectionChanged = !Comparing.equal(myCurrentConfigurable, configurable);
if (myCurrentConfigurable instanceof LibraryConfigurable && selectionChanged) {
((LibraryConfigurable)myCurrentConfigurable).onUnselected();
}
super.updateSelection(configurable);
if (myCurrentConfigurable instanceof LibraryConfigurable && selectionChanged) {
((LibraryConfigurable)myCurrentConfigurable).onSelected();
}
}
@Override
public void onStructureUnselected() {
if (myCurrentConfigurable instanceof LibraryConfigurable) {
((LibraryConfigurable)myCurrentConfigurable).onUnselected();
}
}
@Override
public void onStructureSelected() {
if (myCurrentConfigurable instanceof LibraryConfigurable) {
((LibraryConfigurable)myCurrentConfigurable).onSelected();
}
}
public void removeLibrary(@NotNull LibraryProjectStructureElement element) {
removeLibraries(Collections.singletonList(element));
}
public void removeLibraries(@NotNull List<LibraryProjectStructureElement> libraries) {
List<TreePath> pathsToRemove = new ArrayList<TreePath>();
for (LibraryProjectStructureElement element : libraries) {
getModelProvider().getModifiableModel().removeLibrary(element.getLibrary());
MyNode node = findNodeByObject(myRoot, element.getLibrary());
if (node != null) {
pathsToRemove.add(TreeUtil.getPathFromRoot(node));
}
}
myContext.getDaemonAnalyzer().removeElements(libraries);
removePaths(pathsToRemove.toArray(new TreePath[pathsToRemove.size()]));
}
@Override
protected boolean removeLibrary(final Library library) {
final LibraryTable table = library.getTable();
if (table != null) {
final LibraryProjectStructureElement libraryElement = new LibraryProjectStructureElement(myContext, library);
final Collection<ProjectStructureElementUsage> usages = new ArrayList<ProjectStructureElementUsage>(myContext.getDaemonAnalyzer().getUsages(libraryElement));
if (usages.size() > 0) {
final MultiMap<String, ProjectStructureElementUsage> containerType2Usage = new MultiMap<String, ProjectStructureElementUsage>();
for (final ProjectStructureElementUsage usage : usages) {
containerType2Usage.putValue(usage.getContainingElement().getTypeName(), usage);
}
List<String> types = new ArrayList<String>(containerType2Usage.keySet());
Collections.sort(types);
final StringBuilder sb = new StringBuilder("Library '");
Library libraryModel = myContext.getLibraryModel(library);
sb.append(libraryModel != null ? libraryModel.getName() : library.getName()).append("' is used in ");
for (int i = 0; i < types.size(); i++) {
if (i > 0 && i == types.size() - 1) {
sb.append(" and in ");
}
else if (i > 0) {
sb.append(", in ");
}
String type = types.get(i);
Collection<ProjectStructureElementUsage> usagesOfType = containerType2Usage.get(type);
if (usagesOfType.size() > 1) {
sb.append(usagesOfType.size()).append(" ").append(StringUtil.decapitalize(StringUtil.pluralize(type)));
}
else {
sb.append(StringUtil.decapitalize(usagesOfType.iterator().next().getContainingElement().getPresentableName()));
}
}
sb.append(".\n\nAre you sure you want to delete this library?");
if (Messages.OK == Messages.showOkCancelDialog(myProject, sb.toString(),
"Delete Library", Messages.getQuestionIcon())) {
for (final ProjectStructureElementUsage usage : usages) {
usage.removeSourceElement();
}
getModelProvider().getModifiableModel().removeLibrary(library);
myContext.getDaemonAnalyzer().removeElement(libraryElement);
return true;
}
} else {
getModelProvider().getModifiableModel().removeLibrary(library);
myContext.getDaemonAnalyzer().removeElement(libraryElement);
return true;
}
}
return false;
}
@Override
@Nullable
protected String getEmptySelectionString() {
return "Select a library to view or edit its details here";
}
private class CopyLibraryAction extends AnAction {
private CopyLibraryAction() {
super(CommonBundle.message("button.copy"), CommonBundle.message("button.copy"), COPY_ICON);
}
@Override
public void actionPerformed(final AnActionEvent e) {
final Object o = getSelectedObject();
if (o instanceof LibraryEx) {
final LibraryEx selected = (LibraryEx)o;
final String newName = Messages.showInputDialog("Enter library name:", "Copy Library", null, selected.getName() + "2", new NonEmptyInputValidator());
if (newName == null) return;
BaseLibrariesConfigurable configurable = BaseLibrariesConfigurable.this;
final LibraryEx library = (LibraryEx)myContext.getLibrary(selected.getName(), myLevel);
LOG.assertTrue(library != null);
final LibrariesModifiableModel libsModel = configurable.getModelProvider().getModifiableModel();
final Library lib = libsModel.createLibrary(newName, library.getKind());
final LibraryEx.ModifiableModelEx model = (LibraryEx.ModifiableModelEx)libsModel.getLibraryEditor(lib).getModel();
LibraryEditingUtil.copyLibrary(library, Collections.<String, String>emptyMap(), model);
}
}
@Override
public void update(final AnActionEvent e) {
if (myTree.getSelectionPaths() == null || myTree.getSelectionPaths().length != 1) {
e.getPresentation().setEnabled(false);
} else {
e.getPresentation().setEnabled(getSelectedObject() instanceof LibraryImpl);
}
}
}
}