do not save default project profile
authorVladimir Krivosheev <vladimir.krivosheev@jetbrains.com>
Fri, 22 Jul 2016 10:17:09 +0000 (12:17 +0200)
committerVladimir Krivosheev <vladimir.krivosheev@jetbrains.com>
Fri, 22 Jul 2016 10:17:09 +0000 (12:17 +0200)
16 files changed:
java/java-tests/testSrc/com/intellij/codeInspection/ex/ProjectInspectionManagerTest.kt
platform/analysis-impl/src/com/intellij/profile/codeInspection/BaseInspectionProfileManager.kt
platform/analysis-impl/src/com/intellij/profile/codeInspection/ProjectInspectionProfileManager.kt
platform/configuration-store-impl/src/SchemeManagerImpl.kt
platform/platform-impl/src/com/intellij/openapi/application/impl/LaterInvocator.java
platform/projectModel-impl/src/com/intellij/configurationStore/BinaryXmlOutputter.kt [moved from platform/configuration-store-impl/src/BinaryXmlOutputter.kt with 80% similarity]
platform/projectModel-impl/src/com/intellij/configurationStore/ExternalizableSchemeAdapter.kt [moved from platform/projectModel-impl/src/com/intellij/openapi/ExternalizableSchemeAdapter.kt with 100% similarity]
platform/projectModel-impl/src/com/intellij/configurationStore/scheme-impl.kt [moved from platform/projectModel-impl/src/com/intellij/openapi/scheme-impl.kt with 73% similarity]
platform/testFramework/src/com/intellij/testFramework/FixtureRule.kt
platform/testFramework/testSrc/assertJEx.kt
platform/testFramework/testSrc/com/intellij/testFramework/Assertions.java
platform/util/src/com/intellij/util/ExceptionUtil.java
platform/util/src/com/intellij/util/jdom.kt [moved from platform/platform-impl/src/com/intellij/util/jdom.kt with 100% similarity]
platform/util/src/com/intellij/util/lang/CompoundRuntimeException.java
platform/util/testSrc/util-tests.iml
platform/util/util.iml

index 1b150cf11b136bc04da77557caabe325c6a02e70..bc0bc995b292cbaca54b6026f6edbc53e8a69a5f 100644 (file)
@@ -24,10 +24,10 @@ import com.intellij.openapi.components.stateStore
 import com.intellij.openapi.project.ProjectManager
 import com.intellij.profile.codeInspection.ProjectInspectionProfileManager
 import com.intellij.testFramework.Assertions.assertThat
+import com.intellij.testFramework.InitInspectionRule
 import com.intellij.testFramework.ProjectRule
 import com.intellij.testFramework.RuleChain
 import com.intellij.testFramework.TemporaryDirectory
-import com.intellij.testFramework.runInInitMode
 import com.intellij.util.delete
 import com.intellij.util.readText
 import com.intellij.util.write
@@ -45,8 +45,9 @@ internal class ProjectInspectionManagerTest {
 
   val tempDirManager = TemporaryDirectory()
 
-  private val ruleChain = RuleChain(tempDirManager)
-  @Rule fun getChain() = ruleChain
+  @Rule
+  @JvmField
+  val ruleChain = RuleChain(tempDirManager, InitInspectionRule())
 
   @Test fun `component`() {
     loadAndUseProject(tempDirManager, {
@@ -98,31 +99,49 @@ internal class ProjectInspectionManagerTest {
     }
   }
 
+  @Test fun `do not save default project profile`() {
+    loadAndUseProject(tempDirManager, {
+      it.path
+    }) { project ->
+      val inspectionDir = Paths.get(project.stateStore.stateStorageManager.expandMacros(PROJECT_CONFIG_DIR), "inspectionProfiles")
+      val profileFile = inspectionDir.resolve("Project_Default.xml")
+      assertThat(profileFile).doesNotExist()
+
+      val projectInspectionProfileManager = ProjectInspectionProfileManager.getInstanceImpl(project)
+      assertThat(projectInspectionProfileManager.state).isEmpty()
+
+      projectInspectionProfileManager.currentProfile
+
+      assertThat(projectInspectionProfileManager.state).isEmpty()
+
+      project.saveStore()
+
+      assertThat(profileFile).doesNotExist()
+    }
+  }
+
   @Test fun `profiles`() {
-    runInInitMode {
-      loadAndUseProject(tempDirManager, {
-        it.path
-      }) { project ->
-        val projectInspectionProfileManager = ProjectInspectionProfileManager.getInstanceImpl(project)
-        projectInspectionProfileManager.forceLoadSchemes()
-
-        assertThat(projectInspectionProfileManager.state).isEmpty()
-
-        // cause to use app profile
-        runInInitMode {
-          val currentProfile = projectInspectionProfileManager.currentProfile
-          assertThat(currentProfile.isProjectLevel).isTrue()
-          currentProfile.disableTool("Convert2Diamond", project)
-        }
-
-        project.saveStore()
-
-        val inspectionDir = Paths.get(project.stateStore.stateStorageManager.expandMacros(PROJECT_CONFIG_DIR), "inspectionProfiles")
-        val file = inspectionDir.resolve("profiles_settings.xml")
-
-        assertThat(file).doesNotExist()
-        val profileFile = inspectionDir.resolve("Project_Default.xml")
-        assertThat(profileFile.readText()).isEqualTo("""
+    loadAndUseProject(tempDirManager, {
+      it.path
+    }) { project ->
+      val projectInspectionProfileManager = ProjectInspectionProfileManager.getInstanceImpl(project)
+      projectInspectionProfileManager.forceLoadSchemes()
+
+      assertThat(projectInspectionProfileManager.state).isEmpty()
+
+      // cause to use app profile
+      val currentProfile = projectInspectionProfileManager.currentProfile
+      assertThat(currentProfile.isProjectLevel).isTrue()
+      currentProfile.disableTool("Convert2Diamond", project)
+
+      project.saveStore()
+
+      val inspectionDir = Paths.get(project.stateStore.stateStorageManager.expandMacros(PROJECT_CONFIG_DIR), "inspectionProfiles")
+      val file = inspectionDir.resolve("profiles_settings.xml")
+
+      assertThat(file).doesNotExist()
+      val profileFile = inspectionDir.resolve("Project_Default.xml")
+      assertThat(profileFile.readText()).isEqualTo("""
       <component name="InspectionProjectProfileManager">
         <profile version="1.0">
           <option name="myName" value="Project Default" />
@@ -130,7 +149,7 @@ internal class ProjectInspectionManagerTest {
         </profile>
       </component>""".trimIndent())
 
-        profileFile.write("""
+      profileFile.write("""
       <component name="InspectionProjectProfileManager">
         <profile version="1.0">
           <option name="myName" value="Project Default" />
@@ -138,12 +157,9 @@ internal class ProjectInspectionManagerTest {
         </profile>
       </component>""".trimIndent())
 
-        project.baseDir.refresh(false, true)
-        (ProjectManager.getInstance() as StoreAwareProjectManager).flushChangedAlarm()
-        runInInitMode {
-          assertThat(projectInspectionProfileManager.currentProfile.getToolDefaultState("Convert2Diamond", project).level).isEqualTo(HighlightDisplayLevel.ERROR)
-        }
-      }
+      project.baseDir.refresh(false, true)
+      (ProjectManager.getInstance() as StoreAwareProjectManager).flushChangedAlarm()
+      assertThat(projectInspectionProfileManager.currentProfile.getToolDefaultState("Convert2Diamond", project).level).isEqualTo(HighlightDisplayLevel.ERROR)
     }
   }
 }
\ No newline at end of file
index 0e9d218b2820500fa070257662a287754a164662..e3e0f9201f3c572b926b30a4d6db2852eb45145f 100644 (file)
@@ -102,12 +102,7 @@ abstract class BaseInspectionProfileManager(messageBus: MessageBus) :  Inspectio
 }
 
 abstract class InspectionProfileProcessor : LazySchemeProcessor<InspectionProfileImpl, InspectionProfileImpl>() {
-  override final fun getState(scheme: InspectionProfileImpl): SchemeState {
-    if (scheme is InspectionProfileImpl) {
-      return if (scheme.wasInitialized()) SchemeState.POSSIBLY_CHANGED else SchemeState.UNCHANGED
-    }
-    else {
-      return SchemeState.NON_PERSISTENT
-    }
+  override fun getState(scheme: InspectionProfileImpl): SchemeState {
+    return if (scheme.wasInitialized()) SchemeState.POSSIBLY_CHANGED else SchemeState.UNCHANGED
   }
 }
\ No newline at end of file
index 2dc7293e754333fcad69ad4f1a9aae35b381cf4d..bc3ac0a6481d35a250dfe68ba09079a620ba911f 100644 (file)
@@ -20,6 +20,7 @@ import com.intellij.codeInspection.ex.InspectionProfileImpl
 import com.intellij.codeInspection.ex.InspectionToolRegistrar
 import com.intellij.concurrency.runAsync
 import com.intellij.configurationStore.SchemeDataHolder
+import com.intellij.configurationStore.digest
 import com.intellij.openapi.Disposable
 import com.intellij.openapi.application.ApplicationManager
 import com.intellij.openapi.components.PersistentStateComponent
@@ -38,12 +39,14 @@ import com.intellij.packageDependencies.DependencyValidationManager
 import com.intellij.profile.Profile
 import com.intellij.psi.search.scope.packageSet.NamedScopeManager
 import com.intellij.psi.search.scope.packageSet.NamedScopesHolder
+import com.intellij.util.loadElement
 import com.intellij.util.ui.UIUtil
 import com.intellij.util.xmlb.Accessor
 import com.intellij.util.xmlb.SkipDefaultValuesSerializationFilters
 import com.intellij.util.xmlb.XmlSerializer
 import org.jdom.Element
 import org.jetbrains.annotations.TestOnly
+import java.util.*
 import java.util.concurrent.CompletableFuture
 import java.util.function.Function
 
@@ -54,6 +57,12 @@ private const val SCOPE = "scope"
 private const val NAME = "name"
 private const val PROJECT_DEFAULT_PROFILE_NAME = "Project Default"
 
+private val defaultSchemeDigest = loadElement("""<component name="InspectionProjectProfileManager">
+  <profile version="1.0">
+    <option name="myName" value="Project Default" />
+  </profile>
+</component>""").digest()
+
 @State(name = "InspectionProjectProfileManager", storages = arrayOf(Storage(value = "inspectionProfiles/profiles_settings.xml", exclusive = true)))
 class ProjectInspectionProfileManager(val project: Project,
                                       private val applicationProfileManager: InspectionProfileManager,
@@ -97,6 +106,10 @@ class ProjectInspectionProfileManager(val project: Project,
 
       override fun isSchemeFile(name: CharSequence) = !StringUtil.equals(name, "profiles_settings.xml")
 
+      override fun isSchemeDefault(scheme: InspectionProfileImpl, digest: ByteArray): Boolean {
+        return scheme.name == PROJECT_DEFAULT_PROFILE_NAME && Arrays.equals(digest, defaultSchemeDigest)
+      }
+
       override fun onSchemeDeleted(scheme: InspectionProfileImpl) {
         schemeRemoved(scheme)
       }
index dabc17e4ff10cb10ed775e0fb6f6fd80c5c3132c..70078e81602a1f3d1947fd5ffb9c6395ccebc00b 100644 (file)
@@ -51,9 +51,7 @@ import org.xmlpull.v1.XmlPullParser
 import java.io.File
 import java.io.IOException
 import java.io.InputStream
-import java.io.OutputStream
 import java.nio.file.Path
-import java.security.MessageDigest
 import java.util.*
 import java.util.concurrent.atomic.AtomicBoolean
 import java.util.concurrent.atomic.AtomicReference
@@ -621,6 +619,12 @@ class SchemeManagerImpl<T : Scheme, MUTABLE_SCHEME : T>(val fileSpec: String,
       return
     }
 
+    // we must check it only here to avoid delete old scheme just because it is empty (old idea save -> new idea delete on open)
+    if (processor is LazySchemeProcessor && processor.isSchemeDefault(scheme, newDigest)) {
+      externalInfo?.scheduleDelete()
+      return
+    }
+
     val fileName = fileNameWithoutExtension!! + schemeExtension
     // file will be overwritten, so, we don't need to delete it
     filesToDelete.remove(fileName)
@@ -1000,27 +1004,6 @@ fun getFile(fileName: String, parent: VirtualFile, requestor: Any): VirtualFile
   return parent.findChild(fileName) ?: runWriteAction { parent.createChildData(requestor, fileName) }
 }
 
-class DigestOutputStream(val digest: MessageDigest) : OutputStream() {
-  override fun write(b: Int) {
-    digest.update(b.toByte())
-  }
-
-  override fun write(b: ByteArray, off: Int, len: Int) {
-    digest.update(b, off, len)
-  }
-
-  override fun toString(): String {
-    return "[Digest Output Stream] " + digest.toString()
-  }
-}
-
-fun Element.digest(): ByteArray {
-  // sha-1 is enough, sha-256 is slower, see https://www.nayuki.io/page/native-hash-functions-for-java
-  val digest = MessageDigest.getInstance("SHA-1")
-  serializeElementToBinary(this, DigestOutputStream(digest))
-  return digest.digest()
-}
-
 private inline fun catchAndLog(fileName: String, runnable: (fileName: String) -> Unit) {
   try {
     runnable(fileName)
index 77ffe3a3c3841d1e1c43ad78bddbe6a41cf55823..09a4175f7eeb204cb491a45659768d32bb188aad 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2015 JetBrains s.r.o.
+ * Copyright 2000-2016 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.
@@ -29,10 +29,12 @@ import com.intellij.openapi.util.Conditions;
 import com.intellij.openapi.util.Ref;
 import com.intellij.util.ArrayUtil;
 import com.intellij.util.EventDispatcher;
+import com.intellij.util.ExceptionUtil;
 import com.intellij.util.concurrency.Semaphore;
 import com.intellij.util.containers.ContainerUtil;
 import com.intellij.util.containers.Stack;
 import com.intellij.util.ui.UIUtil;
+import io.netty.util.internal.SystemPropertyUtil;
 import org.jetbrains.annotations.NonNls;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
@@ -172,9 +174,15 @@ public class LaterInvocator {
     invokeLater(runnable1, modalityState);
     semaphore.waitFor();
     if (!exception.isNull()) {
-      // wrap everything to keep the current thread stacktrace
-      // also TC ComparisonFailure feature depends on this
-      throw new RuntimeException(exception.get());
+      Throwable cause = exception.get();
+      if (SystemPropertyUtil.getBoolean("invoke.later.wrap.error", true)) {
+        // wrap everything to keep the current thread stacktrace
+        // also TC ComparisonFailure feature depends on this
+        throw new RuntimeException(cause);
+      }
+      else {
+        ExceptionUtil.rethrow(cause);
+      }
     }
   }
 
similarity index 80%
rename from platform/configuration-store-impl/src/BinaryXmlOutputter.kt
rename to platform/projectModel-impl/src/com/intellij/configurationStore/BinaryXmlOutputter.kt
index bdcbe16be468cf1d383436aadf37d5529e81c308..35ddf8f71407d381ad25ea74b32a1426c010bce5 100644 (file)
@@ -1,3 +1,18 @@
+/*
+ * Copyright 2000-2016 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 org.jdom.*
similarity index 73%
rename from platform/projectModel-impl/src/com/intellij/openapi/scheme-impl.kt
rename to platform/projectModel-impl/src/com/intellij/configurationStore/scheme-impl.kt
index 7fee19968eeb919b4e826d08e8b158a72e4b0d3b..633c5f50ec6f1db56f8ae9fda83606eb9f929b97 100644 (file)
@@ -18,6 +18,8 @@ package com.intellij.configurationStore
 import com.intellij.openapi.options.Scheme
 import com.intellij.openapi.options.SchemeProcessor
 import org.jdom.Element
+import java.io.OutputStream
+import java.security.MessageDigest
 import java.util.function.Function
 
 interface SchemeDataHolder<in MUTABLE_SCHEME : Scheme> {
@@ -59,4 +61,27 @@ abstract class LazySchemeProcessor<SCHEME : Scheme, MUTABLE_SCHEME : SCHEME> : S
   override final fun writeScheme(scheme: MUTABLE_SCHEME) = (scheme as SerializableScheme).writeScheme()
 
   open fun isSchemeFile(name: CharSequence) = true
+
+  open fun isSchemeDefault(scheme: MUTABLE_SCHEME, digest: ByteArray) = false
+}
+
+class DigestOutputStream(val digest: MessageDigest) : OutputStream() {
+  override fun write(b: Int) {
+    digest.update(b.toByte())
+  }
+
+  override fun write(b: ByteArray, off: Int, len: Int) {
+    digest.update(b, off, len)
+  }
+
+  override fun toString(): String {
+    return "[Digest Output Stream] " + digest.toString()
+  }
+}
+
+fun Element.digest(): ByteArray {
+  // sha-1 is enough, sha-256 is slower, see https://www.nayuki.io/page/native-hash-functions-for-java
+  val digest = MessageDigest.getInstance("SHA-1")
+  serializeElementToBinary(this, DigestOutputStream(digest))
+  return digest.digest()
 }
\ No newline at end of file
index f4997c6ae49b0baca901513bdf173aff78d7b495..949e5d287dab1b85a8e438f4ba090a7ff0757b27 100644 (file)
@@ -182,6 +182,10 @@ class EdtRule : TestRule {
   }
 }
 
+class InitInspectionRule : TestRule {
+  override fun apply(base: Statement, description: Description) = statement { runInInitMode { base.evaluate() } }
+}
+
 private inline fun statement(crossinline runnable: () -> Unit) = object : Statement() {
   override fun evaluate() {
     runnable()
index 8d3430dbf6d730655b50d2a8a0804ad1a451ca5a..64309ac961cf02c7d36f1b38787151492c267055 100644 (file)
@@ -18,9 +18,15 @@ package com.intellij.testFramework
 
 import com.intellij.openapi.util.JDOMUtil
 import com.intellij.util.isEmpty
+import com.intellij.util.readText
+import com.intellij.util.size
 import org.assertj.core.api.AbstractAssert
+import org.assertj.core.api.PathAssert
 import org.assertj.core.internal.Objects
 import org.jdom.Element
+import java.nio.file.Files
+import java.nio.file.LinkOption
+import java.nio.file.Path
 
 class JdomAssert(actual: Element?) : AbstractAssert<JdomAssert, Element?>(actual, JdomAssert::class.java) {
   fun isEmpty(): JdomAssert {
@@ -39,4 +45,19 @@ class JdomAssert(actual: Element?) : AbstractAssert<JdomAssert, Element?>(actual
     Objects.instance().assertEqual(info, JDOMUtil.writeElement(actual!!), expected.trimIndent())
     return this
   }
+}
+
+class PathAssertEx(actual: Path?) : PathAssert(actual) {
+  override fun doesNotExist(): PathAssert {
+    isNotNull
+
+    if (Files.exists(actual, LinkOption.NOFOLLOW_LINKS)) {
+      var error = "Expecting path:\n\t${actual}\nnot to exist"
+      if (actual.size() < 16 * 1024) {
+        error += ", content:\n\n${actual.readText()}\n"
+      }
+      failWithMessage(error)
+    }
+    return this
+  }
 }
\ No newline at end of file
index 3fe7397e9225c6231eab9689cc4bb9fd50c33158..57d593b465a05aa69a103ee548a645fb63035544 100644 (file)
  */
 package com.intellij.testFramework;
 
+import org.assertj.core.api.AbstractPathAssert;
 import org.jdom.Element;
+import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
+import java.nio.file.Path;
+
 public final class Assertions extends org.assertj.core.api.Assertions {
+  @NotNull
   public static JdomAssert assertThat(@Nullable Element element) {
     return new JdomAssert(element);
   }
+
+  @SuppressWarnings("MethodOverridesStaticMethodOfSuperclass")
+  @NotNull
+  public static AbstractPathAssert<?> assertThat(@Nullable Path actual) {
+    return new PathAssertEx(actual);
+  }
 }
index 1fc03c880992d185639427f400d421fd44fb4c1b..b8af59c4b747bc6e39456dd59ce752c38894d43a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2015 JetBrains s.r.o.
+ * Copyright 2000-2016 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.
@@ -160,6 +160,18 @@ public class ExceptionUtil {
     }
   }
 
+  public static void rethrow(@Nullable Throwable throwable) {
+    if (throwable instanceof Error) {
+      throw (Error)throwable;
+    }
+    else if (throwable instanceof RuntimeException) {
+      throw (RuntimeException)throwable;
+    }
+    else {
+      throw new RuntimeException(throwable);
+    }
+  }
+
   public static void rethrowAllAsUnchecked(@Nullable Throwable t) {
     if (t != null) {
       rethrowUnchecked(t);
index c7ee6325fa416aefd5b65712c65cd71d4c406174..4beb5afc9e09189fed082af435a8d95baab09df0 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2015 JetBrains s.r.o.
+ * Copyright 2000-2016 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,7 @@ package com.intellij.util.lang;
 
 import com.intellij.util.Consumer;
 import com.intellij.util.EmptyConsumer;
+import com.intellij.util.ExceptionUtil;
 import com.intellij.util.Function;
 import com.intellij.util.containers.ContainerUtil;
 import org.jetbrains.annotations.NotNull;
@@ -140,16 +141,7 @@ public class CompoundRuntimeException extends RuntimeException {
     }
 
     if (throwables.size() == 1) {
-      Throwable throwable = throwables.get(0);
-      if (throwable instanceof Error) {
-        throw (Error)throwable;
-      }
-      else if (throwable instanceof RuntimeException) {
-        throw (RuntimeException)throwable;
-      }
-      else {
-        throw new RuntimeException(throwable);
-      }
+      ExceptionUtil.rethrow(throwables.get(0));
     }
     else {
       throw new CompoundRuntimeException(throwables);
index 16707f9648e1980c84ce97e2b63b0253e4aad3aa..e9203ad10e66c3aa50ec9a81334d3471e92c204c 100644 (file)
@@ -15,6 +15,6 @@
     <orderEntry type="library" scope="TEST" name="JUnit4" level="project" />
     <orderEntry type="library" scope="TEST" name="picocontainer" level="project" />
     <orderEntry type="library" scope="TEST" name="jna" level="project" />
-    <orderEntry type="module" module-name="platform-impl" scope="TEST" />
+    <orderEntry type="module" module-name="core-api" scope="TEST" />
   </component>
 </module>
\ No newline at end of file
index e5314d8d8076e49c5f8d00d3b742a57037396401..9b6e779dc0af87559c6e126307bf29033c3bc465 100644 (file)
@@ -25,6 +25,7 @@
     <orderEntry type="library" name="batik" level="project" />
     <orderEntry type="library" name="xmlgraphics-commons" level="project" />
     <orderEntry type="library" name="xml-apis-ext" level="project" />
+    <orderEntry type="library" name="KotlinJavaRuntime" level="project" />
   </component>
   <component name="copyright">
     <Base>