Merge remote-tracking branch 'origin/master'

This commit is contained in:
Kirill Kirichenko
2017-04-27 17:42:01 +03:00
20 changed files with 292 additions and 57 deletions
@@ -172,7 +172,7 @@ public class SuppressFix extends AbstractBatchSuppressByNoInspectionCommentFix {
if (alternativeID != null) {
final Module module = ModuleUtilCore.findModuleForPsiElement(place);
if (module != null) {
if (!ClassPathStorageUtil.isDefaultStorage(module)) {
if (ClassPathStorageUtil.isClasspathStorage(module)) {
return alternativeID;
}
}
@@ -375,15 +375,15 @@ abstract class ComponentStoreImpl : IComponentStore {
}
}
protected open fun <T> getStorageSpecs(component: PersistentStateComponent<T>, stateSpec: State, operation: StateStorageOperation): Array<out Storage> {
protected open fun <T> getStorageSpecs(component: PersistentStateComponent<T>, stateSpec: State, operation: StateStorageOperation): List<Storage> {
val storages = stateSpec.storages
if (storages.size == 1 || component is StateStorageChooserEx) {
return storages
return storages.toList()
}
if (storages.isEmpty()) {
if (stateSpec.defaultStateAsResource) {
return storages
return emptyList()
}
throw AssertionError("No storage specified")
@@ -513,26 +513,26 @@ enum class StateLoadPolicy {
LOAD, LOAD_ONLY_DEFAULT, NOT_LOAD
}
internal fun Array<Storage>.sortByDeprecated(): Array<out Storage> {
if (isEmpty()) {
return this
internal fun Array<out Storage>.sortByDeprecated(): List<Storage> {
if (size < 2) {
return toList()
}
if (!this[0].deprecated) {
if (!first().deprecated) {
var othersAreDeprecated = true
for (i in 1..size - 1) {
if (!this[i].deprecated) {
if (!get(i).deprecated) {
othersAreDeprecated = false
break
}
}
if (othersAreDeprecated) {
return this
return toList()
}
}
return sortedArrayWith(deprecatedComparator)
return sortedWith(deprecatedComparator)
}
private fun notifyUnknownMacros(store: IComponentStore, project: Project, componentName: String) {
@@ -83,13 +83,13 @@ internal class DefaultProjectStoreImpl(override val project: ProjectImpl, privat
override fun getPathMacroManagerForDefaults() = pathMacroManager
override fun <T> getStorageSpecs(component: PersistentStateComponent<T>, stateSpec: State, operation: StateStorageOperation) = arrayOf(PROJECT_FILE_STORAGE_ANNOTATION)
override fun <T> getStorageSpecs(component: PersistentStateComponent<T>, stateSpec: State, operation: StateStorageOperation) = listOf(PROJECT_FILE_STORAGE_ANNOTATION)
override fun setPath(path: String) {
}
private class MyExternalizationSession(val externalizationSession: StateStorage.ExternalizationSession) : StateStorageManager.ExternalizationSession {
override fun setState(storageSpecs: Array<out Storage>, component: Any, componentName: String, state: Any) {
override fun setState(storageSpecs: List<Storage>, component: Any, componentName: String, state: Any) {
externalizationSession.setState(component, componentName, state)
}
@@ -2,18 +2,25 @@ package com.intellij.configurationStore;
import com.intellij.openapi.components.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.annotation.Annotation;
@SuppressWarnings("ClassExplicitlyAnnotation")
final class FileStorageAnnotation implements Storage {
public final class FileStorageAnnotation implements Storage {
private String path;
private boolean deprecated;
private final Class<? extends StateStorage> storageClass;
FileStorageAnnotation(String path, boolean deprecated) {
public FileStorageAnnotation(@NotNull String path, boolean deprecated) {
this(path, deprecated, StateStorage.class);
}
public FileStorageAnnotation(@NotNull String path, boolean deprecated, @Nullable Class<? extends StateStorage> storageClass) {
this.path = path;
this.deprecated = deprecated;
this.storageClass = storageClass;
}
@Override
@@ -53,7 +60,7 @@ final class FileStorageAnnotation implements Storage {
@Override
public Class<? extends StateStorage> storageClass() {
return StateStorage.class;
return storageClass;
}
@Override
@@ -16,8 +16,10 @@
package com.intellij.configurationStore
import com.intellij.openapi.components.*
import com.intellij.openapi.diagnostic.catchAndLog
import com.intellij.openapi.module.Module
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.util.containers.computeOrNull
import com.intellij.util.io.exists
import java.nio.file.Paths
@@ -30,34 +32,42 @@ private open class ModuleStoreImpl(module: Module, private val pathMacroManager:
override final fun getPathMacroManagerForDefaults() = pathMacroManager
private class TestModuleStore(module: Module, pathMacroManager: PathMacroManager) : ModuleStoreImpl(module, pathMacroManager) {
private var moduleComponentLoadPolicy: StateLoadPolicy? = null
override fun setPath(path: String) {
setPath(path, null)
}
override fun setPath(path: String, file: VirtualFile?) {
super.setPath(path, file)
if ((file != null && file.isValid) || Paths.get(path).exists()) {
moduleComponentLoadPolicy = StateLoadPolicy.LOAD
}
}
override val loadPolicy: StateLoadPolicy
get() = moduleComponentLoadPolicy ?: (project.stateStore as ComponentStoreImpl).loadPolicy
// todo what about Upsource? For now this implemented not in the ModuleStoreBase because `project` and `module` are available only in this class (ModuleStoreImpl)
override fun <T> getStorageSpecs(component: PersistentStateComponent<T>, stateSpec: State, operation: StateStorageOperation): List<Storage> {
val result = super.getStorageSpecs(component, stateSpec, operation)
return StreamProviderFactory.EP_NAME.getExtensions(project).computeOrNull {
LOG.catchAndLog { it.customizeStorageSpecs(component, storageManager.componentManager!!, result, operation) }
} ?: result
}
}
private class TestModuleStore(module: Module, pathMacroManager: PathMacroManager) : ModuleStoreImpl(module, pathMacroManager) {
private var moduleComponentLoadPolicy: StateLoadPolicy? = null
override fun setPath(path: String) {
setPath(path, null)
}
override fun setPath(path: String, file: VirtualFile?) {
super.setPath(path, file)
if ((file != null && file.isValid) || Paths.get(path).exists()) {
moduleComponentLoadPolicy = StateLoadPolicy.LOAD
}
}
override val loadPolicy: StateLoadPolicy
get() = moduleComponentLoadPolicy ?: (project.stateStore as ComponentStoreImpl).loadPolicy
}
// used in upsource
abstract class ModuleStoreBase : ComponentStoreImpl() {
override abstract val storageManager: StateStorageManagerImpl
override final fun <T> getStorageSpecs(component: PersistentStateComponent<T>, stateSpec: State, operation: StateStorageOperation): Array<out Storage> {
override fun <T> getStorageSpecs(component: PersistentStateComponent<T>, stateSpec: State, operation: StateStorageOperation): List<Storage> {
val storages = stateSpec.storages
return if (storages.isEmpty()) {
arrayOf(MODULE_FILE_STORAGE_ANNOTATION)
listOf(MODULE_FILE_STORAGE_ANNOTATION)
}
else {
super.getStorageSpecs(component, stateSpec, operation)
@@ -177,10 +177,10 @@ abstract class ProjectStoreBase(override final val project: ProjectImpl) : Compo
}
}
override fun <T> getStorageSpecs(component: PersistentStateComponent<T>, stateSpec: State, operation: StateStorageOperation): Array<out Storage> {
override fun <T> getStorageSpecs(component: PersistentStateComponent<T>, stateSpec: State, operation: StateStorageOperation): List<Storage> {
val storages = stateSpec.storages
if (storages.isEmpty()) {
return arrayOf(PROJECT_FILE_STORAGE_ANNOTATION)
return listOf(PROJECT_FILE_STORAGE_ANNOTATION)
}
if (isDirectoryBased) {
@@ -196,14 +196,14 @@ abstract class ProjectStoreBase(override final val project: ProjectImpl) : Compo
}
if (result.isNullOrEmpty()) {
return arrayOf(PROJECT_FILE_STORAGE_ANNOTATION)
return listOf(PROJECT_FILE_STORAGE_ANNOTATION)
}
else {
result!!.sortWith(deprecatedComparator)
// if we create project from default, component state written not to own storage file, but to project file,
// we don't have time to fix it properly, so, ancient hack restored
result.add(DEPRECATED_PROJECT_FILE_STORAGE_ANNOTATION)
return result.toTypedArray()
return result
}
}
else {
@@ -223,14 +223,14 @@ abstract class ProjectStoreBase(override final val project: ProjectImpl) : Compo
}
}
if (result.isNullOrEmpty()) {
return arrayOf(PROJECT_FILE_STORAGE_ANNOTATION)
return listOf(PROJECT_FILE_STORAGE_ANNOTATION)
}
else {
if (hasOnlyDeprecatedStorages) {
result!!.add(PROJECT_FILE_STORAGE_ANNOTATION)
}
result!!.sortWith(deprecatedComparator)
return result.toTypedArray()
return result
}
}
}
@@ -384,7 +384,7 @@ open class StateStorageManagerImpl(private val rootTagName: String,
override final fun startExternalization() = object : StateStorageManager.ExternalizationSession {
private val sessions = LinkedHashMap<StateStorage, StateStorage.ExternalizationSession>()
override fun setState(storageSpecs: Array<out Storage>, component: Any, componentName: String, state: Any) {
override fun setState(storageSpecs: List<Storage>, component: Any, componentName: String, state: Any) {
val stateStorageChooser = component as? StateStorageChooserEx
for (storageSpec in storageSpecs) {
@Suppress("IfThenToElvis")
@@ -27,5 +27,6 @@
<orderEntry type="module" module-name="compiler-impl" />
<orderEntry type="module" module-name="java-impl" />
<orderEntry type="module" module-name="openapi" />
<orderEntry type="module" module-name="configuration-store-impl" />
</component>
</module>
@@ -0,0 +1,46 @@
/*
* 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.externalSystem.configurationStore
import com.intellij.configurationStore.*
import com.intellij.openapi.components.RoamingType
import com.intellij.openapi.components.StoragePathMacros
import com.intellij.openapi.module.Module
import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream
import org.jdom.Element
import java.io.ByteArrayInputStream
internal class ExternalProjectStorage(private val module: Module, storageManager: StateStorageManager) : XmlElementStorage(StoragePathMacros.MODULE_FILE, null, storageManager.macroSubstitutor, RoamingType.DISABLED) {
private val manager = StreamProviderFactory.EP_NAME.getExtensions(module.project).first { it is ExternalSystemStreamProviderFactory } as ExternalSystemStreamProviderFactory
override public fun loadLocalData(): Element? {
val data = manager.nameToData.get(module.name) ?: return null
return ByteArrayInputStream(data).use { readElement(it) }
}
override fun createSaveSession(states: StateMap) = object : XmlElementStorageSaveSession<ExternalProjectStorage>(states, this) {
override fun saveLocally(element: Element?) {
if (element == null) {
manager.nameToData.remove(module.name)
}
else {
val byteOut = BufferExposingByteArrayOutputStream()
serializeElementToBinary(element, byteOut)
manager.nameToData.put(module.name, byteOut.toByteArray())
}
}
}
}
@@ -0,0 +1,121 @@
/*
* 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.externalSystem.configurationStore
import com.intellij.configurationStore.FileStorageAnnotation
import com.intellij.configurationStore.StreamProviderFactory
import com.intellij.openapi.Disposable
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.components.*
import com.intellij.openapi.diagnostic.catchAndLog
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.externalSystem.service.project.manage.ExternalProjectsDataStorage
import com.intellij.openapi.module.Module
import com.intellij.openapi.project.Project
import com.intellij.openapi.project.ex.ProjectEx
import com.intellij.openapi.roots.ExternalProjectSystemRegistry
import com.intellij.openapi.roots.ProjectModelElement
import com.intellij.openapi.util.Disposer
import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.util.registry.Registry
import com.intellij.util.io.DataExternalizer
import com.intellij.util.io.EnumeratorStringDescriptor
import com.intellij.util.io.PersistentHashMap
import java.io.*
import java.util.*
private val EXTERNAL_STORAGE_ANNOTATION = FileStorageAnnotation(StoragePathMacros.MODULE_FILE, false, ExternalProjectStorage::class.java)
private val LOG = logger<ExternalSystemStreamProviderFactory>()
// todo handle module rename
internal class ExternalSystemStreamProviderFactory(private val project: Project) : StreamProviderFactory {
val nameToData = createStorage(project)
private var isStorageFlushed = true
init {
Disposer.register(project, Disposable { nameToData.close() })
// flush on save to be sure that data is saved (it is easy to reimport if corrupted (force exit, blue screen), but we need to avoid it if possible)
ApplicationManager.getApplication().messageBus
.connect(project)
.subscribe(ProjectEx.ProjectSaved.TOPIC, ProjectEx.ProjectSaved {
if (it === project && !isStorageFlushed && nameToData.isDirty) {
isStorageFlushed = false
ApplicationManager.getApplication().executeOnPooledThread {
try {
LOG.catchAndLog { nameToData.force() }
}
finally {
isStorageFlushed = true
}
}
}
})
}
override fun customizeStorageSpecs(component: PersistentStateComponent<*>, componentManager: ComponentManager, storages: List<Storage>, operation: StateStorageOperation): List<Storage>? {
if (componentManager !is Module || component !is ProjectModelElement || !Registry.`is`("store.imported.project.elements.separately", false)) {
return null
}
if (operation == StateStorageOperation.WRITE) {
// Keep in mind - this call will require storage for module because module option values are used.
// We cannot just check that module name exists in the nameToData - new external system module will be not in the nameToData because not yet saved.
@Suppress("INTERFACE_STATIC_METHOD_CALL_FROM_JAVA6_TARGET")
if (ExternalProjectSystemRegistry.getInstance().getExternalSource(componentManager) == null) {
return null
}
}
else {
// on read we cannot check because on import module is just created and not yet marked as external system module,
// so, we just add our storage as first and default storages in the end as fallback
val result = ArrayList<Storage>(storages.size + 1)
result.add(EXTERNAL_STORAGE_ANNOTATION)
result.addAll(storages)
return result
}
// todo we can return on StateStorageOperation.WRITE default iml storage and then somehow using StateStorageChooserEx return Resolution.CLEAR to remove data from iml
return listOf(EXTERNAL_STORAGE_ANNOTATION)
}
}
private fun createStorage(project: Project): PersistentHashMap<String, ByteArray> {
val file = File(ExternalProjectsDataStorage.getProjectConfigurationDir(), "${project.locationHash}/projectConfiguration")
fun createMap() = PersistentHashMap<String, ByteArray>(file, EnumeratorStringDescriptor.INSTANCE, object : DataExternalizer<ByteArray> {
override fun read(`in`: DataInput): ByteArray {
val available = (`in` as InputStream).available()
val result = ByteArray(available)
`in`.readFully(result)
return result
}
override fun save(out: DataOutput, value: ByteArray) {
out.write(value)
}
})
try {
return createMap()
}
catch (e: IOException) {
// todo force project reimport
LOG.info(e)
FileUtil.delete(file)
}
return createMap()
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
* 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.
@@ -394,7 +394,7 @@ public class ExternalProjectsDataStorage implements SettingsSavingComponent, Per
return new File(getProjectConfigurationDir(), project.getLocationHash() + "/project.dat");
}
private static File getProjectConfigurationDir() {
public static File getProjectConfigurationDir() {
return getExternalBuildSystemDir("Projects");
}
@@ -51,14 +51,16 @@ public class ModuleRootManagerComponent extends ModuleRootManagerImpl implements
@NotNull
@Override
public Resolution getResolution(@NotNull Storage storage, @NotNull StateStorageOperation operation) {
boolean isDefault = storage.storageClass() == StateStorage.class;
boolean isEffectiveStorage = ClassPathStorageUtil.isDefaultStorage(getModule()) == isDefault;
boolean isClasspathStorage = storage.storageClass() == ClasspathStorage.class;
boolean isEffectiveStorage = ClassPathStorageUtil.isClasspathStorage(getModule()) == isClasspathStorage;
if (operation == StateStorageOperation.READ) {
return isEffectiveStorage ? Resolution.DO : Resolution.SKIP;
}
else {
// IDEA-133480 Eclipse integration: .iml content is not reduced on setting Dependencies Storage Format = Eclipse
return isEffectiveStorage ? Resolution.DO : (isDefault ? Resolution.CLEAR : Resolution.SKIP);
// We clear any storage except eclipse (because we must not clear shared files).
// Currently there is only one known non-default storage - ExternalProjectStorage.
return isEffectiveStorage ? Resolution.DO : (isClasspathStorage ? Resolution.SKIP : Resolution.CLEAR);
}
}
@@ -27,6 +27,7 @@ import com.intellij.ui.LayeredIcon;
import com.intellij.ui.UIBundle;
import com.intellij.util.IconUtil;
import com.intellij.util.PlatformIcons;
import gnu.trove.THashMap;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -56,7 +57,7 @@ public class FileChooserDescriptor implements Cloneable {
private Condition<VirtualFile> myFileFilter = null;
private boolean myForcedToUseIdeaFileChooser = false;
private final Map<String, Object> myUserData = new HashMap<>();
private final Map<String, Object> myUserData = new THashMap<>();
/**
* Creates new instance. Use methods from {@link FileChooserDescriptorFactory} for most used descriptors.
@@ -344,7 +345,7 @@ public class FileChooserDescriptor implements Cloneable {
}
@Nullable
public Object getUserData(String dataId) {
public Object getUserData(@NotNull String dataId) {
return myUserData.get(dataId);
}
@@ -42,7 +42,8 @@
<!--Execution-->
<programRunner implementation="com.intellij.openapi.externalSystem.service.execution.ExternalSystemTaskRunner"/>
<programRunner implementation="com.intellij.openapi.externalSystem.service.execution.ExternalSystemTaskDebugRunner"/>
<streamProviderFactory implementation="com.intellij.openapi.externalSystem.configurationStore.ExternalSystemStreamProviderFactory"/>
</extensions>
</idea-plugin>
@@ -353,7 +353,7 @@
serviceImplementation="com.intellij.openapi.roots.impl.ExternalProjectSystemRegistryImpl"/>
<moduleService serviceInterface="com.intellij.openapi.components.impl.stores.IComponentStore"
serviceImplementation="com.intellij.configurationStore.ModuleStoreImpl"
testServiceImplementation="com.intellij.configurationStore.ModuleStoreImpl$TestModuleStore"/>
testServiceImplementation="com.intellij.configurationStore.TestModuleStore"/>
<moduleService serviceInterface="com.intellij.openapi.roots.TestModuleProperties"
serviceImplementation="com.intellij.openapi.roots.impl.TestModulePropertiesImpl"/>
@@ -283,5 +283,6 @@
<extensionPoint name="bundledInspectionProfile" beanClass="com.intellij.configurationStore.BundledSchemeEP"/>
<extensionPoint name="projectNameProvider" beanClass="com.intellij.openapi.project.ex.ProjectNameProvider"/>
<extensionPoint name="streamProviderFactory" interface="com.intellij.configurationStore.StreamProviderFactory" area="IDEA_PROJECT"/>
</extensionPoints>
</idea-plugin>
@@ -48,7 +48,7 @@ interface StateStorageManager {
fun expandMacros(path: String): String
interface ExternalizationSession {
fun setState(storageSpecs: Array<out Storage>, component: Any, componentName: String, state: Any)
fun setState(storageSpecs: List<Storage>, component: Any, componentName: String, state: Any)
fun setStateInOldStorage(component: Any, componentName: String, state: Any)
@@ -0,0 +1,44 @@
/*
* 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.configurationStore
import com.intellij.openapi.components.ComponentManager
import com.intellij.openapi.components.PersistentStateComponent
import com.intellij.openapi.components.StateStorageOperation
import com.intellij.openapi.components.Storage
import com.intellij.openapi.extensions.ExtensionPointName
/**
* Project level extension point.
*
* Allows to set custom storage class using providing custom storage specs.
* Or set custom stream provider for default storage (XmlElementStorage).
*/
interface StreamProviderFactory {
companion object {
val EP_NAME = ExtensionPointName.create<StreamProviderFactory>("com.intellij.streamProviderFactory")
}
fun createProvider(componentManager: ComponentManager, storageManager: StateStorageManager): StreamProvider? = null
/**
* For now called only for Module.
* `storages` are preprocessed by component store - not raw from state spec.
*
* @return null if not applicable
*/
fun customizeStorageSpecs(component: PersistentStateComponent<*>, componentManager: ComponentManager, storages: List<Storage>, operation: StateStorageOperation): List<Storage>? = null
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
* 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.
@@ -23,13 +23,13 @@ import org.jetbrains.jps.model.serialization.JpsProjectLoader;
public class ClassPathStorageUtil {
@NonNls public static final String DEFAULT_STORAGE = "default";
public static boolean isDefaultStorage(@NotNull Module module) {
return getStorageType(module).equals(DEFAULT_STORAGE);
}
@NotNull
public static String getStorageType(@NotNull Module module) {
String id = module.getOptionValue(JpsProjectLoader.CLASSPATH_ATTRIBUTE);
return id == null ? DEFAULT_STORAGE : id;
}
public static boolean isClasspathStorage(@NotNull Module module) {
return module.getOptionValue(JpsProjectLoader.CLASSPATH_ATTRIBUTE) != null;
}
}
@@ -42,6 +42,7 @@ public class ReformatCommitMessageAction extends DumbAwareAction {
public static final String NAME = "Reformat commit message";
public ReformatCommitMessageAction() {
super(NAME);
setEnabledInModalContext(true);
}