<commit_before>package org.jetbrains.plugins.scala.util

import javax.swing.event.{HyperlinkEvent, HyperlinkListener}

import com.intellij.application.options.CodeStyleSchemesConfigurable
import com.intellij.application.options.codeStyle.CodeStyleMainPanel
import com.intellij.ide.actions.ShowSettingsUtilImpl
import com.intellij.openapi.options.ex.ConfigurableVisitor
import com.intellij.openapi.options.{Configurable, ConfigurableGroup, ShowSettingsUtil}
import com.intellij.openapi.project.Project
import com.intellij.psi._
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.ui.HyperlinkLabel
import org.jetbrains.plugins.scala.codeInsight.intention.types.AddOnlyStrategy
import org.jetbrains.plugins.scala.extensions.PsiElementExt
import org.jetbrains.plugins.scala.lang.formatting.settings.{ScalaCodeStyleSettings, ScalaTabbedCodeStylePanel, TypeAnnotationPolicy, TypeAnnotationRequirement}
import org.jetbrains.plugins.scala.lang.psi.api.base.ScLiteral
import org.jetbrains.plugins.scala.lang.psi.api.base.types.ScTypeElement
import org.jetbrains.plugins.scala.lang.psi.api.expr._
import org.jetbrains.plugins.scala.lang.psi.api.statements._
import org.jetbrains.plugins.scala.lang.psi.api.toplevel.templates.ScTemplateBody
import org.jetbrains.plugins.scala.lang.psi.api.toplevel.typedef.ScMember
import org.jetbrains.plugins.scala.lang.psi.impl.ScalaPsiManager
import org.jetbrains.plugins.scala.lang.psi.{ScalaPsiElement, ScalaPsiUtil}
import org.jetbrains.plugins.scala.settings.ScalaApplicationSettings
import org.jetbrains.plugins.scala.settings.ScalaApplicationSettings.ReturnTypeLevel
import org.jetbrains.plugins.scala.extensions._

/**
  * Created by kate on 7/14/16.
  */
object TypeAnnotationUtil {
  private val CollectionClassNames =
    Seq("Seq", "Array", "Vector", "Set", "HashSet", "Map", "HashMap", "Iterator", "Option")

  def isTypeAnnotationNeeded(requiment: Int, ovPolicy: Int, simplePolicy: Int, isOverride: Boolean, isSimple: Boolean): Boolean = {

    requiment != TypeAnnotationRequirement.Optional.ordinal() &&
      (!isSimple || simplePolicy != TypeAnnotationPolicy.Optional.ordinal()) &&
      (!isOverride || ovPolicy != TypeAnnotationPolicy.Optional.ordinal())
  }

  def isTypeAnnotationNeeded(element: ScalaPsiElement): Boolean = {
    val settings = ScalaCodeStyleSettings.getInstance(element.getProject)

    element match {
      case value: ScPatternDefinition if value.isSimple => //not simple will contains more than one declaration

        isTypeAnnotationNeeded(requirementForProperty(value, settings),
          settings.OVERRIDING_PROPERTY_TYPE_ANNOTATION,
          settings.SIMPLE_PROPERTY_TYPE_ANNOTATION,
          isOverriding(value),
          value.expr.exists(isSimple))

      case variable: ScVariableDefinition if variable.isSimple =>

        isTypeAnnotationNeeded(requirementForProperty(variable, settings),
          settings.OVERRIDING_PROPERTY_TYPE_ANNOTATION,
          settings.SIMPLE_PROPERTY_TYPE_ANNOTATION,
          isOverriding(variable),
          variable.expr.exists(isSimple))

      case method: ScFunctionDefinition if method.hasAssign && !method.isSecondaryConstructor =>

        isTypeAnnotationNeeded(requirementForMethod(method, settings),
          settings.OVERRIDING_METHOD_TYPE_ANNOTATION,
          settings.SIMPLE_METHOD_TYPE_ANNOTATION,
          isOverriding(method),
          method.body.exists(isSimple))

      case _ => true
    }
  }
  def isOverriding(element: PsiElement): Boolean = {
    element match {
      case func: ScFunctionDefinition =>
        func.superSignaturesIncludingSelfType.nonEmpty
      case variable: ScVariableDefinition =>
        variable.declaredElements.headOption.map(ScalaPsiUtil.superValsSignatures(_, withSelfType = true)).exists(_.nonEmpty)
      case pattern: ScPatternDefinition =>
        pattern.declaredElements.headOption.map(ScalaPsiUtil.superValsSignatures(_, withSelfType = true)).exists(_.nonEmpty)
      case _ => false
    }
  }

  def requirementForProperty(isLocal: Boolean, visibility: Visibility, settings: ScalaCodeStyleSettings): Int = {
    if (isLocal)
      settings.LOCAL_PROPERTY_TYPE_ANNOTATION
    else visibility match {
      case Private => settings.PRIVATE_PROPERTY_TYPE_ANNOTATION
      case Protected => settings.PROTECTED_PROPERTY_TYPE_ANNOTATION
      case Public => settings.PUBLIC_PROPERTY_TYPE_ANNOTATION
    }
  }

  def requirementForMethod(isLocal: Boolean, visibility: Visibility, settings: ScalaCodeStyleSettings): Int = {
    if (isLocal)
      settings.LOCAL_METHOD_TYPE_ANNOTATION
    else visibility match {
      case Private => settings.PRIVATE_METHOD_TYPE_ANNOTATION
      case Protected => settings.PROTECTED_METHOD_TYPE_ANNOTATION
      case Public => settings.PUBLIC_METHOD_TYPE_ANNOTATION
    }
  }

  def requirementForProperty(property: ScMember, settings: ScalaCodeStyleSettings): Int = {
    if (property.isLocal || isMemberOf(property, "scala.DelayedInit")) {
      settings.LOCAL_PROPERTY_TYPE_ANNOTATION
    } else {
      if (property.isPrivate) settings.PRIVATE_PROPERTY_TYPE_ANNOTATION
      else if (property.isProtected) settings.PROTECTED_PROPERTY_TYPE_ANNOTATION
      else settings.PUBLIC_PROPERTY_TYPE_ANNOTATION
    }
  }

  def isMemberOf(member: ScMember, fqn: String) = {
    val aClass =
      for (module <- Option(ScalaPsiUtil.getModule(member));
           scope <- Option(GlobalSearchScope.moduleWithDependenciesAndLibrariesScope(module));
           aClass <- ScalaPsiManager.instance(member.getProject).getCachedClass(scope, fqn))
        yield aClass

    aClass.exists(member.getContainingClass.isInheritor(_, true))
  }

  def requirementForMethod(method: ScMember, settings: ScalaCodeStyleSettings): Int = {
    if (method.isLocal) {
      settings.LOCAL_METHOD_TYPE_ANNOTATION
    } else {
      if (method.isPrivate) settings.PRIVATE_METHOD_TYPE_ANNOTATION
      else if (method.isProtected) settings.PROTECTED_METHOD_TYPE_ANNOTATION
      else settings.PUBLIC_METHOD_TYPE_ANNOTATION
    }
  }

  def isSimple(exp: ScExpression): Boolean = {
    exp match {
      case _: ScLiteral => true
      case _: ScNewTemplateDefinition => true<commit_msg>TypeAnnotationUtil.isMemberOf: NPE fixed (#SCL-10969 fixed)<commit_after>package org.jetbrains.plugins.scala.util

import javax.swing.event.{HyperlinkEvent, HyperlinkListener}

import com.intellij.application.options.CodeStyleSchemesConfigurable
import com.intellij.application.options.codeStyle.CodeStyleMainPanel
import com.intellij.ide.actions.ShowSettingsUtilImpl
import com.intellij.openapi.options.ex.ConfigurableVisitor
import com.intellij.openapi.options.{Configurable, ConfigurableGroup, ShowSettingsUtil}
import com.intellij.openapi.project.Project
import com.intellij.psi._
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.ui.HyperlinkLabel
import org.jetbrains.plugins.scala.codeInsight.intention.types.AddOnlyStrategy
import org.jetbrains.plugins.scala.extensions.PsiElementExt
import org.jetbrains.plugins.scala.lang.formatting.settings.{ScalaCodeStyleSettings, ScalaTabbedCodeStylePanel, TypeAnnotationPolicy, TypeAnnotationRequirement}
import org.jetbrains.plugins.scala.lang.psi.api.base.ScLiteral
import org.jetbrains.plugins.scala.lang.psi.api.base.types.ScTypeElement
import org.jetbrains.plugins.scala.lang.psi.api.expr._
import org.jetbrains.plugins.scala.lang.psi.api.statements._
import org.jetbrains.plugins.scala.lang.psi.api.toplevel.templates.ScTemplateBody
import org.jetbrains.plugins.scala.lang.psi.api.toplevel.typedef.ScMember
import org.jetbrains.plugins.scala.lang.psi.impl.ScalaPsiManager
import org.jetbrains.plugins.scala.lang.psi.{ScalaPsiElement, ScalaPsiUtil}
import org.jetbrains.plugins.scala.settings.ScalaApplicationSettings
import org.jetbrains.plugins.scala.settings.ScalaApplicationSettings.ReturnTypeLevel
import org.jetbrains.plugins.scala.extensions._

/**
  * Created by kate on 7/14/16.
  */
object TypeAnnotationUtil {
  private val CollectionClassNames =
    Seq("Seq", "Array", "Vector", "Set", "HashSet", "Map", "HashMap", "Iterator", "Option")

  def isTypeAnnotationNeeded(requiment: Int, ovPolicy: Int, simplePolicy: Int, isOverride: Boolean, isSimple: Boolean): Boolean = {

    requiment != TypeAnnotationRequirement.Optional.ordinal() &&
      (!isSimple || simplePolicy != TypeAnnotationPolicy.Optional.ordinal()) &&
      (!isOverride || ovPolicy != TypeAnnotationPolicy.Optional.ordinal())
  }

  def isTypeAnnotationNeeded(element: ScalaPsiElement): Boolean = {
    val settings = ScalaCodeStyleSettings.getInstance(element.getProject)

    element match {
      case value: ScPatternDefinition if value.isSimple => //not simple will contains more than one declaration

        isTypeAnnotationNeeded(requirementForProperty(value, settings),
          settings.OVERRIDING_PROPERTY_TYPE_ANNOTATION,
          settings.SIMPLE_PROPERTY_TYPE_ANNOTATION,
          isOverriding(value),
          value.expr.exists(isSimple))

      case variable: ScVariableDefinition if variable.isSimple =>

        isTypeAnnotationNeeded(requirementForProperty(variable, settings),
          settings.OVERRIDING_PROPERTY_TYPE_ANNOTATION,
          settings.SIMPLE_PROPERTY_TYPE_ANNOTATION,
          isOverriding(variable),
          variable.expr.exists(isSimple))

      case method: ScFunctionDefinition if method.hasAssign && !method.isSecondaryConstructor =>

        isTypeAnnotationNeeded(requirementForMethod(method, settings),
          settings.OVERRIDING_METHOD_TYPE_ANNOTATION,
          settings.SIMPLE_METHOD_TYPE_ANNOTATION,
          isOverriding(method),
          method.body.exists(isSimple))

      case _ => true
    }
  }
  def isOverriding(element: PsiElement): Boolean = {
    element match {
      case func: ScFunctionDefinition =>
        func.superSignaturesIncludingSelfType.nonEmpty
      case variable: ScVariableDefinition =>
        variable.declaredElements.headOption.map(ScalaPsiUtil.superValsSignatures(_, withSelfType = true)).exists(_.nonEmpty)
      case pattern: ScPatternDefinition =>
        pattern.declaredElements.headOption.map(ScalaPsiUtil.superValsSignatures(_, withSelfType = true)).exists(_.nonEmpty)
      case _ => false
    }
  }

  def requirementForProperty(isLocal: Boolean, visibility: Visibility, settings: ScalaCodeStyleSettings): Int = {
    if (isLocal)
      settings.LOCAL_PROPERTY_TYPE_ANNOTATION
    else visibility match {
      case Private => settings.PRIVATE_PROPERTY_TYPE_ANNOTATION
      case Protected => settings.PROTECTED_PROPERTY_TYPE_ANNOTATION
      case Public => settings.PUBLIC_PROPERTY_TYPE_ANNOTATION
    }
  }

  def requirementForMethod(isLocal: Boolean, visibility: Visibility, settings: ScalaCodeStyleSettings): Int = {
    if (isLocal)
      settings.LOCAL_METHOD_TYPE_ANNOTATION
    else visibility match {
      case Private => settings.PRIVATE_METHOD_TYPE_ANNOTATION
      case Protected => settings.PROTECTED_METHOD_TYPE_ANNOTATION
      case Public => settings.PUBLIC_METHOD_TYPE_ANNOTATION
    }
  }

  def requirementForProperty(property: ScMember, settings: ScalaCodeStyleSettings): Int = {
    if (property.isLocal || isMemberOf(property, "scala.DelayedInit")) {
      settings.LOCAL_PROPERTY_TYPE_ANNOTATION
    } else {
      if (property.isPrivate) settings.PRIVATE_PROPERTY_TYPE_ANNOTATION
      else if (property.isProtected) settings.PROTECTED_PROPERTY_TYPE_ANNOTATION
      else settings.PUBLIC_PROPERTY_TYPE_ANNOTATION
    }
  }

  def isMemberOf(member: ScMember, fqn: String): Boolean = {
    val result =
      for (containtingClass <- Option(member.getContainingClass);
           module <- Option(ScalaPsiUtil.getModule(member));
           scope <- Option(GlobalSearchScope.moduleWithDependenciesAndLibrariesScope(module));
           aClass <- ScalaPsiManager.instance(member.getProject).getCachedClass(scope, fqn))
        yield containtingClass.isInheritor(aClass, true)

    result.getOrElse(false)
  }

  def requirementForMethod(method: ScMember, settings: ScalaCodeStyleSettings): Int = {
    if (method.isLocal) {
      settings.LOCAL_METHOD_TYPE_ANNOTATION
    } else {
      if (method.isPrivate) settings.PRIVATE_METHOD_TYPE_ANNOTATION
      else if (method.isProtected) settings.PROTECTED_METHOD_TYPE_ANNOTATION
      else settings.PUBLIC_METHOD_TYPE_ANNOTATION
    }
  }

  def isSimple(exp: ScExpression): Boolean = {
    exp match {
      case _: ScLiteral => true
      case _: ScNewTemplateDefinition => true
{'fraction_non_alphanumeric': 0.08887821282728801, 'fraction_numerical': 0.0012010569300984868, 'mean_word_length': 4.548645046645935, 'pattern_counts': {'":': 0, '<': 10, '<?xml version=': 0, '>': 35, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4152495', 'n_tokens_mistral': 3713, 'n_tokens_neox': 3573, 'n_words': 586}