All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.nawforce.apexlink.org.ImplementationProvider.scala Maven / Gradle / Ivy

/*
 * Copyright (c) 2022 FinancialForce.com, inc. All rights reserved.
 */

package com.nawforce.apexlink.org

import com.nawforce.apexlink.finding.TypeResolver
import com.nawforce.apexlink.org.TextOps.TestOpsUtils
import com.nawforce.apexlink.rpc.LocationLink
import com.nawforce.apexlink.types.apex.{ApexClassDeclaration, ApexDeclaration, ApexMethodLike}
import com.nawforce.apexlink.types.core.{Dependent, DependentType, TypeDeclaration}
import com.nawforce.pkgforce.parsers.CLASS_NATURE
import com.nawforce.pkgforce.path.{Locatable, PathLike}

import scala.collection.mutable.ArrayBuffer

trait ImplementationProvider extends SourceOps {
  this: OPM.PackageImpl =>

  def getImplementation(
    path: PathLike,
    line: Int,
    offset: Int,
    content: Option[String]
  ): Array[LocationLink] = {
    val sourceAndType = loadSource(path, content).flatMap(source => {
      loadTypeFromModule(path) match {
        case Some(dt: DependentType) => Some((source, dt))
        case _                       => loadRawType(path, source)
      }
    })

    if (sourceAndType.isEmpty)
      return Array.empty

    getImplementation(line, offset, sourceAndType)
  }

  private def getImplementation(
    line: Int,
    offset: Int,
    sourceAndType: Option[(String, DependentType)]
  ): Array[LocationLink] = {
    def getTransitiveDependents(td: DependentType): ArrayBuffer[TypeDeclaration] = {
      td.getTypeDependencyHolders.toIterable.foldLeft(ArrayBuffer[TypeDeclaration]())((acc, id) => {
        TypeResolver(id.typeName, id.module).toOption match {
          // if the used by declaration is extensible, find the other classes that use it and add it to the acc
          case Some(ExtensibleClassesAndInterface(clsOrInterface)) =>
            acc.appendAll(
              getTransitiveDependents(clsOrInterface)
                .appendAll(clsOrInterface.nestedTypes)
                .append(clsOrInterface)
            )
          case Some(value) => acc.append(value)
          case _           =>
        }
        acc
      })
    }

    def getSearchContext(td: TypeDeclaration): Option[Dependent with Locatable] = {
      td match {
        case ExtensibleClassesAndInterface(td) =>
          // Only find method or class declaration to search implementations for
          td.localMethods
            .collect { case m: ApexMethodLike => m }
            .find(_.location.location.contains(line, offset))
            .orElse(Option.when(td.location.location.contains(line, offset)) { td })

        case _ =>
          if (td.nestedTypes.isEmpty) None else td.nestedTypes.flatMap(getSearchContext).headOption
      }
    }

    val source   = sourceAndType.get._1
    val sourceTD = sourceAndType.get._2

    val searchLocation =
      source
        .extractDotTermInclusive(() => new IdentifierLimiter, line, offset)
        .orElse(return Array.empty)
        .get
        ._2

    val searchContext = getSearchContext(sourceTD)

    lazy val usedByTds = getTransitiveDependents(sourceTD)

    searchContext match {
      case Some(method: ApexMethodLike) =>
        method
          .collectMethods()
          .filterNot(_ eq method)
          .map(m =>
            LocationLink(
              searchLocation,
              m.location.path.toString,
              m.location.location,
              m.idLocation
            )
          )
          .toArray
      case Some(td: ApexDeclaration) =>
        usedByTds
          .collect { case fd: ApexDeclaration => fd }
          .filter(_.nature == CLASS_NATURE)
          .filter(_.superTypes().contains(td.typeName))
          .map(fd =>
            LocationLink(
              searchLocation,
              fd.location.path.toString,
              fd.location.location,
              fd.idLocation
            )
          )
          .distinct
          .toArray
      case _ => Array.empty
    }
  }

}

private object ExtensibleClassesAndInterface {
  def unapply(td: TypeDeclaration): Option[ApexClassDeclaration] =
    Option.when(td.isExtensible) { td } collect { case td: ApexClassDeclaration => td }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy