mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-08 15:09:39 +07:00
IDEA-166461 Cannot revert module language level to 1.8
Introduce StoredProperty and BaseState as approach to simplfy creating trackable (modified, differs from default) state classes.
This commit is contained in:
@@ -10,6 +10,6 @@
|
||||
<orderEntry type="module" module-name="analysis-api" />
|
||||
<orderEntry type="module" module-name="java-psi-api" />
|
||||
<orderEntry type="module" module-name="projectModel-api" />
|
||||
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
|
||||
</module>
|
||||
@@ -20,24 +20,29 @@
|
||||
*/
|
||||
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.jdom.Element;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class LanguageLevelModuleExtensionImpl extends ModuleExtension implements LanguageLevelModuleExtension {
|
||||
public class LanguageLevelModuleExtensionImpl extends ModuleExtension implements LanguageLevelModuleExtension,
|
||||
PersistentStateComponentWithModificationTracker<LanguageLevelState> {
|
||||
private static final Logger LOG = Logger.getInstance(LanguageLevelModuleExtensionImpl.class);
|
||||
|
||||
@NonNls private static final String LANGUAGE_LEVEL_ELEMENT_NAME = "LANGUAGE_LEVEL";
|
||||
private Module myModule;
|
||||
private final boolean myWritable;
|
||||
|
||||
private LanguageLevel myLanguageLevel;
|
||||
private final LanguageLevelModuleExtensionImpl mySource;
|
||||
|
||||
private LanguageLevelState myState = new LanguageLevelState();
|
||||
|
||||
@Override
|
||||
public long getStateModificationCount() {
|
||||
return myState.getModificationCount();
|
||||
}
|
||||
|
||||
public static LanguageLevelModuleExtensionImpl getInstance(final Module module) {
|
||||
return ModuleRootManager.getInstance(module).getModuleExtension(LanguageLevelModuleExtensionImpl.class);
|
||||
}
|
||||
@@ -51,37 +56,31 @@ public class LanguageLevelModuleExtensionImpl extends ModuleExtension implements
|
||||
public LanguageLevelModuleExtensionImpl(final LanguageLevelModuleExtensionImpl source, boolean writable) {
|
||||
myWritable = writable;
|
||||
myModule = source.myModule;
|
||||
myLanguageLevel = source.myLanguageLevel;
|
||||
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) {
|
||||
LOG.assertTrue(myWritable, "Writable model can be retrieved from writable ModifiableRootModel");
|
||||
myLanguageLevel = languageLevel;
|
||||
myState.setLanguageLevel(languageLevel);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public LanguageLevelState getState() {
|
||||
return myState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readExternal(@NotNull Element element) {
|
||||
final String languageLevel = element.getAttributeValue(LANGUAGE_LEVEL_ELEMENT_NAME);
|
||||
if (languageLevel != null) {
|
||||
try {
|
||||
myLanguageLevel = LanguageLevel.valueOf(languageLevel);
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
//bad value was stored
|
||||
}
|
||||
}
|
||||
else {
|
||||
myLanguageLevel = null;
|
||||
}
|
||||
public void loadState(LanguageLevelState state) {
|
||||
myState = state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeExternal(final Element element) {
|
||||
if (myLanguageLevel != null) {
|
||||
element.setAttribute(LANGUAGE_LEVEL_ELEMENT_NAME, myLanguageLevel.toString());
|
||||
}
|
||||
public int compareTo(@NotNull Object o) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -91,26 +90,26 @@ public class LanguageLevelModuleExtensionImpl extends ModuleExtension implements
|
||||
|
||||
@Override
|
||||
public void commit() {
|
||||
if (mySource != null && mySource.myLanguageLevel != myLanguageLevel) {
|
||||
mySource.myLanguageLevel = myLanguageLevel;
|
||||
if (isChanged()) {
|
||||
mySource.myState.setLanguageLevel(myState.getLanguageLevel());
|
||||
LanguageLevelProjectExtension.getInstance(myModule.getProject()).languageLevelsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChanged() {
|
||||
return mySource != null && mySource.myLanguageLevel != myLanguageLevel;
|
||||
return mySource != null && !mySource.myState.equals(myState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
myModule = null;
|
||||
myLanguageLevel = null;
|
||||
myState = null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public LanguageLevel getLanguageLevel() {
|
||||
return myLanguageLevel;
|
||||
return myState.getLanguageLevel();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import com.intellij.openapi.components.BaseState
|
||||
import com.intellij.pom.java.LanguageLevel
|
||||
import com.intellij.util.xmlb.annotations.Attribute
|
||||
|
||||
class LanguageLevelState : BaseState() {
|
||||
@get:Attribute("LANGUAGE_LEVEL")
|
||||
var languageLevel: LanguageLevel? by storedProperty()
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.intellij.configurationStore
|
||||
|
||||
import com.intellij.openapi.components.BaseState
|
||||
import com.intellij.testFramework.assertions.Assertions.assertThat
|
||||
import com.intellij.util.loadElement
|
||||
import com.intellij.util.xmlb.XmlSerializer
|
||||
import com.intellij.util.xmlb.annotations.Attribute
|
||||
import org.junit.Test
|
||||
|
||||
private class AState : BaseState() {
|
||||
@get:Attribute("customName")
|
||||
var languageLevel: String? by storedProperty()
|
||||
|
||||
var property2 by storedProperty(0)
|
||||
}
|
||||
|
||||
class StoredPropertyStateTest {
|
||||
@Test
|
||||
fun test() {
|
||||
val state = AState()
|
||||
|
||||
assertThat(state).isEqualTo(AState())
|
||||
|
||||
assertThat(state.modificationCount).isEqualTo(0)
|
||||
assertThat(state.languageLevel).isNull()
|
||||
state.languageLevel = "foo"
|
||||
assertThat(state.modificationCount).isEqualTo(1)
|
||||
assertThat(state.languageLevel).isEqualTo("foo")
|
||||
assertThat(state.modificationCount).isEqualTo(1)
|
||||
|
||||
assertThat(state).isNotEqualTo(AState())
|
||||
|
||||
assertThat(XmlSerializer.serialize(state)).isEqualTo("""<AState customName="foo" />""")
|
||||
assertThat(XmlSerializer.deserialize(loadElement("""<AState customName="foo" />"""), AState::class.java)!!.languageLevel).isEqualTo("foo")
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
@@ -78,6 +78,6 @@ public class ModuleRootManagerComponent extends ModuleRootManagerImpl implements
|
||||
}
|
||||
return true;
|
||||
});
|
||||
return result[0];
|
||||
return result[0] + myRootModel.getStateModificationCount();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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.components
|
||||
|
||||
import com.intellij.openapi.util.SimpleModificationTracker
|
||||
import com.intellij.util.SmartList
|
||||
import com.intellij.util.xmlb.Accessor
|
||||
import com.intellij.util.xmlb.SerializationFilter
|
||||
import kotlin.properties.ReadWriteProperty
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
abstract class BaseState : SimpleModificationTracker(), SerializationFilter {
|
||||
// if property value differs from default
|
||||
private val properties: MutableList<StoredProperty<*>> = SmartList()
|
||||
|
||||
override fun accepts(accessor: Accessor, bean: Any): Boolean {
|
||||
for (property in properties) {
|
||||
if (property.name == accessor.name) {
|
||||
return property.value != property.defaultValue
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun <T : Any> storedProperty(defaultValue: T? = null): ReadWriteProperty<BaseState, T?> {
|
||||
val result = StoredProperty(defaultValue)
|
||||
properties.add(result)
|
||||
return result
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is BaseState) return false
|
||||
|
||||
return properties == other.properties
|
||||
}
|
||||
|
||||
override fun hashCode() = properties.hashCode()
|
||||
}
|
||||
|
||||
internal class StoredProperty<T>(internal val defaultValue: T?) : ReadWriteProperty<BaseState, T?> {
|
||||
internal var value = defaultValue
|
||||
internal var name: String? = null
|
||||
|
||||
override operator fun getValue(thisRef: BaseState, property: KProperty<*>) = value
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun setValue(thisRef: BaseState, property: KProperty<*>, @Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE") newValue: T?) {
|
||||
if (value != newValue) {
|
||||
thisRef.incModificationCount()
|
||||
|
||||
name = property.name
|
||||
value = newValue
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is StoredProperty<*>) return false
|
||||
|
||||
if (value != other.value) return false
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode() = value?.hashCode() ?: 0
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2009 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.
|
||||
@@ -22,10 +22,13 @@ package com.intellij.openapi.roots;
|
||||
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||
import com.intellij.openapi.util.JDOMExternalizable;
|
||||
import org.jdom.Element;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public abstract class ModuleExtension<T extends ModuleExtension> implements JDOMExternalizable, Disposable, Comparable<ModuleExtension> {
|
||||
/**
|
||||
* Implement {@link com.intellij.openapi.components.PersistentStateComponent} to be serializable.
|
||||
*/
|
||||
public abstract class ModuleExtension<T extends ModuleExtension> implements Disposable, Comparable<ModuleExtension> {
|
||||
public static final ExtensionPointName<ModuleExtension> EP_NAME = ExtensionPointName.create("com.intellij.moduleExtension");
|
||||
|
||||
/**
|
||||
@@ -58,4 +61,20 @@ public abstract class ModuleExtension<T extends ModuleExtension> implements JDOM
|
||||
public int compareTo(@NotNull final ModuleExtension o) {
|
||||
return getClass().getName().compareTo(o.getClass().getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Please implement PersistentStateComponent instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public void readExternal(@NotNull Element element) {
|
||||
throw new Error("Please implement PersistentStateComponent");
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Please implement PersistentStateComponent instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public void writeExternal(@NotNull Element element) {
|
||||
throw new Error("Please implement PersistentStateComponent");
|
||||
}
|
||||
}
|
||||
@@ -51,7 +51,7 @@ public class ModuleRootManagerImpl extends ModuleRootManager implements Disposab
|
||||
private final Module myModule;
|
||||
private final ProjectRootManagerImpl myProjectRootManager;
|
||||
private final VirtualFilePointerManager myFilePointerManager;
|
||||
private RootModelImpl myRootModel;
|
||||
protected RootModelImpl myRootModel;
|
||||
private boolean myIsDisposed = false;
|
||||
private boolean myLoaded = false;
|
||||
private final OrderRootsCache myOrderRootsCache;
|
||||
|
||||
@@ -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.
|
||||
@@ -17,6 +17,9 @@ package com.intellij.openapi.roots.impl;
|
||||
|
||||
import com.intellij.openapi.CompositeDisposable;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.components.ComponentSerializationUtil;
|
||||
import com.intellij.openapi.components.PersistentStateComponent;
|
||||
import com.intellij.openapi.components.PersistentStateComponentWithModificationTracker;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.extensions.Extensions;
|
||||
import com.intellij.openapi.module.Module;
|
||||
@@ -34,6 +37,7 @@ import com.intellij.openapi.vfs.pointers.VirtualFilePointerManager;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.xmlb.XmlSerializer;
|
||||
import org.jdom.Element;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -122,7 +126,15 @@ public class RootModelImpl extends RootModelBase implements ModifiableRootModel
|
||||
RootModelImpl originalRootModel = moduleRootManager.getRootModel();
|
||||
for (ModuleExtension extension : originalRootModel.myExtensions) {
|
||||
ModuleExtension model = extension.getModifiableModel(false);
|
||||
model.readExternal(element);
|
||||
|
||||
if (model instanceof PersistentStateComponent) {
|
||||
PersistentStateComponent<?> component = (PersistentStateComponent)model;
|
||||
component.loadState(XmlSerializer.deserialize(element, ComponentSerializationUtil.getStateClass((component.getClass()))));
|
||||
}
|
||||
else {
|
||||
model.readExternal(element);
|
||||
}
|
||||
|
||||
registerOnDispose(model);
|
||||
myExtensions.add(model);
|
||||
}
|
||||
@@ -407,9 +419,24 @@ public class RootModelImpl extends RootModelBase implements ModifiableRootModel
|
||||
return e;
|
||||
}
|
||||
|
||||
public long getStateModificationCount() {
|
||||
long result = 0;
|
||||
for (ModuleExtension extension : myExtensions) {
|
||||
if (extension instanceof PersistentStateComponentWithModificationTracker) {
|
||||
result += ((PersistentStateComponentWithModificationTracker)extension).getStateModificationCount();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public void writeExternal(@NotNull Element element) {
|
||||
for (ModuleExtension extension : myExtensions) {
|
||||
extension.writeExternal(element);
|
||||
if (extension instanceof PersistentStateComponent) {
|
||||
XmlSerializer.serializeInto(((PersistentStateComponent)extension).getState(), element);
|
||||
}
|
||||
else {
|
||||
extension.writeExternal(element);
|
||||
}
|
||||
}
|
||||
|
||||
for (ContentEntry contentEntry : getContent()) {
|
||||
|
||||
@@ -89,6 +89,10 @@ class BeanBinding extends Binding {
|
||||
for (Binding binding : myBindings) {
|
||||
Accessor accessor = binding.getAccessor();
|
||||
|
||||
if (o instanceof SerializationFilter && !((SerializationFilter)o).accepts(accessor, o)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (filter instanceof SkipDefaultsSerializationFilter) {
|
||||
if (((SkipDefaultsSerializationFilter)filter).equal(binding, o)) {
|
||||
continue;
|
||||
|
||||
Reference in New Issue
Block a user