blob: 89155decc845c6a36455eb33e1b73d2c70ac3cef [file] [log] [blame]
/*
* Copyright (c) 2010-2020 BSI Business Systems Integration AG.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* BSI Business Systems Integration AG - initial API and implementation
*/
package org.eclipse.scout.sdk.s2i.nls.inspection
import com.intellij.codeInspection.*
import com.intellij.openapi.module.Module
import com.intellij.openapi.progress.ProgressManager
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiFile
import org.eclipse.scout.sdk.core.log.SdkLog
import org.eclipse.scout.sdk.core.s.environment.IProgress
import org.eclipse.scout.sdk.core.s.nls.query.MissingTranslationQuery
import org.eclipse.scout.sdk.core.s.util.search.FileQueryInput
import org.eclipse.scout.sdk.core.s.util.search.FileQueryMatch
import org.eclipse.scout.sdk.core.s.util.search.IFileQuery
import org.eclipse.scout.sdk.s2i.EclipseScoutBundle.message
import org.eclipse.scout.sdk.s2i.containingModule
import org.eclipse.scout.sdk.s2i.environment.IdeaEnvironment
import org.eclipse.scout.sdk.s2i.getNioPath
import org.eclipse.scout.sdk.s2i.moduleDirPath
import org.eclipse.scout.sdk.s2i.toScoutProgress
import java.util.concurrent.ConcurrentHashMap
import java.util.logging.Level
open class MissingTranslationInspection : LocalInspectionTool() {
private val m_environmentByProject = ConcurrentHashMap<Project, Pair<IdeaEnvironment, MissingTranslationQuery>>()
private val m_supportedFileTypes = MissingTranslationQuery.supportedFileTypes()
override fun checkFile(file: PsiFile, manager: InspectionManager, isOnTheFly: Boolean): Array<ProblemDescriptor> {
if (!m_supportedFileTypes.contains(file.virtualFile.extension)) {
return ProblemDescriptor.EMPTY_ARRAY
}
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())
}
} catch (e: Exception) {
SdkLog.error("Failed to check for missing translations in {}.", file, e)
return ProblemDescriptor.EMPTY_ARRAY
}
}
fun checkFile(file: PsiFile, module: Module, query: IFileQuery, manager: InspectionManager, isOnTheFly: Boolean, environment: IdeaEnvironment, progress: IProgress): Array<ProblemDescriptor> {
val start = System.currentTimeMillis()
val path = file.virtualFile.getNioPath()
val queryInput = FileQueryInput(path, module.moduleDirPath()) { file.textToCharArray() }
query.searchIn(queryInput, environment, progress)
val result = query.result(path)
.filter { it.severity() >= Level.WARNING.intValue() } // only report important findings
.mapNotNull { toProblemDescription(it, file, manager, isOnTheFly) }
.toTypedArray()
SdkLog.debug("Missing translation inspection took {}ms", System.currentTimeMillis() - start)
return result
}
protected fun toProblemDescription(range: FileQueryMatch, file: PsiFile, manager: InspectionManager, isOnTheFly: Boolean): ProblemDescriptor? {
return IdeaEnvironment.computeInReadAction(file.project) {
val element = file.findElementAt(range.start()) ?: return@computeInReadAction null
val type = julLevelToProblemHighlightType(range.severity())
val msg = when (range.severity()) {
Level.INFO.intValue() -> message("possibly.missing.translation")
else -> message("missing.translation.for.key.x", range.text())
}
val fixes = if (isOnTheFly) arrayOf(AddMissingTranslationQuickFix(range.text())) else LocalQuickFix.EMPTY_ARRAY
return@computeInReadAction manager.createProblemDescriptor(element, msg, isOnTheFly, fixes, type)
}
}
protected fun julLevelToProblemHighlightType(severity: Int): ProblemHighlightType = when (severity) {
Level.INFO.intValue() -> ProblemHighlightType.WEAK_WARNING
Level.SEVERE.intValue() -> ProblemHighlightType.ERROR
else -> ProblemHighlightType.WARNING
}
override fun cleanup(project: Project) {
m_environmentByProject.remove(project)?.first?.close()
super.cleanup(project)
}
}