mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
test "import settings"
This commit is contained in:
@@ -16,24 +16,27 @@
|
||||
package com.intellij.configurationStore
|
||||
|
||||
import com.intellij.ide.actions.ExportableItem
|
||||
import com.intellij.ide.actions.exportSettings
|
||||
import com.intellij.ide.actions.getExportableComponentsMap
|
||||
import com.intellij.ide.actions.getPaths
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.components.*
|
||||
import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream
|
||||
import com.intellij.openapi.vfs.CharsetToolkit
|
||||
import com.intellij.testFramework.*
|
||||
import com.intellij.util.SmartList
|
||||
import com.intellij.util.xmlb.XmlSerializer
|
||||
import com.intellij.util.xmlb.XmlSerializerUtil
|
||||
import com.intellij.util.xmlb.annotations.Attribute
|
||||
import com.intellij.util.xmlb.serialize
|
||||
import gnu.trove.THashMap
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.assertj.core.data.MapEntry
|
||||
import org.intellij.lang.annotations.Language
|
||||
import org.jdom.Element
|
||||
import org.junit.Before
|
||||
import org.junit.ClassRule
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.picocontainer.MutablePicoContainer
|
||||
import org.picocontainer.defaults.InstanceComponentAdapter
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.File
|
||||
import java.io.InputStream
|
||||
@@ -113,14 +116,65 @@ internal class ApplicationStoreTest {
|
||||
}
|
||||
|
||||
@Test fun `export settings`() {
|
||||
testAppConfig.refreshVfs()
|
||||
|
||||
val storageManager = ApplicationManager.getApplication().stateStore.stateStorageManager
|
||||
val optionsPath = storageManager.expandMacros(StoragePathMacros.APP_CONFIG)
|
||||
val rootConfigPath = storageManager.expandMacros(ROOT_CONFIG)
|
||||
val map = getExportableComponentsMap(false, true, storageManager)
|
||||
assertThat(map.size()).isNotEqualTo(0)
|
||||
assertThat(map).isNotEmpty()
|
||||
|
||||
val key = File(optionsPath, "filetypes.xml")
|
||||
assertThat(map.get(key)).containsExactly(ExportableItem(listOf(key, File(rootConfigPath, "filetypes")), "File types", RoamingType.DEFAULT))
|
||||
fun test(item: ExportableItem) {
|
||||
val file = item.files.first()
|
||||
assertThat(map.get(file)).containsExactly(item)
|
||||
assertThat(file).doesNotExist()
|
||||
}
|
||||
|
||||
test(ExportableItem(listOf(File(optionsPath, "filetypes.xml"), File(rootConfigPath, "filetypes")), "File types", RoamingType.DEFAULT))
|
||||
test(ExportableItem(listOf(File(optionsPath, "customization.xml")), "Menus and toolbars customization", RoamingType.DEFAULT))
|
||||
}
|
||||
|
||||
@Test fun `import settings`() {
|
||||
testAppConfig.refreshVfs()
|
||||
|
||||
val component = A()
|
||||
componentStore.initComponent(component, false)
|
||||
|
||||
component.options.foo = "new"
|
||||
|
||||
saveStore()
|
||||
|
||||
val storageManager = componentStore.storageManager
|
||||
|
||||
val configPath = storageManager.expandMacros(ROOT_CONFIG)
|
||||
val configDir = Paths.get(configPath)
|
||||
|
||||
val componentPath = configDir.resolve("a.xml")
|
||||
assertThat(componentPath).isRegularFile()
|
||||
val componentFile = componentPath.toFile()
|
||||
|
||||
// additional export path
|
||||
val additionalPath = configDir.resolve("foo")
|
||||
additionalPath.writeChild("bar.icls", "")
|
||||
val additionalFile = additionalPath.toFile()
|
||||
val exportedData = BufferExposingByteArrayOutputStream()
|
||||
exportSettings(setOf(componentFile, additionalFile), exportedData, configPath)
|
||||
|
||||
val relativePaths = getPaths(ByteArrayInputStream(exportedData.internalBuffer, 0, exportedData.size()))
|
||||
assertThat(relativePaths).containsOnly("a.xml", "foo/", "foo/bar.icls", "IntelliJ IDEA Global Settings")
|
||||
val list = listOf(ExportableItem(listOf(componentFile, additionalFile), ""))
|
||||
|
||||
fun <B> File.to(that: B) = MapEntry.entry(this, that)
|
||||
|
||||
val picoContainer = ApplicationManager.getApplication().picoContainer as MutablePicoContainer
|
||||
val componentKey = A::class.java.name
|
||||
picoContainer.registerComponent(InstanceComponentAdapter(componentKey, component))
|
||||
try {
|
||||
assertThat(getExportableComponentsMap(false, false, storageManager, relativePaths)).containsOnly(componentFile to list, additionalFile to list)
|
||||
}
|
||||
finally {
|
||||
picoContainer.unregisterComponent(componentKey)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createComponentData(foo: String) = """<component name="A" foo="$foo" />"""
|
||||
@@ -146,16 +200,16 @@ internal class ApplicationStoreTest {
|
||||
</application>""")
|
||||
}
|
||||
|
||||
@State(name = "A", storages = arrayOf(Storage(file = "a.xml")))
|
||||
private open class A : PersistentStateComponent<Element> {
|
||||
@State(name = "A", storages = arrayOf(Storage(file = "a.xml")), additionalExportFile = "foo")
|
||||
private open class A : PersistentStateComponent<A.State> {
|
||||
data class State(@Attribute var foo: String = "", @Attribute var bar: String = "")
|
||||
|
||||
var state = State()
|
||||
var options = State()
|
||||
|
||||
override fun getState() = state.serialize()
|
||||
override fun getState() = options
|
||||
|
||||
override fun loadState(state: Element) {
|
||||
this.state = XmlSerializer.deserialize(state, State::class.java)!!
|
||||
override fun loadState(state: State) {
|
||||
this.options = state
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,15 +221,15 @@ internal class ApplicationStoreTest {
|
||||
|
||||
val component = A()
|
||||
componentStore.initComponent(component, false)
|
||||
assertThat(component.state).isEqualTo(A.State("old"))
|
||||
assertThat(component.options).isEqualTo(A.State("old"))
|
||||
|
||||
saveStore()
|
||||
|
||||
assertThat(file).hasContent(oldContent)
|
||||
assertThat(oldModificationTime).isEqualTo(file.getLastModifiedTime())
|
||||
|
||||
component.state.bar = "2"
|
||||
component.state.foo = "1"
|
||||
component.options.bar = "2"
|
||||
component.options.foo = "1"
|
||||
saveStore()
|
||||
|
||||
assertThat(file).hasContent("<application>\n <component name=\"A\" foo=\"1\" bar=\"2\" />\n</application>")
|
||||
@@ -191,7 +245,7 @@ internal class ApplicationStoreTest {
|
||||
|
||||
val component = AWorkspace()
|
||||
componentStore.initComponent(component, false)
|
||||
assertThat(component.state).isEqualTo(A.State("old"))
|
||||
assertThat(component.options).isEqualTo(A.State("old"))
|
||||
|
||||
saveStore()
|
||||
|
||||
@@ -242,6 +296,8 @@ internal class ApplicationStoreTest {
|
||||
|
||||
override fun setPath(path: String) {
|
||||
storageManager.addMacro(StoragePathMacros.APP_CONFIG, path)
|
||||
// yes, in tests APP_CONFIG equals to ROOT_CONFIG (as ICS does)
|
||||
storageManager.addMacro(ROOT_CONFIG, path)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-3
@@ -365,9 +365,8 @@ public abstract class ComponentManagerImpl extends UserDataHolderBase implements
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public final PluginId getConfig(@NotNull Class<?> componentImplementation) {
|
||||
ComponentConfigComponentAdapter adapter = getComponentAdapter(componentImplementation);
|
||||
return adapter == null ? null : adapter.myPluginId;
|
||||
public final PluginId getConfig(@NotNull ComponentAdapter adapter) {
|
||||
return adapter instanceof ComponentConfigComponentAdapter ? ((ComponentConfigComponentAdapter)adapter).myPluginId : null;
|
||||
}
|
||||
|
||||
public final boolean isWorkspaceComponent(@NotNull Class<?> componentImplementation) {
|
||||
|
||||
+24
-19
@@ -33,7 +33,7 @@ import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.ui.FieldPanel;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.Consumer;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import gnu.trove.THashSet;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -57,19 +57,21 @@ public class ChooseComponentsToExportDialog extends DialogWrapper {
|
||||
private final boolean myShowFilePath;
|
||||
private final String myDescription;
|
||||
|
||||
public ChooseComponentsToExportDialog(@NotNull MultiMap<File, ExportableItem> fileToComponents,
|
||||
public ChooseComponentsToExportDialog(@NotNull Map<File, List<ExportableItem>> fileToComponents,
|
||||
boolean showFilePath, final String title, String description) {
|
||||
super(false);
|
||||
|
||||
myDescription = description;
|
||||
myShowFilePath = showFilePath;
|
||||
Map<ExportableItem, ComponentElementProperties> componentToContainingListElement = new LinkedHashMap<ExportableItem, ComponentElementProperties>();
|
||||
for (ExportableItem component : fileToComponents.values()) {
|
||||
if (!addToExistingListElement(component, componentToContainingListElement, fileToComponents)) {
|
||||
ComponentElementProperties componentElementProperties = new ComponentElementProperties();
|
||||
componentElementProperties.addComponent(component);
|
||||
for (List<ExportableItem> list : fileToComponents.values()) {
|
||||
for (ExportableItem component : list) {
|
||||
if (!addToExistingListElement(component, componentToContainingListElement, fileToComponents)) {
|
||||
ComponentElementProperties componentElementProperties = new ComponentElementProperties();
|
||||
componentElementProperties.addComponent(component);
|
||||
|
||||
componentToContainingListElement.put(component, componentElementProperties);
|
||||
componentToContainingListElement.put(component, componentElementProperties);
|
||||
}
|
||||
}
|
||||
}
|
||||
myChooser = new ElementsChooser<ComponentElementProperties>(true);
|
||||
@@ -149,21 +151,24 @@ public class ChooseComponentsToExportDialog extends DialogWrapper {
|
||||
|
||||
private static boolean addToExistingListElement(@NotNull ExportableItem component,
|
||||
@NotNull Map<ExportableItem, ComponentElementProperties> componentToContainingListElement,
|
||||
@NotNull MultiMap<File, ExportableItem> fileToComponents) {
|
||||
@NotNull Map<File, List<ExportableItem>> fileToComponents) {
|
||||
File file = null;
|
||||
for (File exportFile : component.getFiles()) {
|
||||
for (ExportableItem tiedComponent : fileToComponents.get(exportFile)) {
|
||||
if (tiedComponent == component) {
|
||||
continue;
|
||||
}
|
||||
List<ExportableItem> list = fileToComponents.get(exportFile);
|
||||
if (!ContainerUtil.isEmpty(list)) {
|
||||
for (ExportableItem tiedComponent : list) {
|
||||
if (tiedComponent == component) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final ComponentElementProperties elementProperties = componentToContainingListElement.get(tiedComponent);
|
||||
if (elementProperties != null && !FileUtil.filesEqual(exportFile, file)) {
|
||||
LOG.assertTrue(file == null, "Component " + component + " serialize itself into " + file + " and " + exportFile);
|
||||
// found
|
||||
elementProperties.addComponent(component);
|
||||
componentToContainingListElement.put(component, elementProperties);
|
||||
file = exportFile;
|
||||
final ComponentElementProperties elementProperties = componentToContainingListElement.get(tiedComponent);
|
||||
if (elementProperties != null && !FileUtil.filesEqual(exportFile, file)) {
|
||||
LOG.assertTrue(file == null, "Component " + component + " serialize itself into " + file + " and " + exportFile);
|
||||
// found
|
||||
elementProperties.addComponent(component);
|
||||
componentToContainingListElement.put(component, elementProperties);
|
||||
file = exportFile;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,15 +37,19 @@ import com.intellij.openapi.project.DumbAware
|
||||
import com.intellij.openapi.ui.Messages
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.openapi.util.io.FileUtilRt
|
||||
import com.intellij.openapi.util.io.systemIndependentPath
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.intellij.openapi.vfs.CharsetToolkit
|
||||
import com.intellij.util.PairProcessor
|
||||
import com.intellij.util.PlatformUtils
|
||||
import com.intellij.util.ReflectionUtil
|
||||
import com.intellij.util.containers.MultiMap
|
||||
import com.intellij.util.SmartList
|
||||
import com.intellij.util.io.ZipUtil
|
||||
import gnu.trove.THashSet
|
||||
import java.io.*
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.io.OutputStream
|
||||
import java.io.OutputStreamWriter
|
||||
import java.util.*
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipOutputStream
|
||||
@@ -80,26 +84,7 @@ private class ExportSettingsAction : AnAction(), DumbAware {
|
||||
return
|
||||
}
|
||||
|
||||
val zipOut = MyZipOutputStream(BufferedOutputStream(FileOutputStream(saveFile)))
|
||||
try {
|
||||
val writtenItemRelativePaths = THashSet<String>()
|
||||
val configRoot = FileUtilRt.toSystemIndependentName(PathManager.getConfigPath())
|
||||
for (file in exportFiles) {
|
||||
if (file.exists()) {
|
||||
val relativePath = FileUtilRt.getRelativePath(configRoot, FileUtilRt.toSystemIndependentName(file.absolutePath), '/')!!
|
||||
ZipUtil.addFileOrDirRecursively(zipOut, null, file, relativePath, null, writtenItemRelativePaths)
|
||||
}
|
||||
}
|
||||
|
||||
exportInstalledPlugins(zipOut)
|
||||
|
||||
val zipEntry = ZipEntry(ImportSettingsFilenameFilter.SETTINGS_JAR_MARKER)
|
||||
zipOut.putNextEntry(zipEntry)
|
||||
zipOut.closeEntry()
|
||||
}
|
||||
finally {
|
||||
zipOut.doClose()
|
||||
}
|
||||
exportSettings(exportFiles, saveFile.outputStream().buffered(), FileUtilRt.toSystemIndependentName(PathManager.getConfigPath()))
|
||||
ShowFilePathAction.showDialog(AnAction.getEventProject(e), IdeBundle.message("message.settings.exported.successfully"),
|
||||
IdeBundle.message("title.export.successful"), saveFile, null)
|
||||
}
|
||||
@@ -109,6 +94,29 @@ private class ExportSettingsAction : AnAction(), DumbAware {
|
||||
}
|
||||
}
|
||||
|
||||
// not internal only to test
|
||||
fun exportSettings(exportFiles: Set<File>, out: OutputStream, configPath: String) {
|
||||
val zipOut = MyZipOutputStream(out)
|
||||
try {
|
||||
val writtenItemRelativePaths = THashSet<String>()
|
||||
for (file in exportFiles) {
|
||||
if (file.exists()) {
|
||||
val relativePath = FileUtilRt.getRelativePath(configPath, FileUtilRt.toSystemIndependentName(file.absolutePath), '/')!!
|
||||
ZipUtil.addFileOrDirRecursively(zipOut, null, file, relativePath, null, writtenItemRelativePaths)
|
||||
}
|
||||
}
|
||||
|
||||
exportInstalledPlugins(zipOut)
|
||||
|
||||
val zipEntry = ZipEntry(ImportSettingsFilenameFilter.SETTINGS_JAR_MARKER)
|
||||
zipOut.putNextEntry(zipEntry)
|
||||
zipOut.closeEntry()
|
||||
}
|
||||
finally {
|
||||
zipOut.doClose()
|
||||
}
|
||||
}
|
||||
|
||||
private class MyZipOutputStream(out: OutputStream) : ZipOutputStream(out) {
|
||||
override fun close() {
|
||||
}
|
||||
@@ -118,7 +126,7 @@ private class MyZipOutputStream(out: OutputStream) : ZipOutputStream(out) {
|
||||
}
|
||||
}
|
||||
|
||||
data class ExportableItem(val files: List<File>, val presentableName: String, val roamingType: RoamingType)
|
||||
data class ExportableItem(val files: List<File>, val presentableName: String, val roamingType: RoamingType = RoamingType.DEFAULT)
|
||||
|
||||
private val LOG = Logger.getInstance(ExportSettingsAction::class.java)
|
||||
|
||||
@@ -143,8 +151,23 @@ private fun exportInstalledPlugins(zipOut: MyZipOutputStream) {
|
||||
}
|
||||
}
|
||||
|
||||
fun getExportableComponentsMap(onlyExisting: Boolean, computePresentableNames: Boolean, storageManager: StateStorageManager = ApplicationManager.getApplication().stateStore.stateStorageManager): MultiMap<File, ExportableItem> {
|
||||
val result = MultiMap.createLinkedSet<File, ExportableItem>()
|
||||
fun <K, V> MutableMap<K, MutableList<V>>.putValue(key: K, value: V) {
|
||||
var list = get(key)
|
||||
if (list == null) {
|
||||
list = SmartList<V>(value)
|
||||
put(key, list)
|
||||
}
|
||||
else {
|
||||
list.add(value)
|
||||
}
|
||||
}
|
||||
|
||||
// onlyPaths - include only specified paths (relative to config dir, ends with "/" if directory)
|
||||
fun getExportableComponentsMap(onlyExisting: Boolean,
|
||||
computePresentableNames: Boolean,
|
||||
storageManager: StateStorageManager = ApplicationManager.getApplication().stateStore.stateStorageManager,
|
||||
onlyPaths: Set<String>? = null): Map<File, List<ExportableItem>> {
|
||||
val result = LinkedHashMap<File, MutableList<ExportableItem>>()
|
||||
val processor = { component: ExportableComponent ->
|
||||
val item = ExportableItem(component.exportFiles.toList(), component.presentableName, RoamingType.DEFAULT)
|
||||
for (exportFile in item.files) {
|
||||
@@ -156,10 +179,27 @@ fun getExportableComponentsMap(onlyExisting: Boolean, computePresentableNames: B
|
||||
ApplicationManager.getApplication().getComponents(ExportableApplicationComponent::class.java).forEach(processor)
|
||||
ServiceBean.loadServicesFromBeans(ExportableComponent.EXTENSION_POINT, ExportableComponent::class.java).forEach(processor)
|
||||
|
||||
if (onlyExisting) {
|
||||
@Suppress("DEPRECATED_SYMBOL_WITH_MESSAGE")
|
||||
val configPath = storageManager.expandMacros(StoragePathMacros.ROOT_CONFIG)
|
||||
|
||||
fun isSkipFile(file: File): Boolean {
|
||||
if (onlyPaths != null) {
|
||||
var relativePath = FileUtilRt.getRelativePath(configPath, file.systemIndependentPath, '/')!!
|
||||
if (!file.name.contains('.') && !file.isFile) {
|
||||
relativePath += '/'
|
||||
}
|
||||
if (!onlyPaths.contains(relativePath)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return onlyExisting && !file.exists()
|
||||
}
|
||||
|
||||
if (onlyExisting || onlyPaths != null) {
|
||||
val it = result.keySet().iterator()
|
||||
while (it.hasNext()) {
|
||||
if (!it.next().exists()) {
|
||||
if (isSkipFile(it.next())) {
|
||||
it.remove()
|
||||
}
|
||||
}
|
||||
@@ -183,30 +223,32 @@ fun getExportableComponentsMap(onlyExisting: Boolean, computePresentableNames: B
|
||||
}
|
||||
|
||||
val storage = storages[storageIndex]
|
||||
if (storage.roamingType != RoamingType.DISABLED && storage.storageClass == StateStorage::class && storage.scheme == StorageScheme.DEFAULT && !storage.file.isNullOrEmpty()) {
|
||||
var additionalExportFile: File? = null
|
||||
var additionalExportPath = stateAnnotation.additionalExportFile
|
||||
if (additionalExportPath.isNotEmpty()) {
|
||||
// backward compatibility - path can contain macro
|
||||
@Suppress("DEPRECATED_SYMBOL_WITH_MESSAGE")
|
||||
if (additionalExportPath[0] != '$') {
|
||||
additionalExportPath = StoragePathMacros.ROOT_CONFIG + "/" + additionalExportPath
|
||||
}
|
||||
additionalExportFile = File(storageManager.expandMacros(additionalExportPath))
|
||||
if (onlyExisting && !additionalExportFile.exists()) {
|
||||
additionalExportFile = null
|
||||
}
|
||||
}
|
||||
if (!(storage.roamingType != RoamingType.DISABLED && storage.storageClass == StateStorage::class && storage.scheme == StorageScheme.DEFAULT && !storage.file.isNullOrEmpty())) {
|
||||
return true
|
||||
}
|
||||
|
||||
val file = File(storageManager.expandMacros(storage.file))
|
||||
val fileExists = !onlyExisting || file.exists()
|
||||
if (fileExists || additionalExportFile != null) {
|
||||
val files = if (additionalExportFile == null) listOf(file) else if (fileExists) listOf(file, additionalExportFile) else listOf(additionalExportFile)
|
||||
val item = ExportableItem(files, if (computePresentableNames) getComponentPresentableName(stateAnnotation, aClass, pluginDescriptor) else "", storage.roamingType)
|
||||
result.putValue(file, item)
|
||||
if (additionalExportFile != null) {
|
||||
result.putValue(additionalExportFile, item)
|
||||
}
|
||||
var additionalExportFile: File? = null
|
||||
var additionalExportPath = stateAnnotation.additionalExportFile
|
||||
if (additionalExportPath.isNotEmpty()) {
|
||||
// backward compatibility - path can contain macro
|
||||
@Suppress("DEPRECATED_SYMBOL_WITH_MESSAGE")
|
||||
if (additionalExportPath[0] != '$') {
|
||||
additionalExportPath = StoragePathMacros.ROOT_CONFIG + "/" + additionalExportPath
|
||||
}
|
||||
additionalExportFile = File(storageManager.expandMacros(additionalExportPath))
|
||||
if (isSkipFile(additionalExportFile)) {
|
||||
additionalExportFile = null
|
||||
}
|
||||
}
|
||||
|
||||
val file = File(storageManager.expandMacros(storage.file))
|
||||
val isFileIncluded = !isSkipFile(file)
|
||||
if (isFileIncluded || additionalExportFile != null) {
|
||||
val files = if (additionalExportFile == null) listOf(file) else if (isFileIncluded) listOf(file, additionalExportFile) else listOf(additionalExportFile)
|
||||
val item = ExportableItem(files, if (computePresentableNames) getComponentPresentableName(stateAnnotation, aClass, pluginDescriptor) else "", storage.roamingType)
|
||||
result.putValue(file, item)
|
||||
if (additionalExportFile != null) {
|
||||
result.putValue(additionalExportFile, item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,13 +30,14 @@ import com.intellij.openapi.ui.Messages
|
||||
import com.intellij.openapi.updateSettings.impl.UpdateSettings
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.openapi.util.io.FileUtilRt
|
||||
import com.intellij.openapi.util.io.parentPath
|
||||
import com.intellij.util.Consumer
|
||||
import gnu.trove.THashSet
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
import java.io.InputStream
|
||||
import java.util.zip.ZipException
|
||||
import java.util.zip.ZipFile
|
||||
import java.util.zip.ZipInputStream
|
||||
|
||||
private class ImportSettingsAction : AnAction(), DumbAware {
|
||||
override fun actionPerformed(e: AnActionEvent) {
|
||||
@@ -68,40 +69,26 @@ private class ImportSettingsAction : AnAction(), DumbAware {
|
||||
return
|
||||
}
|
||||
|
||||
@SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
|
||||
val magicEntry = ZipFile(saveFile).getEntry(ImportSettingsFilenameFilter.SETTINGS_JAR_MARKER)
|
||||
if (magicEntry == null) {
|
||||
val relativePaths = getPaths(saveFile.inputStream())
|
||||
if (!relativePaths.contains(ImportSettingsFilenameFilter.SETTINGS_JAR_MARKER)) {
|
||||
Messages.showErrorDialog(
|
||||
IdeBundle.message("error.file.contains.no.settings.to.import", presentableFileName(saveFile), promptLocationMessage()),
|
||||
IdeBundle.message("title.invalid.file"))
|
||||
return
|
||||
}
|
||||
|
||||
val fileToComponents = getExportableComponentsMap(false, true)
|
||||
val components = getComponentsStored(saveFile, fileToComponents.values())
|
||||
fileToComponents.values().retainAll(components)
|
||||
val dialog = ChooseComponentsToExportDialog(fileToComponents, false,
|
||||
val configPath = FileUtil.toSystemIndependentName(PathManager.getConfigPath())
|
||||
val dialog = ChooseComponentsToExportDialog(getExportableComponentsMap(false, true, onlyPaths = relativePaths), false,
|
||||
IdeBundle.message("title.select.components.to.import"),
|
||||
IdeBundle.message("prompt.check.components.to.import"))
|
||||
if (!dialog.showAndGet()) {
|
||||
return
|
||||
}
|
||||
|
||||
val chosenComponents = dialog.exportableComponents
|
||||
val relativeNamesToExtract = THashSet<String>()
|
||||
for (chosenComponent in chosenComponents) {
|
||||
for (exportFile in chosenComponent.files) {
|
||||
relativeNamesToExtract.add(FileUtil.toSystemIndependentName(FileUtilRt.getRelativePath(File(PathManager.getConfigPath()), exportFile)!!))
|
||||
}
|
||||
}
|
||||
|
||||
relativeNamesToExtract.add(PluginManager.INSTALLED_TXT)
|
||||
|
||||
val tempFile = File(PathManager.getPluginTempPath(), saveFile.name)
|
||||
FileUtil.copy(saveFile, tempFile)
|
||||
val outDir = File(PathManager.getConfigPath())
|
||||
val filenameFilter = ImportSettingsFilenameFilter(relativeNamesToExtract)
|
||||
StartupActionScriptManager.addActionCommand(StartupActionScriptManager.UnzipCommand(tempFile, outDir, filenameFilter))
|
||||
val filenameFilter = ImportSettingsFilenameFilter(getRelativeNamesToExtract(dialog.exportableComponents))
|
||||
StartupActionScriptManager.addActionCommand(StartupActionScriptManager.UnzipCommand(tempFile, File(configPath), filenameFilter))
|
||||
// remove temp file
|
||||
StartupActionScriptManager.addActionCommand(StartupActionScriptManager.DeleteCommand(tempFile))
|
||||
|
||||
@@ -119,38 +106,39 @@ private class ImportSettingsAction : AnAction(), DumbAware {
|
||||
}
|
||||
}
|
||||
|
||||
private fun getRelativeNamesToExtract(chosenComponents: Set<ExportableItem>): Set<String> {
|
||||
val result = THashSet<String>()
|
||||
for (chosenComponent in chosenComponents) {
|
||||
for (exportFile in chosenComponent.files) {
|
||||
result.add(FileUtil.toSystemIndependentName(FileUtilRt.getRelativePath(File(PathManager.getConfigPath()), exportFile)!!))
|
||||
}
|
||||
}
|
||||
|
||||
result.add(PluginManager.INSTALLED_TXT)
|
||||
return result
|
||||
}
|
||||
|
||||
private fun presentableFileName(file: File) = "'" + FileUtil.toSystemDependentName(file.path) + "'"
|
||||
|
||||
private fun promptLocationMessage() = IdeBundle.message("message.please.ensure.correct.settings")
|
||||
}
|
||||
|
||||
private fun getComponentsStored(settings: File, registeredComponents: Collection<ExportableItem>): List<ExportableItem> {
|
||||
val zipEntries = THashSet<String>()
|
||||
val zipFile = ZipFile(settings)
|
||||
try {
|
||||
val enumeration = zipFile.entries()
|
||||
while (enumeration.hasMoreElements()) {
|
||||
zipEntries.add(enumeration.nextElement().name)
|
||||
fun getPaths(input: InputStream): Set<String> {
|
||||
val result = THashSet<String>()
|
||||
val zipIn = ZipInputStream(input)
|
||||
try {
|
||||
while (true) {
|
||||
val entry = zipIn.nextEntry ?: break
|
||||
var path = entry.name
|
||||
result.add(path)
|
||||
while (true) {
|
||||
path = path.parentPath ?: break
|
||||
result.add("$path/")
|
||||
}
|
||||
}
|
||||
finally {
|
||||
zipFile.close()
|
||||
}
|
||||
|
||||
val configPath = File(PathManager.getConfigPath())
|
||||
val components = ArrayList<ExportableItem>()
|
||||
for (component in registeredComponents) {
|
||||
for (exportFile in component.files) {
|
||||
var relativePath = FileUtilRt.getRelativePath(configPath, exportFile)!!
|
||||
relativePath = FileUtilRt.toSystemIndependentName(relativePath)
|
||||
if (exportFile.name.indexOf('.') == -1 && !exportFile.isFile) {
|
||||
relativePath += '/'
|
||||
}
|
||||
if (zipEntries.contains(relativePath)) {
|
||||
components.add(component)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return components
|
||||
}
|
||||
finally {
|
||||
zipIn.close()
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
+12
-10
@@ -40,6 +40,7 @@ import com.intellij.util.pico.ConstructorInjectionComponentAdapter;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.picocontainer.*;
|
||||
import org.picocontainer.defaults.InstanceComponentAdapter;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
@@ -142,17 +143,18 @@ public class ServiceManagerImpl implements BaseComponent {
|
||||
}
|
||||
}
|
||||
else if (o instanceof ComponentAdapter && !(o instanceof ExtensionComponentAdapter)) {
|
||||
try {
|
||||
aClass = ((ComponentAdapter)o).getComponentImplementation();
|
||||
}
|
||||
catch (Throwable e) {
|
||||
LOG.error(e);
|
||||
continue;
|
||||
}
|
||||
PluginId pluginId = componentManager.getConfig((ComponentAdapter)o);
|
||||
// allow InstanceComponentAdapter without pluginId to test
|
||||
if (pluginId != null || o instanceof InstanceComponentAdapter) {
|
||||
try {
|
||||
aClass = ((ComponentAdapter)o).getComponentImplementation();
|
||||
}
|
||||
catch (Throwable e) {
|
||||
LOG.error(e);
|
||||
continue;
|
||||
}
|
||||
|
||||
PluginId pluginId = componentManager.getConfig(aClass);
|
||||
if (pluginId != null) {
|
||||
processor.process(aClass, PluginManager.getPlugin(pluginId));
|
||||
processor.process(aClass, pluginId == null ? null : PluginManager.getPlugin(pluginId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,9 +17,20 @@ package com.intellij.openapi.util.io
|
||||
|
||||
import java.io.File
|
||||
|
||||
public val File.systemIndependentPath: String
|
||||
get() = getPath().replace(File.separatorChar, '/')
|
||||
val File.systemIndependentPath: String
|
||||
get() = path.replace(File.separatorChar, '/')
|
||||
|
||||
val File.parentSystemIndependentPath: String
|
||||
get() = getParent().replace(File.separatorChar, '/')
|
||||
|
||||
public val File.parentSystemIndependentPath: String
|
||||
get() = getParent().replace(File.separatorChar, '/')
|
||||
val String.parentPath: String?
|
||||
get() {
|
||||
if (isEmpty()) {
|
||||
return null
|
||||
}
|
||||
var end = Math.max(lastIndexOf('/'), lastIndexOf('\\'))
|
||||
if (end == length() - 1) {
|
||||
end = Math.max(lastIndexOf('/', end - 1), lastIndexOf('\\', end - 1))
|
||||
}
|
||||
return if (end == -1) null else substring(0, end)
|
||||
}
|
||||
@@ -42,7 +42,7 @@ fun copyLocalConfig(storageManager: StateStorageManagerImpl = ApplicationManager
|
||||
}
|
||||
}
|
||||
|
||||
val roamingType = getRoamingType(fileToComponents.get(file))
|
||||
val roamingType = getRoamingType(fileToComponents.get(file)!!)
|
||||
if (file.isFile) {
|
||||
val fileBytes = FileUtil.loadFileBytes(file)
|
||||
streamProvider.doSave(fileSpec, fileBytes, fileBytes.size(), roamingType)
|
||||
|
||||
Reference in New Issue
Block a user