[workspace model] store java language level in JavaModuleSettingsEntity (IDEA-266774, IDEA-270617)

This provides better API to read/change language level via workspace model storage and decreases memory usage (storing language level id instead of xml tag with attribute). In order to allow contributing implementations of ModuleExtension based on data from workspace model storage from plugins, ModuleExtensionBridgeFactory extension point is introduced.

GitOrigin-RevId: 51d7e38390bb8df19e072e16a328514d6e863597
This commit is contained in:
Nikolay Chashnikov
2021-06-04 13:46:00 +03:00
committed by intellij-monorepo-bot
parent f6b8636486
commit c5a4251cc8
31 changed files with 273 additions and 128 deletions

View File

@@ -2,34 +2,14 @@
package com.intellij.openapi.roots;
import com.intellij.openapi.components.PersistentStateComponentWithModificationTracker;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.pom.java.LanguageLevel;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* This is an internal class, use {@link LanguageLevelModuleExtension} instead.
*/
@ApiStatus.Internal
public class LanguageLevelModuleExtensionImpl extends ModuleExtension implements LanguageLevelModuleExtension,
PersistentStateComponentWithModificationTracker<LanguageLevelState> {
private static final Logger LOG = Logger.getInstance(LanguageLevelModuleExtensionImpl.class);
private Module myModule;
private final boolean myWritable;
private final LanguageLevelModuleExtensionImpl mySource;
private LanguageLevelState myState = new LanguageLevelState();
@Override
public long getStateModificationCount() {
return myState.getModificationCount();
}
public abstract class LanguageLevelModuleExtensionImpl extends ModuleExtension implements LanguageLevelModuleExtension {
/**
* @deprecated this method returns an implementation specific class, use {@link com.intellij.openapi.module.LanguageLevelUtil#getCustomLanguageLevel(Module)} instead.
*/
@@ -37,68 +17,4 @@ public class LanguageLevelModuleExtensionImpl extends ModuleExtension implements
public static LanguageLevelModuleExtensionImpl getInstance(final Module module) {
return ModuleRootManager.getInstance(module).getModuleExtension(LanguageLevelModuleExtensionImpl.class);
}
public LanguageLevelModuleExtensionImpl(Module module) {
myModule = module;
mySource = null;
myWritable = false;
}
public LanguageLevelModuleExtensionImpl(final LanguageLevelModuleExtensionImpl source, boolean writable) {
myWritable = writable;
myModule = source.myModule;
mySource = source;
// setter must be used instead of creating new state with constructor param because in any case default language level for module is null (i.e. project language level)
myState.setLanguageLevel(source.getLanguageLevel());
}
@Override
public void setLanguageLevel(final LanguageLevel languageLevel) {
if (myState.getLanguageLevel() == languageLevel) return;
LOG.assertTrue(myWritable, "Writable model can be retrieved from writable ModifiableRootModel");
myState.setLanguageLevel(languageLevel);
}
@Nullable
@Override
public LanguageLevelState getState() {
return myState;
}
@Override
public void loadState(@NotNull LanguageLevelState state) {
myState = state;
}
@NotNull
@Override
public ModuleExtension getModifiableModel(final boolean writable) {
return new LanguageLevelModuleExtensionImpl(this, writable);
}
@Override
public void commit() {
if (isChanged()) {
mySource.myState.setLanguageLevel(myState.getLanguageLevel());
LanguageLevelProjectExtension.getInstance(myModule.getProject()).languageLevelsChanged();
}
}
@Override
public boolean isChanged() {
return mySource != null && !mySource.myState.equals(myState);
}
@Override
public void dispose() {
myModule = null;
myState = null;
}
@Nullable
@Override
public LanguageLevel getLanguageLevel() {
return myState.getLanguageLevel();
}
}

View File

@@ -28,5 +28,6 @@
<orderEntry type="library" name="Guava" level="project" />
<orderEntry type="library" name="fastutil-min" level="project" />
<orderEntry type="library" name="Trove4j" level="project" />
<orderEntry type="module" module-name="intellij.platform.workspaceModel.storage" />
</component>
</module>

View File

@@ -268,6 +268,7 @@
<syntaxHighlighter id="java.class" key="CLASS" factoryClass="com.intellij.lang.java.JavaSyntaxHighlighterFactory"/>
<lang.syntaxHighlighterFactory language="JAVA" implementationClass="com.intellij.lang.java.JavaSyntaxHighlighterFactory"/>
<moduleExtension implementation="com.intellij.openapi.roots.impl.JavaModuleExternalPathsImpl"/>
<workspaceModel.moduleExtensionBridgeFactory implementation="com.intellij.workspaceModel.ide.legacyBridge.impl.java.LanguageLevelModuleExtensionBridge$Companion"/>
<localInspection groupPath="Java" language="JAVA" shortName="ClassGetClass"
groupBundle="messages.InspectionsBundle" groupKey="group.names.probable.bugs"
enabledByDefault="true" level="WARNING"

View File

@@ -0,0 +1,25 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.workspaceModel.ide.java
import com.intellij.pom.java.LanguageLevel
import com.intellij.workspaceModel.storage.bridgeEntities.JavaModuleSettingsEntity
import com.intellij.workspaceModel.storage.bridgeEntities.ModifiableJavaModuleSettingsEntity
var ModifiableJavaModuleSettingsEntity.languageLevel: LanguageLevel?
get() = idToLanguageLevel(languageLevelId)
set(value) {
languageLevelId = value?.name
}
val JavaModuleSettingsEntity.languageLevel: LanguageLevel?
get() = idToLanguageLevel(languageLevelId)
private fun idToLanguageLevel(id: String?): LanguageLevel? {
return try {
LanguageLevel.valueOf(id ?: return null)
}
catch (e: IllegalArgumentException) {
null
}
}

View File

@@ -0,0 +1,67 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.workspaceModel.ide.legacyBridge.impl.java
import com.intellij.openapi.roots.LanguageLevelModuleExtensionImpl
import com.intellij.openapi.roots.ModuleExtension
import com.intellij.pom.java.LanguageLevel
import com.intellij.workspaceModel.ide.java.languageLevel
import com.intellij.workspaceModel.ide.legacyBridge.ModuleBridge
import com.intellij.workspaceModel.ide.legacyBridge.ModuleExtensionBridge
import com.intellij.workspaceModel.ide.legacyBridge.ModuleExtensionBridgeFactory
import com.intellij.workspaceModel.storage.ExternalEntityMapping
import com.intellij.workspaceModel.storage.VersionedEntityStorage
import com.intellij.workspaceModel.storage.WorkspaceEntityStorage
import com.intellij.workspaceModel.storage.WorkspaceEntityStorageDiffBuilder
import com.intellij.workspaceModel.storage.bridgeEntities.ModifiableJavaModuleSettingsEntity
import com.intellij.workspaceModel.storage.bridgeEntities.ModuleEntity
import com.intellij.workspaceModel.storage.bridgeEntities.addJavaModuleSettingsEntity
class LanguageLevelModuleExtensionBridge private constructor(private val module: ModuleBridge,
private val entityStorage: VersionedEntityStorage,
private val diff: WorkspaceEntityStorageDiffBuilder?) : LanguageLevelModuleExtensionImpl(), ModuleExtensionBridge {
private var changed = false
private val moduleEntity
get() = entityStorage.current.moduleMap.getEntities(module).firstOrNull() as ModuleEntity?
override fun setLanguageLevel(languageLevel: LanguageLevel?) {
if (diff == null) error("Cannot modify data via read-only extensions")
val moduleEntity = moduleEntity ?: error("Cannot find entity for $module")
val javaSettings = moduleEntity.javaSettings
if (javaSettings != null) {
diff.modifyEntity(ModifiableJavaModuleSettingsEntity::class.java, javaSettings) {
this.languageLevel = languageLevel
}
}
else if (languageLevel != null) {
diff.addJavaModuleSettingsEntity(inheritedCompilerOutput = true, excludeOutput = true, compilerOutput = null,
compilerOutputForTests = null, languageLevelId = languageLevel.name, module = moduleEntity,
source = moduleEntity.entitySource)
}
}
override fun getLanguageLevel(): LanguageLevel? {
return moduleEntity?.javaSettings?.languageLevel
}
override fun isChanged(): Boolean = changed
override fun getModifiableModel(writable: Boolean): ModuleExtension {
throw UnsupportedOperationException("This method must not be called for extensions backed by workspace model")
}
override fun commit() = Unit
override fun dispose() = Unit
companion object : ModuleExtensionBridgeFactory<LanguageLevelModuleExtensionBridge> {
//todo move corresponding fields from ModuleManagerComponentBridge to projectModel.impl module
private val WorkspaceEntityStorage.moduleMap: ExternalEntityMapping<ModuleBridge>
get() = getExternalMapping("intellij.modules.bridge")
override fun createExtension(module: ModuleBridge,
entityStorage: VersionedEntityStorage,
diff: WorkspaceEntityStorageDiffBuilder?): LanguageLevelModuleExtensionBridge {
return LanguageLevelModuleExtensionBridge(module, entityStorage, diff)
}
}
}

View File

@@ -81,6 +81,7 @@
<orderEntry type="module" module-name="intellij.platform.statistics" />
<orderEntry type="module" module-name="intellij.platform.inspect" />
<orderEntry type="library" name="fastutil-min" level="project" />
<orderEntry type="module" module-name="intellij.platform.workspaceModel.storage" />
<orderEntry type="module" module-name="intellij.platform.core.ui" />
<orderEntry type="module" module-name="intellij.platform.codeStyle.impl" />
<orderEntry type="module" module-name="intellij.platform.ide.util.io" />

View File

@@ -557,8 +557,6 @@
<bundledKeymap file="NetBeans 6.5.xml"/>
<keymapExtension implementation="com.intellij.debugger.actions.DebuggerKeymapExtension"/>
<moduleExtension implementation="com.intellij.openapi.roots.LanguageLevelModuleExtensionImpl"/>
<orderRootType implementation="com.intellij.openapi.roots.NativeLibraryOrderRootType"/>
<codeUsageScopeOptimizer implementation="com.intellij.compiler.JavaCompilerReferencesCodeUsageScopeOptimizer"/>

View File

@@ -10,11 +10,18 @@ import com.intellij.openapi.roots.ProjectExtension;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.util.ObjectUtils;
import com.intellij.workspaceModel.ide.WorkspaceModelChangeListener;
import com.intellij.workspaceModel.ide.WorkspaceModelTopics;
import com.intellij.workspaceModel.storage.EntityChange;
import com.intellij.workspaceModel.storage.VersionedStorageChange;
import com.intellij.workspaceModel.storage.bridgeEntities.JavaModuleSettingsEntity;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;
import java.util.Objects;
/**
* @author anna
*/
@@ -29,6 +36,25 @@ public class LanguageLevelProjectExtensionImpl extends LanguageLevelProjectExten
public LanguageLevelProjectExtensionImpl(final Project project) {
myProject = project;
setDefault(project.isDefault() ? true : null);
WorkspaceModelTopics.Companion.getInstance(project).subscribeAfterModuleLoading(
project.getMessageBus().connect(),
new WorkspaceModelChangeListener() {
@Override
public void beforeChanged(@NotNull VersionedStorageChange event) {
}
@Override
public void changed(@NotNull VersionedStorageChange event) {
if (event.getChanges(JavaModuleSettingsEntity.class).stream().anyMatch(change ->
change instanceof EntityChange.Replaced<?> &&
!Objects.equals(((EntityChange.Replaced<JavaModuleSettingsEntity>)change).getOldEntity().getLanguageLevelId(),
((EntityChange.Replaced<JavaModuleSettingsEntity>)change).getNewEntity().getLanguageLevelId())
)) {
languageLevelsChanged();
}
}
}
);
}
public static LanguageLevelProjectExtensionImpl getInstanceImpl(Project project) {

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="11" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/bar/bar.iml" filepath="$PROJECT_DIR$/bar/bar.iml" />
<module fileurl="file://$PROJECT_DIR$/foo/foo.iml" filepath="$PROJECT_DIR$/foo/foo.iml" />
</modules>
</component>
</project>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8" inherit-compiler-output="true">
<exclude-output />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@@ -0,0 +1,49 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.java.configurationStore
import com.intellij.openapi.application.ex.PathManagerEx
import com.intellij.openapi.application.runReadAction
import com.intellij.openapi.module.EffectiveLanguageLevelUtil
import com.intellij.openapi.module.LanguageLevelUtil
import com.intellij.openapi.module.ModuleManager
import com.intellij.openapi.roots.LanguageLevelModuleExtensionImpl
import com.intellij.pom.java.LanguageLevel
import com.intellij.testFramework.ApplicationRule
import com.intellij.testFramework.TemporaryDirectory
import com.intellij.testFramework.loadProjectAndCheckResults
import org.assertj.core.api.Assertions.assertThat
import org.junit.ClassRule
import org.junit.Rule
import org.junit.Test
import java.nio.file.Paths
class LoadJavaProjectTest {
companion object {
@JvmField
@ClassRule
val appRule = ApplicationRule()
}
@get:Rule
val tempDirectory = TemporaryDirectory()
@Test
fun `load java project with custom language level`() {
val projectPath = Paths.get(PathManagerEx.getCommunityHomePath()).resolve("java/java-tests/testData/configurationStore/java-module-with-custom-language-level")
loadProjectAndCheckResults(listOf(projectPath), tempDirectory) { project ->
val modules = ModuleManager.getInstance(project).modules.sortedBy { it.name }
assertThat(modules).hasSize(2)
val (bar, foo) = modules
assertThat(foo.name).isEqualTo("foo")
assertThat(bar.name).isEqualTo("bar")
runReadAction {
assertThat(EffectiveLanguageLevelUtil.getEffectiveLanguageLevel(bar)).isEqualTo(LanguageLevel.JDK_11)
assertThat(LanguageLevelUtil.getCustomLanguageLevel(bar)).isNull()
assertThat(LanguageLevelModuleExtensionImpl.getInstance(bar).languageLevel).isNull()
assertThat(EffectiveLanguageLevelUtil.getEffectiveLanguageLevel(foo)).isEqualTo(LanguageLevel.JDK_1_8)
assertThat(LanguageLevelUtil.getCustomLanguageLevel(foo)).isEqualTo(LanguageLevel.JDK_1_8)
assertThat(LanguageLevelModuleExtensionImpl.getInstance(foo).languageLevel).isEqualTo(LanguageLevel.JDK_1_8)
}
}
}
}

View File

@@ -23,6 +23,7 @@ import com.intellij.psi.tree.IFileElementType;
import com.intellij.psi.util.PsiUtil;
import com.intellij.testFramework.LightVirtualFile;
import com.intellij.testFramework.ParsingTestCase;
import com.intellij.workspaceModel.ide.WorkspaceModelTopics;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
@@ -39,6 +40,7 @@ public abstract class JavaParsingTestCase extends ParsingTestCase {
protected void setUp() throws Exception {
super.setUp();
myLanguageLevel = JavaTestUtil.getMaxRegisteredLanguageLevel();
getProject().registerService(WorkspaceModelTopics.class, new WorkspaceModelTopics());
getProject().registerService(LanguageLevelProjectExtension.class, new LanguageLevelProjectExtensionImpl(getProject()));
addExplicitExtension(LanguageASTFactory.INSTANCE, JavaLanguage.INSTANCE, new JavaASTFactory());
}

View File

@@ -89,6 +89,7 @@ class CompilerModuleExtensionBridge(
excludeOutput = true,
compilerOutputForTests = null,
compilerOutput = null,
languageLevelId = null,
module = moduleEntity,
source = moduleSource
)

View File

@@ -32,6 +32,7 @@ import com.intellij.workspaceModel.ide.impl.legacyBridge.module.CompilerModuleEx
import com.intellij.workspaceModel.ide.impl.legacyBridge.module.ModuleManagerComponentBridge.Companion.findModuleEntity
import com.intellij.workspaceModel.ide.legacyBridge.ModifiableRootModelBridge
import com.intellij.workspaceModel.ide.legacyBridge.ModuleBridge
import com.intellij.workspaceModel.ide.legacyBridge.ModuleExtensionBridge
import com.intellij.workspaceModel.storage.CachedValue
import com.intellij.workspaceModel.storage.WorkspaceEntityStorage
import com.intellij.workspaceModel.storage.WorkspaceEntityStorageBuilder
@@ -74,7 +75,7 @@ class ModifiableRootModelBridgeImpl(
private val virtualFileManager: VirtualFileUrlManager = VirtualFileUrlManager.getInstance(project)
private val extensionsDelegate = lazy {
RootModelBridgeImpl.loadExtensions(storage = initialStorage, module = module, writable = true,
RootModelBridgeImpl.loadExtensions(storage = entityStorageOnDiff, module = module, diff = diff, writable = true,
parentDisposable = extensionsDisposable)
.filterNot { compilerModuleExtensionClass.isAssignableFrom(it.javaClass) }
}
@@ -429,6 +430,8 @@ class ModifiableRootModelBridgeImpl(
val element = Element("component")
for (extension in extensions) {
if (extension is ModuleExtensionBridge) continue
extension.commit()
if (extension is PersistentStateComponent<*>) {

View File

@@ -4,6 +4,7 @@ package com.intellij.workspaceModel.ide.impl.legacyBridge.module.roots
import com.intellij.configurationStore.deserializeAndLoadState
import com.intellij.openapi.Disposable
import com.intellij.openapi.components.PersistentStateComponent
import com.intellij.openapi.extensions.ExtensionPointName
import com.intellij.openapi.roots.*
import com.intellij.openapi.roots.impl.ModuleOrderEnumerator
import com.intellij.openapi.roots.impl.RootModelBase
@@ -13,6 +14,8 @@ import com.intellij.openapi.util.JDOMUtil
import com.intellij.workspaceModel.ide.impl.legacyBridge.module.CompilerModuleExtensionBridge
import com.intellij.workspaceModel.ide.impl.legacyBridge.module.ModuleManagerComponentBridge.Companion.findModuleEntity
import com.intellij.workspaceModel.ide.legacyBridge.ModuleBridge
import com.intellij.workspaceModel.ide.legacyBridge.ModuleExtensionBridgeFactory
import com.intellij.workspaceModel.storage.VersionedEntityStorage
import com.intellij.workspaceModel.storage.WorkspaceEntityStorage
import com.intellij.workspaceModel.storage.WorkspaceEntityStorageDiffBuilder
import com.intellij.workspaceModel.storage.bridgeEntities.ContentRootEntity
@@ -32,7 +35,7 @@ internal class RootModelBridgeImpl(internal val moduleEntity: ModuleEntity?,
private val module: ModuleBridge = rootModel.moduleBridge
private val extensions by lazy {
loadExtensions(storage = storage, module = module, writable = false, parentDisposable = this)
loadExtensions(storage = VersionedEntityStorageOnStorage(storage), module = module, writable = false, diff = null, parentDisposable = this)
}
private val orderEntriesArray: Array<OrderEntry> by lazy {
@@ -99,6 +102,8 @@ internal class RootModelBridgeImpl(internal val moduleEntity: ModuleEntity?,
override fun orderEntries(): OrderEnumerator = ModuleOrderEnumerator(this, null)
companion object {
private val MODULE_EXTENSION_BRIDGE_FACTORY_EP = ExtensionPointName<ModuleExtensionBridgeFactory<*>>("com.intellij.workspaceModel.moduleExtensionBridgeFactory")
internal fun toOrderEntry(
item: ModuleDependencyItem,
index: Int,
@@ -116,16 +121,21 @@ internal class RootModelBridgeImpl(internal val moduleEntity: ModuleEntity?,
}
}
internal fun loadExtensions(storage: WorkspaceEntityStorage,
internal fun loadExtensions(storage: VersionedEntityStorage,
module: ModuleBridge,
writable: Boolean,
diff: WorkspaceEntityStorageDiffBuilder?,
parentDisposable: Disposable): Set<ModuleExtension> {
val result = TreeSet<ModuleExtension> { o1, o2 ->
Comparing.compare(o1.javaClass.name, o2.javaClass.name)
}
val moduleEntity = storage.findModuleEntity(module)
MODULE_EXTENSION_BRIDGE_FACTORY_EP.extensionList.mapTo(result) {
it.createExtension(module, storage, diff)
}
val moduleEntity = storage.current.findModuleEntity(module)
val rootManagerElement = moduleEntity?.customImlData?.rootManagerTagCustomData?.let { JDOMUtil.load(it) }
for (extension in ModuleRootManagerEx.MODULE_EXTENSION_NAME.getExtensions(module)) {

View File

@@ -4,6 +4,9 @@
<extensionPoint name="directoryIndexExcludePolicy" interface="com.intellij.openapi.roots.impl.DirectoryIndexExcludePolicy"
area="IDEA_PROJECT" dynamic="true"/>
<extensionPoint name="projectExtension" interface="com.intellij.openapi.roots.ProjectExtension" area="IDEA_PROJECT" dynamic="true"/>
<extensionPoint name="workspaceModel.moduleExtensionBridgeFactory"
interface="com.intellij.workspaceModel.ide.legacyBridge.ModuleExtensionBridgeFactory"
dynamic="true"/>
</extensionPoints>
<extensions defaultExtensionNs="com.intellij">
<projectService serviceInterface="com.intellij.openapi.roots.impl.DirectoryIndex"

View File

@@ -0,0 +1,8 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.workspaceModel.ide.legacyBridge
/**
* Marker interface for implementations of [com.intellij.openapi.roots.ModuleExtension] which are created via [ModuleExtensionBridgeFactory].
*/
interface ModuleExtensionBridge {
}

View File

@@ -0,0 +1,14 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.workspaceModel.ide.legacyBridge
import com.intellij.openapi.roots.ModuleExtension
import com.intellij.workspaceModel.storage.VersionedEntityStorage
import com.intellij.workspaceModel.storage.WorkspaceEntityStorageDiffBuilder
/**
* Register implementation of this interface as `com.intellij.workspaceModel.moduleExtensionBridgeFactory` extension to provide implementations
* of [ModuleExtension] based on data from workspace model.
*/
interface ModuleExtensionBridgeFactory<T> where T : ModuleExtension, T : ModuleExtensionBridge {
fun createExtension(module: ModuleBridge, entityStorage: VersionedEntityStorage, diff: WorkspaceEntityStorageDiffBuilder?): T
}

View File

@@ -307,6 +307,7 @@ internal open class ModuleImlFileEntitiesSerializer(internal val modulePath: Mod
}
val inheritedCompilerOutput = rootManagerElement.getAttributeAndDetach(INHERIT_COMPILER_OUTPUT_ATTRIBUTE)
val languageLevel = rootManagerElement.getAttributeAndDetach(MODULE_LANGUAGE_LEVEL_ATTRIBUTE)
val excludeOutput = rootManagerElement.getChildAndDetach(EXCLUDE_OUTPUT_TAG) != null
val compilerOutput = rootManagerElement.getChildAndDetach(OUTPUT_TAG)?.getAttributeValue(URL_ATTRIBUTE)
val compilerOutputForTests = rootManagerElement.getChildAndDetach(TEST_OUTPUT_TAG)?.getAttributeValue(URL_ATTRIBUTE)
@@ -316,6 +317,7 @@ internal open class ModuleImlFileEntitiesSerializer(internal val modulePath: Mod
excludeOutput = excludeOutput,
compilerOutput = compilerOutput?.let { virtualFileManager.fromUrl(it) },
compilerOutputForTests = compilerOutputForTests?.let { virtualFileManager.fromUrl(it) },
languageLevelId = languageLevel,
module = moduleEntity,
source = entitySource
)
@@ -490,6 +492,9 @@ internal open class ModuleImlFileEntitiesSerializer(internal val modulePath: Mod
if (javaSettings.excludeOutput) {
rootManagerElement.addContent(Element(EXCLUDE_OUTPUT_TAG))
}
javaSettings.languageLevelId?.let {
rootManagerElement.setAttribute(MODULE_LANGUAGE_LEVEL_ATTRIBUTE, it)
}
}
private fun saveDependencyItem(dependencyItem: ModuleDependencyItem, moduleLibraries: Map<String, LibraryEntity>)

View File

@@ -46,7 +46,7 @@ class ImlSerializationTest {
checkSerializationSize(bytes, expectedSize, 2_000)
assertTrue("This assertion is a reminder. Have you updated the serializer? Update the serializer version!",
26_000 == expectedSize && "v19" == EntityStorageSerializerImpl.SERIALIZER_VERSION)
26_000 == expectedSize && "v20" == EntityStorageSerializerImpl.SERIALIZER_VERSION)
}
@Test

View File

@@ -98,7 +98,7 @@ class JpsProjectEntitiesLoaderTest : HeavyPlatformTestCase() {
val utilModule = modules[1]
assertEquals("util", utilModule.name)
assertEquals("""<component LANGUAGE_LEVEL="JDK_1_7">
assertEquals("""<component>
<annotation-paths>
<root url="$projectUrl/lib/anno" />
</annotation-paths>
@@ -109,6 +109,7 @@ class JpsProjectEntitiesLoaderTest : HeavyPlatformTestCase() {
val utilJavaSettings = utilModule.javaSettings!!
assertEquals(false, utilJavaSettings.inheritedCompilerOutput)
assertEquals(true, utilJavaSettings.excludeOutput)
assertEquals("JDK_1_7", utilJavaSettings.languageLevelId)
assertEquals("$projectUrl/out/production-util", utilJavaSettings.compilerOutput?.url)
assertEquals("$projectUrl/out/test-util", utilJavaSettings.compilerOutputForTests?.url)
val utilContentRoot = assertOneElement(utilModule.contentRoots.toList())

View File

@@ -4,10 +4,10 @@ import com.intellij.openapi.application.ex.PathManagerEx
import com.intellij.openapi.util.io.FileUtil
import com.intellij.testFramework.HeavyPlatformTestCase
import com.intellij.workspaceModel.ide.getInstance
import com.intellij.workspaceModel.storage.url.VirtualFileUrlManager
import com.intellij.workspaceModel.storage.WorkspaceEntityStorageBuilder
import com.intellij.workspaceModel.storage.bridgeEntities.ModuleEntity
import com.intellij.workspaceModel.storage.bridgeEntities.projectLibraries
import com.intellij.workspaceModel.storage.url.VirtualFileUrlManager
import org.jetbrains.jps.util.JpsPathUtil
import java.io.File
@@ -27,7 +27,7 @@ class JpsProjectReloadingTest : HeavyPlatformTestCase() {
assertEquals("util", utilModule.name)
val utilModuleSrc = assertOneElement(utilModule.sourceRoots.toList())
assertEquals("$projectDirUrl/util/src2", utilModuleSrc.url.url)
assertEquals("""<component LANGUAGE_LEVEL="JDK_1_7">
assertEquals("""<component>
<annotation-paths>
<root url="$projectDirUrl/lib/anno2" />
</annotation-paths>

View File

@@ -39,7 +39,7 @@ class JpsProjectSaveAfterChangesTest {
url = configLocation.baseDirectoryUrl.append("util/src2")
}
builder.modifyEntity(ModifiableModuleCustomImlDataEntity::class.java, utilModule.customImlData!!) {
rootManagerTagCustomData = """<component LANGUAGE_LEVEL="JDK_1_7">
rootManagerTagCustomData = """<component>
<annotation-paths>
<root url="${configLocation.baseDirectoryUrlString}/lib/anno2" />
</annotation-paths>
@@ -96,7 +96,7 @@ class JpsProjectSaveAfterChangesTest {
val sourceRootEntity = builder.addSourceRootEntity(contentRootEntity, configLocation.baseDirectoryUrl.append("new"),
false, "java-source", source)
builder.addJavaSourceRootEntity(sourceRootEntity, false, "")
builder.addJavaModuleSettingsEntity(true, true, null, null, module, source)
builder.addJavaModuleSettingsEntity(true, true, null, null, null, module, source)
}
}

View File

@@ -147,9 +147,10 @@ class JavaModuleSettingsEntityData : WorkspaceEntityData<JavaModuleSettingsEntit
var excludeOutput: Boolean = false
var compilerOutput: VirtualFileUrl? = null
var compilerOutputForTests: VirtualFileUrl? = null
var languageLevelId: String? = null
override fun createEntity(snapshot: WorkspaceEntityStorage): JavaModuleSettingsEntity {
return JavaModuleSettingsEntity(inheritedCompilerOutput, excludeOutput, compilerOutput, compilerOutputForTests)
return JavaModuleSettingsEntity(inheritedCompilerOutput, excludeOutput, compilerOutput, compilerOutputForTests, languageLevelId)
.also { addMetaData(it, snapshot) }
}
}
@@ -158,7 +159,8 @@ class JavaModuleSettingsEntity(
val inheritedCompilerOutput: Boolean,
val excludeOutput: Boolean,
val compilerOutput: VirtualFileUrl?,
val compilerOutputForTests: VirtualFileUrl?
val compilerOutputForTests: VirtualFileUrl?,
val languageLevelId: String?
) : WorkspaceEntityBase() {
val module: ModuleEntity by moduleDelegate

View File

@@ -40,6 +40,7 @@ class ModifiableJavaModuleSettingsEntity : ModifiableWorkspaceEntityBase<JavaMod
var excludeOutput: Boolean by EntityDataDelegation()
var compilerOutput: VirtualFileUrl? by VirtualFileUrlNullableProperty()
var compilerOutputForTests: VirtualFileUrl? by VirtualFileUrlNullableProperty()
var languageLevelId: String? by EntityDataDelegation()
var module: ModuleEntity by MutableOneToOneChild.NotNull(JavaModuleSettingsEntity::class.java, ModuleEntity::class.java, true)
}
@@ -48,6 +49,7 @@ fun WorkspaceEntityStorageDiffBuilder.addJavaModuleSettingsEntity(inheritedCompi
excludeOutput: Boolean,
compilerOutput: VirtualFileUrl?,
compilerOutputForTests: VirtualFileUrl?,
languageLevelId: String?,
module: ModuleEntity,
source: EntitySource) = addEntity(
ModifiableJavaModuleSettingsEntity::class.java, source) {
@@ -55,6 +57,7 @@ fun WorkspaceEntityStorageDiffBuilder.addJavaModuleSettingsEntity(inheritedCompi
this.excludeOutput = excludeOutput
this.compilerOutput = compilerOutput
this.compilerOutputForTests = compilerOutputForTests
this.languageLevelId = languageLevelId
this.module = module
}

View File

@@ -43,7 +43,7 @@ class EntityStorageSerializerImpl(
private val versionsContributor: () -> Map<String, String> = { emptyMap() },
) : EntityStorageSerializer {
companion object {
const val SERIALIZER_VERSION = "v19"
const val SERIALIZER_VERSION = "v20"
}
private val KRYO_BUFFER_SIZE = 64 * 1024

View File

@@ -84,7 +84,7 @@ class EclipseModuleRootsSerializer : CustomModuleRootsSerializer, StorageManager
virtualFileManager)
}
else {
builder.addJavaModuleSettingsEntity(false, true, storageRootUrl.append("bin"), null, moduleEntity, entitySource)
builder.addJavaModuleSettingsEntity(false, true, storageRootUrl.append("bin"), null, null, moduleEntity, entitySource)
}
val emlUrl = getEmlFileUrl(imlFileUrl)
@@ -101,7 +101,7 @@ class EclipseModuleRootsSerializer : CustomModuleRootsSerializer, StorageManager
}
}
else {
builder.addJavaModuleSettingsEntity(true, false, null, null, moduleEntity, entitySource)
builder.addJavaModuleSettingsEntity(true, false, null, null, null, moduleEntity, entitySource)
}
}
}
@@ -199,7 +199,7 @@ class EclipseModuleRootsSerializer : CustomModuleRootsSerializer, StorageManager
outputUrl = getUrlByRelativePath(path)
}
builder.addJavaModuleSettingsEntity(false, true, outputUrl, null,
moduleEntity, contentRootEntity.entitySource)
null, moduleEntity, contentRootEntity.entitySource)
}
EclipseXml.LIB_KIND -> {
val linked = expandLinkedResourcesPath(storageRootPath, expandMacroMap, path)

View File

@@ -3,7 +3,6 @@ package org.jetbrains.idea.eclipse.config
import com.intellij.openapi.components.ExpandMacroToPathMap
import com.intellij.openapi.roots.OrderRootType
import com.intellij.openapi.util.JDOMUtil
import com.intellij.openapi.util.SystemInfo
import com.intellij.openapi.util.io.FileUtil
import com.intellij.workspaceModel.ide.toPath
@@ -28,8 +27,7 @@ internal class EmlFileLoader(
private val virtualFileManager: VirtualFileUrlManager
) {
fun loadEml(emlTag: Element, contentRoot: ContentRootEntity) {
loadLanguageLevel(emlTag)
loadCompilerSettings(emlTag)
loadCustomJavaSettings(emlTag)
loadContentEntries(emlTag, contentRoot)
loadJdkSettings(emlTag)
@@ -143,8 +141,8 @@ internal class EmlFileLoader(
}
}
private fun loadCompilerSettings(emlTag: Element) {
val javaSettings = module.javaSettings ?: builder.addJavaModuleSettingsEntity(true, true, null, null, module, module.entitySource)
private fun loadCustomJavaSettings(emlTag: Element) {
val javaSettings = module.javaSettings ?: builder.addJavaModuleSettingsEntity(true, true, null, null, null, module, module.entitySource)
builder.modifyEntity(ModifiableJavaModuleSettingsEntity::class.java, javaSettings) {
val testOutputElement = emlTag.getChild(IdeaXml.OUTPUT_TEST_TAG)
if (testOutputElement != null) {
@@ -157,22 +155,8 @@ internal class EmlFileLoader(
}
excludeOutput = emlTag.getChild(IdeaXml.EXCLUDE_OUTPUT_TAG) != null
}
}
private fun loadLanguageLevel(emlTag: Element) {
val languageLevelAttribute = emlTag.getAttributeValue("LANGUAGE_LEVEL")
if (languageLevelAttribute != null) {
val languageLevelTag = JDOMUtil.write(Element("component").setAttribute("LANGUAGE_LEVEL", languageLevelAttribute))
val customImlData = module.customImlData
if (customImlData != null) {
builder.modifyEntity(ModifiableModuleCustomImlDataEntity::class.java, customImlData) {
rootManagerTagCustomData = languageLevelTag
}
}
else {
builder.addModuleCustomImlDataEntity(languageLevelTag, emptyMap(), module, module.entitySource)
}
languageLevelId = emlTag.getAttributeValue("LANGUAGE_LEVEL")
}
}

View File

@@ -157,11 +157,7 @@ internal class EmlFileSaver(private val module: ModuleEntity,
if (javaSettings.excludeOutput) {
root.addContent(Element(EXCLUDE_OUTPUT_TAG))
}
}
module.customImlData?.rootManagerTagCustomData?.let { languageLevelTagString ->
val tag = JDOMUtil.load(languageLevelTagString)
tag.getAttributeValue("LANGUAGE_LEVEL")?.let {
javaSettings.languageLevelId?.let {
root.setAttribute("LANGUAGE_LEVEL", it)
}
}