IntelliJ logging improvements

Never log or swallow ControlFlowExceptions (always rethrow)
Log error level as warning to prevent the fatal plugin error dialog
diff --git a/org.eclipse.scout.sdk.core/src/main/java/org/eclipse/scout/sdk/core/log/LogMessage.java b/org.eclipse.scout.sdk.core/src/main/java/org/eclipse/scout/sdk/core/log/LogMessage.java
index 66fce67..886ddac 100644
--- a/org.eclipse.scout.sdk.core/src/main/java/org/eclipse/scout/sdk/core/log/LogMessage.java
+++ b/org.eclipse.scout.sdk.core/src/main/java/org/eclipse/scout/sdk/core/log/LogMessage.java
@@ -11,6 +11,7 @@
 package org.eclipse.scout.sdk.core.log;
 
 import static java.lang.System.lineSeparator;
+import static java.util.Collections.unmodifiableList;
 
 import java.util.Iterator;
 import java.util.List;
@@ -61,6 +62,14 @@
   }
 
   /**
+   * @return All {@link Throwable}s of the arguments as unmodifiable {@link List}. Neither the {@link List} nor one of
+   *         its elements may be {@code null}.
+   */
+  public List<Throwable> throwableList() {
+    return unmodifiableList(m_throwables);
+  }
+
+  /**
    * @return The first {@link Throwable}.
    */
   public Optional<Throwable> firstThrowable() {
diff --git a/org.eclipse.scout.sdk.s2i/src/main/kotlin/org/eclipse/scout/sdk/s2i/IdeaLogger.kt b/org.eclipse.scout.sdk.s2i/src/main/kotlin/org/eclipse/scout/sdk/s2i/IdeaLogger.kt
index 0760a04..203908c 100644
--- a/org.eclipse.scout.sdk.s2i/src/main/kotlin/org/eclipse/scout/sdk/s2i/IdeaLogger.kt
+++ b/org.eclipse.scout.sdk.s2i/src/main/kotlin/org/eclipse/scout/sdk/s2i/IdeaLogger.kt
@@ -27,6 +27,7 @@
 open class IdeaLogger : ISdkConsoleSpi, StartupActivity, DumbAware {
 
     private val m_textLog = Logger.getInstance(IdeaLogger::class.java)
+
     @Suppress("MissingRecentApi") // method does not exist on the companion, but static access is available -> ok
     private val m_balloonLog = NotificationGroup.balloonGroup("Scout")
 
@@ -80,20 +81,26 @@
         notification.notify(null)
     }
 
+    protected fun isControlFlowException(t: Throwable) = t is ControlFlowException
+
     protected fun logToTextFile(msg: LogMessage) {
-        val exception: Throwable? = msg.throwables()
-                .filter { it !is ControlFlowException } // ControlFlowExceptions should not be logged. See com.intellij.openapi.diagnostic.Logger.checkException
-                .findFirst()
-                .orElse(null)
+        // ControlFlowExceptions should not be logged. See com.intellij.openapi.diagnostic.Logger.checkException
+        val controlFlowException = msg.throwableList().firstOrNull { isControlFlowException(it) }
+        val loggableThrowable = msg.throwableList().firstOrNull { !isControlFlowException(it) }
+        if (controlFlowException != null && loggableThrowable == null) {
+            // the log event is from a ControlFlowException. These must not be logged. Instead: rethrow
+            throw controlFlowException
+        }
+
         // do not log the prefix here as this information is already logged by the text logger
         when (msg.severity()) {
-            Level.SEVERE -> m_textLog.error(msg.text(), exception)
-            Level.WARNING -> m_textLog.warn(msg.text(), exception)
-            Level.INFO -> m_textLog.info(msg.text(), exception)
-            Level.FINE -> m_textLog.debug(msg.text(), exception)
+            Level.SEVERE -> m_textLog.warn(msg.text(), loggableThrowable) // do not use m_textLog.error() because this is registered as fatal plugin error
+            Level.WARNING -> m_textLog.warn(msg.text(), loggableThrowable)
+            Level.INFO -> m_textLog.info(msg.text(), loggableThrowable)
+            Level.FINE -> m_textLog.debug(msg.text(), loggableThrowable)
             else -> {
                 m_textLog.trace(msg.text())
-                exception?.let { m_textLog.trace(it) }
+                loggableThrowable?.let { m_textLog.trace(it) }
             }
         }
     }
diff --git a/org.eclipse.scout.sdk.s2i/src/main/kotlin/org/eclipse/scout/sdk/s2i/classid/ClassIdCacheImplementor.kt b/org.eclipse.scout.sdk.s2i/src/main/kotlin/org/eclipse/scout/sdk/s2i/classid/ClassIdCacheImplementor.kt
index 54b37c7..03eb04e 100644
--- a/org.eclipse.scout.sdk.s2i/src/main/kotlin/org/eclipse/scout/sdk/s2i/classid/ClassIdCacheImplementor.kt
+++ b/org.eclipse.scout.sdk.s2i/src/main/kotlin/org/eclipse/scout/sdk/s2i/classid/ClassIdCacheImplementor.kt
@@ -11,7 +11,6 @@
 package org.eclipse.scout.sdk.s2i.classid
 
 import com.intellij.lang.java.JavaLanguage
-import com.intellij.openapi.progress.ProcessCanceledException
 import com.intellij.openapi.progress.ProgressIndicator
 import com.intellij.openapi.project.Project
 import com.intellij.openapi.util.Disposer
@@ -28,7 +27,6 @@
 import com.intellij.psi.util.PsiTreeUtil
 import com.intellij.util.FileContentUtilCore
 import org.eclipse.scout.sdk.core.log.SdkLog
-import org.eclipse.scout.sdk.core.log.SdkLog.onTrace
 import org.eclipse.scout.sdk.core.s.IScoutRuntimeTypes
 import org.eclipse.scout.sdk.core.s.dto.AbstractDtoGenerator
 import org.eclipse.scout.sdk.s2i.environment.TransactionManager
@@ -67,8 +65,6 @@
             m_cacheReady = true
 
             duplicates().forEach { SdkLog.debug("Duplicate @ClassId value '{}' found for types {}.", it.key, it.value) }
-        } catch (e: ProcessCanceledException) {
-            SdkLog.debug("@ClassId value cache creation canceled. Retry on next use.", onTrace(e))
         } catch (t: Exception) {
             SdkLog.warning("Error building @ClassId value cache.", t)
         }
diff --git a/org.eclipse.scout.sdk.s2i/src/main/kotlin/org/eclipse/scout/sdk/s2i/classid/DuplicateClassIdInspection.kt b/org.eclipse.scout.sdk.s2i/src/main/kotlin/org/eclipse/scout/sdk/s2i/classid/DuplicateClassIdInspection.kt
index f66bf6e..9b33eaf 100644
--- a/org.eclipse.scout.sdk.s2i/src/main/kotlin/org/eclipse/scout/sdk/s2i/classid/DuplicateClassIdInspection.kt
+++ b/org.eclipse.scout.sdk.s2i/src/main/kotlin/org/eclipse/scout/sdk/s2i/classid/DuplicateClassIdInspection.kt
@@ -14,7 +14,6 @@
 import com.intellij.codeInspection.LocalInspectionTool
 import com.intellij.codeInspection.ProblemDescriptor
 import com.intellij.codeInspection.ProblemHighlightType
-import com.intellij.openapi.progress.ProcessCanceledException
 import com.intellij.psi.PsiClass
 import com.intellij.psi.PsiFile
 import com.intellij.psi.PsiJavaFile
@@ -36,8 +35,8 @@
                     .mapNotNull { createProblemFor(it.value, javaFile, manager, isOnTheFly) }
                     .flatten()
                     .toTypedArray()
-        } catch (e: ProcessCanceledException) {
-            SdkLog.debug("Duplicate @ClassId inspection canceled.", e)
+        } catch (e: Exception) {
+            SdkLog.error("Duplicate @ClassId inspection failed for file '{}'.", file, e)
             ProblemDescriptor.EMPTY_ARRAY
         }
     }
diff --git a/org.eclipse.scout.sdk.s2i/src/main/kotlin/org/eclipse/scout/sdk/s2i/classid/MissingClassIdInspection.kt b/org.eclipse.scout.sdk.s2i/src/main/kotlin/org/eclipse/scout/sdk/s2i/classid/MissingClassIdInspection.kt
index 8df52f7..cd33a45 100644
--- a/org.eclipse.scout.sdk.s2i/src/main/kotlin/org/eclipse/scout/sdk/s2i/classid/MissingClassIdInspection.kt
+++ b/org.eclipse.scout.sdk.s2i/src/main/kotlin/org/eclipse/scout/sdk/s2i/classid/MissingClassIdInspection.kt
@@ -16,6 +16,7 @@
 import com.intellij.codeInspection.ProblemHighlightType
 import com.intellij.psi.PsiClass
 import com.intellij.psi.PsiTypeParameter
+import org.eclipse.scout.sdk.core.log.SdkLog
 import org.eclipse.scout.sdk.core.s.IScoutRuntimeTypes
 import org.eclipse.scout.sdk.core.util.Strings
 import org.eclipse.scout.sdk.s2i.EclipseScoutBundle
@@ -29,19 +30,22 @@
     private val m_template = EclipseScoutBundle.message("missing.classid.annotation")
 
     override fun checkClass(aClass: PsiClass, manager: InspectionManager, isOnTheFly: Boolean): Array<ProblemDescriptor>? {
-
         if (!supportsClassId(aClass) || !isInSourceRoot(aClass)) {
             return ProblemDescriptor.EMPTY_ARRAY
         }
 
-        val hasClassIdValue = ClassIdAnnotation.of(aClass)?.hasValue() ?: false
-        if (hasClassIdValue) {
+        try {
+            val hasClassIdValue = ClassIdAnnotation.of(aClass)?.hasValue() ?: false
+            if (hasClassIdValue) {
+                return ProblemDescriptor.EMPTY_ARRAY
+            }
+            val nameElement = aClass.nameIdentifier ?: return ProblemDescriptor.EMPTY_ARRAY
+            val problem = manager.createProblemDescriptor(nameElement, m_template, isOnTheFly, arrayOf(m_addMissingClassIdQuickFix), ProblemHighlightType.ERROR)
+            return arrayOf(problem)
+        } catch (e: Exception) {
+            SdkLog.error("Failed to check for missing @ClassId in {}.", aClass, e)
             return ProblemDescriptor.EMPTY_ARRAY
         }
-
-        val nameElement = aClass.nameIdentifier ?: return ProblemDescriptor.EMPTY_ARRAY
-        val problem = manager.createProblemDescriptor(nameElement, m_template, isOnTheFly, arrayOf(m_addMissingClassIdQuickFix), ProblemHighlightType.ERROR)
-        return arrayOf(problem)
     }
 
 
diff --git a/org.eclipse.scout.sdk.s2i/src/main/kotlin/org/eclipse/scout/sdk/s2i/derived/impl/DerivedResourceManagerImplementor.kt b/org.eclipse.scout.sdk.s2i/src/main/kotlin/org/eclipse/scout/sdk/s2i/derived/impl/DerivedResourceManagerImplementor.kt
index 4cee248..30d7374 100644
--- a/org.eclipse.scout.sdk.s2i/src/main/kotlin/org/eclipse/scout/sdk/s2i/derived/impl/DerivedResourceManagerImplementor.kt
+++ b/org.eclipse.scout.sdk.s2i/src/main/kotlin/org/eclipse/scout/sdk/s2i/derived/impl/DerivedResourceManagerImplementor.kt
@@ -14,7 +14,6 @@
 import com.intellij.openapi.editor.Document
 import com.intellij.openapi.fileEditor.FileDocumentManager
 import com.intellij.openapi.fileEditor.FileDocumentManagerListener
-import com.intellij.openapi.progress.ProcessCanceledException
 import com.intellij.openapi.progress.ProgressIndicator
 import com.intellij.openapi.project.Project
 import com.intellij.openapi.util.Disposer
@@ -25,7 +24,6 @@
 import com.intellij.util.concurrency.AppExecutorUtil
 import com.intellij.util.messages.MessageBusConnection
 import org.eclipse.scout.sdk.core.log.SdkLog
-import org.eclipse.scout.sdk.core.log.SdkLog.onTrace
 import org.eclipse.scout.sdk.core.s.derived.IDerivedResourceHandler
 import org.eclipse.scout.sdk.core.s.environment.IFuture
 import org.eclipse.scout.sdk.core.s.environment.IProgress
@@ -58,6 +56,7 @@
 
     @Volatile
     private var m_busConnection: MessageBusConnection? = null
+
     @Volatile
     private var m_workerEnabled = false
 
@@ -222,9 +221,7 @@
                 SdkLog.debug("About to execute derived resource handler: {}", handler)
                 return@callInExistingTransaction handler.apply(env, progress)
             }
-        } catch (e: ProcessCanceledException) {
-            SdkLog.debug("Derived resources update canceled.", onTrace(e))
-        } catch (e: RuntimeException) {
+        } catch (e: Exception) {
             // log the exception but continue processing. The failure of one handler does not abort the transaction
             SdkLog.error("Error while: {}", handler, e)
         } finally {
diff --git a/org.eclipse.scout.sdk.s2i/src/main/kotlin/org/eclipse/scout/sdk/s2i/nls/TranslationStoreStackLoader.kt b/org.eclipse.scout.sdk.s2i/src/main/kotlin/org/eclipse/scout/sdk/s2i/nls/TranslationStoreStackLoader.kt
index 5a98fad..4dcbed1 100644
--- a/org.eclipse.scout.sdk.s2i/src/main/kotlin/org/eclipse/scout/sdk/s2i/nls/TranslationStoreStackLoader.kt
+++ b/org.eclipse.scout.sdk.s2i/src/main/kotlin/org/eclipse/scout/sdk/s2i/nls/TranslationStoreStackLoader.kt
@@ -11,7 +11,10 @@
 package org.eclipse.scout.sdk.s2i.nls
 
 import com.intellij.openapi.module.Module
-import com.intellij.openapi.progress.*
+import com.intellij.openapi.progress.EmptyProgressIndicator
+import com.intellij.openapi.progress.ProgressIndicator
+import com.intellij.openapi.progress.ProgressManager
+import com.intellij.openapi.progress.Task
 import com.intellij.openapi.project.Project
 import com.intellij.openapi.vfs.VirtualFile
 import org.eclipse.scout.sdk.core.log.SdkLog
@@ -104,9 +107,7 @@
         return try {
             val indicator = ProgressManager.getInstance().progressIndicator ?: EmptyProgressIndicator()
             return createStack(module, nlsFile, indicator)
-        } catch (e: ProcessCanceledException) {
-            throw e
-        } catch (e: RuntimeException) {
+        } catch (e: Exception) {
             SdkLog.error("Error computing texts for module '{}'.", module.name, e)
             null
         } finally {
diff --git a/org.eclipse.scout.sdk.s2i/src/main/kotlin/org/eclipse/scout/sdk/s2i/nls/inspection/MissingTranslationInspection.kt b/org.eclipse.scout.sdk.s2i/src/main/kotlin/org/eclipse/scout/sdk/s2i/nls/inspection/MissingTranslationInspection.kt
index e2d119f..89155de 100644
--- a/org.eclipse.scout.sdk.s2i/src/main/kotlin/org/eclipse/scout/sdk/s2i/nls/inspection/MissingTranslationInspection.kt
+++ b/org.eclipse.scout.sdk.s2i/src/main/kotlin/org/eclipse/scout/sdk/s2i/nls/inspection/MissingTranslationInspection.kt
@@ -40,20 +40,25 @@
             return ProblemDescriptor.EMPTY_ARRAY
         }
 
-        val module = file.containingModule(false) ?: return ProblemDescriptor.EMPTY_ARRAY
-        val progress = ProgressManager.getInstance().progressIndicator
-        return if (isOnTheFly) {
-            // single file: create short living environment for this file only
-            val query = MissingTranslationQuery()
-            IdeaEnvironment.callInIdeaEnvironmentSync(file.project, progress.toScoutProgress()) { e, p ->
-                checkFile(file, module, query, manager, true, e, p)
+        try {
+            val module = file.containingModule(false) ?: return ProblemDescriptor.EMPTY_ARRAY
+            val progress = ProgressManager.getInstance().progressIndicator
+            return if (isOnTheFly) {
+                // single file: create short living environment for this file only
+                val query = MissingTranslationQuery()
+                IdeaEnvironment.callInIdeaEnvironmentSync(file.project, progress.toScoutProgress()) { e, p ->
+                    checkFile(file, module, query, manager, true, e, p)
+                }
+            } else {
+                // batch inspection run: create environment and query in cache. Will be removed and closed in cleanup function
+                val cache = m_environmentByProject.computeIfAbsent(file.project) { project ->
+                    Pair(IdeaEnvironment.createUnsafeFor(project) { }, MissingTranslationQuery())
+                }
+                checkFile(file, module, cache.second, manager, false, cache.first, progress.toScoutProgress())
             }
-        } else {
-            // batch inspection run: create environment and query in cache. Will be removed and closed in cleanup function
-            val cache = m_environmentByProject.computeIfAbsent(file.project) { project ->
-                Pair(IdeaEnvironment.createUnsafeFor(project) { }, MissingTranslationQuery())
-            }
-            checkFile(file, module, cache.second, manager, false, cache.first, progress.toScoutProgress())
+        } catch (e: Exception) {
+            SdkLog.error("Failed to check for missing translations in {}.", file, e)
+            return ProblemDescriptor.EMPTY_ARRAY
         }
     }