project structure dialog: 'Convert to Repository Library' action works for module-level libraries

(IDEA-176748)
This commit is contained in:
nik
2017-07-28 10:10:24 +03:00
parent a06a550a92
commit 7f2a64e167
6 changed files with 122 additions and 28 deletions
@@ -40,6 +40,7 @@ import com.intellij.openapi.roots.ui.configuration.ProjectStructureConfigurable;
import com.intellij.openapi.roots.ui.configuration.dependencyAnalysis.AnalyzeDependenciesDialog;
import com.intellij.openapi.roots.ui.configuration.libraries.LibraryEditingUtil;
import com.intellij.openapi.roots.ui.configuration.libraryEditor.EditExistingLibraryDialog;
import com.intellij.openapi.roots.ui.configuration.projectRoot.ConvertModuleLibraryToRepositoryLibraryAction;
import com.intellij.openapi.roots.ui.configuration.projectRoot.FindUsagesInProjectStructureActionBase;
import com.intellij.openapi.roots.ui.configuration.projectRoot.ModuleStructureConfigurable;
import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
@@ -288,6 +289,7 @@ public class ClasspathPanelImpl extends JPanel implements ClasspathPanel {
addChangeLibraryLevelAction(actionGroup, LibraryTablesRegistrar.PROJECT_LEVEL);
addChangeLibraryLevelAction(actionGroup, LibraryTablesRegistrar.APPLICATION_LEVEL);
addChangeLibraryLevelAction(actionGroup, LibraryTableImplUtil.MODULE_LEVEL);
actionGroup.add(new ConvertModuleLibraryToRepositoryLibraryAction(this, getStructureConfigurableContext()));
PopupHandler.installPopupHandler(myEntryTable, actionGroup, ActionPlaces.UNKNOWN, ActionManager.getInstance());
}
@@ -0,0 +1,51 @@
/*
* Copyright 2000-2017 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.jarRepository.RepositoryLibraryType
import com.intellij.openapi.roots.LibraryOrderEntry
import com.intellij.openapi.roots.impl.OrderEntryUtil
import com.intellij.openapi.roots.impl.libraries.LibraryEx
import com.intellij.openapi.roots.impl.libraries.LibraryTableImplUtil
import com.intellij.openapi.roots.libraries.Library
import com.intellij.openapi.roots.ui.configuration.classpath.ClasspathPanel
import com.intellij.openapi.roots.ui.configuration.libraryEditor.ExistingLibraryEditor
import com.intellij.openapi.roots.ui.configuration.libraryEditor.LibraryEditorBase
/**
* @author nik
*/
class ConvertModuleLibraryToRepositoryLibraryAction(private val classpathPanel: ClasspathPanel,
context: StructureConfigurableContext)
: ConvertToRepositoryLibraryActionBase(context) {
override fun getSelectedLibrary(): LibraryEx? {
val entry = classpathPanel.selectedEntry as? LibraryOrderEntry
if (entry == null || !entry.isModuleLevel) return null
return entry.library as? LibraryEx
}
override fun replaceLibrary(library: Library, configureNewLibrary: (LibraryEditorBase) -> Unit) {
val name = library.name
val modifiableModel = classpathPanel.getModifiableModelProvider(LibraryTableImplUtil.MODULE_LEVEL).modifiableModel
val newLibrary = modifiableModel.createLibrary(name, RepositoryLibraryType.getInstance().kind, null)
OrderEntryUtil.replaceLibraryEntryByAdded(classpathPanel.rootModel, classpathPanel.rootModel.findLibraryOrderEntry(library)!!)
val editor = ExistingLibraryEditor(newLibrary, null)
configureNewLibrary(editor)
editor.commit()
}
}
@@ -0,0 +1,47 @@
/*
* Copyright 2000-2017 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.jarRepository.RepositoryLibraryType
import com.intellij.openapi.roots.impl.libraries.LibraryEx
import com.intellij.openapi.roots.libraries.Library
import com.intellij.openapi.roots.ui.configuration.ProjectStructureConfigurable
import com.intellij.openapi.roots.ui.configuration.libraryEditor.LibraryEditorBase
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.LibraryProjectStructureElement
/**
* @author nik
*/
class ConvertProjectLibraryToRepositoryLibraryAction(private val librariesConfigurable: BaseLibrariesConfigurable,
context: StructureConfigurableContext)
: ConvertToRepositoryLibraryActionBase(context) {
override fun getSelectedLibrary() = (librariesConfigurable.selectedElement as? LibraryProjectStructureElement)?.library as? LibraryEx
override fun replaceLibrary(library: Library, configureNewLibrary: (LibraryEditorBase) -> Unit) {
val name = library.name
val modifiableModel = librariesConfigurable.modelProvider.modifiableModel
val usages = context.daemonAnalyzer.getUsages(LibraryProjectStructureElement(context, library))
modifiableModel.removeLibrary(library)
val newLibrary = modifiableModel.createLibrary(name, RepositoryLibraryType.getInstance().kind, null)
usages.forEach { it.replaceElement(LibraryProjectStructureElement(context, newLibrary)) }
val editor = modifiableModel.getLibraryEditor(newLibrary)
configureNewLibrary(editor)
ProjectStructureConfigurable.getInstance(project).selectProjectOrGlobalLibrary(newLibrary, true)
}
}
@@ -32,10 +32,12 @@ import com.intellij.openapi.roots.AnnotationOrderRootType
import com.intellij.openapi.roots.OrderRootType
import com.intellij.openapi.roots.impl.libraries.LibraryEx
import com.intellij.openapi.roots.libraries.Library
import com.intellij.openapi.roots.libraries.LibraryUtil
import com.intellij.openapi.roots.libraries.NewLibraryConfiguration
import com.intellij.openapi.roots.libraries.ui.OrderRoot
import com.intellij.openapi.roots.ui.configuration.ProjectStructureConfigurable
import com.intellij.openapi.roots.ui.configuration.libraryEditor.LibraryEditor
import com.intellij.openapi.roots.ui.configuration.libraryEditor.LibraryEditorBase
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.LibraryProjectStructureElement
import com.intellij.openapi.ui.Messages
import com.intellij.openapi.util.io.FileUtil
@@ -55,20 +57,23 @@ import java.util.*
/**
* @author nik
*/
private val LOG = logger<ConvertToRepositoryLibraryAction>()
private val LOG = logger<ConvertToRepositoryLibraryActionBase>()
class ConvertToRepositoryLibraryAction(private val librariesConfigurable: BaseLibrariesConfigurable, private val project: Project) : DumbAwareAction(
abstract class ConvertToRepositoryLibraryActionBase(protected val context: StructureConfigurableContext) : DumbAwareAction(
"Convert to Repository Library...",
"Convert a regular library to a repository library which additionally stores its Maven coordinates, so the IDE can automatically download the library JARs if they are missing",
null) {
protected val project = context.project
protected abstract fun getSelectedLibrary(): LibraryEx?
override fun update(e: AnActionEvent) {
val library = (librariesConfigurable.selectedElement as? LibraryProjectStructureElement)?.library as? LibraryEx
val library = getSelectedLibrary()
e.presentation.isEnabledAndVisible = library != null && library.kind == null
}
override fun actionPerformed(e: AnActionEvent) {
val library = (librariesConfigurable.selectedElement as LibraryProjectStructureElement).library
val library = getSelectedLibrary() ?: return
val mavenCoordinates = detectOrSpecifyMavenCoordinates(library) ?: return
val libraryProperties = RepositoryLibraryProperties(mavenCoordinates.groupId, mavenCoordinates.artifactId, mavenCoordinates.version)
@@ -85,14 +90,14 @@ class ConvertToRepositoryLibraryAction(private val librariesConfigurable: BaseLi
if (task.cancelled) return
if (!task.filesAreTheSame) {
val ok = LibraryJarsDiffDialog(task.libraryFileToCompare!!, task.downloadedFileToCompare!!, mavenCoordinates, library.name!!, project).showAndGet()
val ok = LibraryJarsDiffDialog(task.libraryFileToCompare!!, task.downloadedFileToCompare!!, mavenCoordinates, LibraryUtil.getPresentableName(library), project).showAndGet()
task.deleteTemporaryFiles()
if (!ok) {
return
}
}
ApplicationManager.getApplication().invokeLater {
replaceByLibrary(library, object : NewLibraryConfiguration(library.name!!, RepositoryLibraryType.getInstance(), libraryProperties) {
replaceByLibrary(library, object : NewLibraryConfiguration(library.name ?: "", RepositoryLibraryType.getInstance(), libraryProperties) {
override fun addRoots(editor: LibraryEditor) {
editor.addRoots(roots)
}
@@ -102,7 +107,7 @@ class ConvertToRepositoryLibraryAction(private val librariesConfigurable: BaseLi
private fun detectOrSpecifyMavenCoordinates(library: Library): JpsMavenRepositoryLibraryDescriptor? {
val detectedCoordinates = detectMavenCoordinates(library.getFiles(OrderRootType.CLASSES))
LOG.debug("Maven coordinates for ${library.name} JARs: $detectedCoordinates")
LOG.debug("Maven coordinates for ${LibraryUtil.getPresentableName(library)} JARs: $detectedCoordinates")
if (detectedCoordinates.size == 1) {
return detectedCoordinates[0]
}
@@ -120,23 +125,16 @@ class ConvertToRepositoryLibraryAction(private val librariesConfigurable: BaseLi
private fun replaceByLibrary(library: Library, configuration: NewLibraryConfiguration) {
val annotationUrls = library.getUrls(AnnotationOrderRootType.getInstance())
val name = library.name
val modifiableModel = librariesConfigurable.modelProvider.modifiableModel
val context = librariesConfigurable.myContext
val usages = context.daemonAnalyzer.getUsages(LibraryProjectStructureElement(context, library))
modifiableModel.removeLibrary(library)
val newLibrary = modifiableModel.createLibrary(name, RepositoryLibraryType.getInstance().kind, null)
usages.forEach { it.replaceElement(LibraryProjectStructureElement(context, newLibrary)) }
val editor = modifiableModel.getLibraryEditor(newLibrary)
editor.properties = configuration.properties
editor.removeAllRoots()
configuration.addRoots(editor)
annotationUrls.forEach { editor.addRoot(it, AnnotationOrderRootType.getInstance()) }
ProjectStructureConfigurable.getInstance(project).selectProjectOrGlobalLibrary(newLibrary, true)
replaceLibrary(library) { editor ->
editor.properties = configuration.properties
editor.removeAllRoots()
configuration.addRoots(editor)
annotationUrls.forEach { editor.addRoot(it, AnnotationOrderRootType.getInstance()) }
}
}
protected abstract fun replaceLibrary(library: Library, configureNewLibrary: (LibraryEditorBase) -> Unit)
companion object {
fun detectMavenCoordinates(libraryRoots: Array<out VirtualFile>): List<JpsMavenRepositoryLibraryDescriptor> =
libraryRoots.flatMap { root ->
@@ -77,7 +77,7 @@ public class ProjectLibrariesConfigurable extends BaseLibrariesConfigurable {
protected List<? extends AnAction> createCopyActions(boolean fromPopup) {
List<? extends AnAction> actions = super.createCopyActions(fromPopup);
if (fromPopup) {
return ContainerUtil.concat(actions, Collections.singletonList(new ConvertToRepositoryLibraryAction(this, myProject)));
return ContainerUtil.concat(actions, Collections.singletonList(new ConvertProjectLibraryToRepositoryLibraryAction(this, myContext)));
}
return actions;
}
@@ -16,13 +16,9 @@
package com.intellij.openapi.roots.ui.configuration.projectRoot
import com.intellij.openapi.application.PathManager
import com.intellij.openapi.projectRoots.ex.JavaSdkUtil
import com.intellij.openapi.vfs.LocalFileSystem
import com.intellij.openapi.vfs.VfsUtil
import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.testFramework.LightPlatformTestCase
import junit.framework.TestCase
import java.io.File
/**
* @author nik
@@ -32,7 +28,7 @@ class ConvertToRepositoryLibraryActionTest : LightPlatformTestCase() {
val jarFile = PathManager.findFileInLibDirectory("commons-codec-1.9.jar")
val file = VirtualFileManager.getInstance().refreshAndFindFileByUrl(VfsUtil.getUrlForLibraryRoot(jarFile))
assertNotNull(jarFile.absolutePath, file)
val coordinates = assertOneElement(ConvertToRepositoryLibraryAction.detectMavenCoordinates(arrayOf(file!!)))
val coordinates = assertOneElement(ConvertToRepositoryLibraryActionBase.detectMavenCoordinates(arrayOf(file!!)))
assertEquals("commons-codec", coordinates.groupId)
assertEquals("commons-codec", coordinates.artifactId)
assertEquals("1.9", coordinates.version)