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

com.nawforce.apexlink.deps.MaxDependencyCountParser.scala Maven / Gradle / Ivy

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

package com.nawforce.apexlink.deps

import com.nawforce.apexlink.api.Org
import com.nawforce.apexlink.deps.MaxDependencyCountParser.{maxCountMarker, maxCountMarkerLength}
import com.nawforce.apexlink.types.apex.ApexDeclaration
import com.nawforce.apexlink.types.core.TypeId
import io.github.apexdevtools.apexparser.ApexLexer
import com.nawforce.runtime.parsers.CodeParser
import org.antlr.v4.runtime.{CommonTokenStream, Token}

import scala.collection.mutable
import scala.jdk.CollectionConverters.ListHasAsScala
import scala.util.{Failure, Success, Try}

class MaxDependencyCountParser(org: Org) {
  def count(typeId: TypeId): Either[Option[String], Int] = {
    typeId.toTypeDeclaration[ApexDeclaration] match {
      case Some(td) => count(td)
      case None     => Left(Some(s"Cannot resolve type ${typeId.typeName.toString}"))
    }

  }

  def count(td: ApexDeclaration): Either[Option[String], Int] = {
    val dependencyLimitParseExceptions = mutable.Queue[String]()

    def parseTokenToDependencyLimit(t: Token): Option[Int] = {
      getDependencyLimit(t) match {
        case Right(result) => Some(result)
        case Left(value) =>
          if (value.nonEmpty)
            dependencyLimitParseExceptions.enqueue(value.get)
          None
      }
    }

    val sourcePath   = td.location.path
    val defaultCount = org.getProjectConfig().flatMap(_.maxDependencyCount)
    sourcePath.readSourceData() match {
      case Right(source) =>
        if (source.asString.indexOf(maxCountMarker) == -1)
          return if (defaultCount.isEmpty) Left(None) else Right(defaultCount.get)
        val parser      = CodeParser(sourcePath, source)
        val tokenStream = new CommonTokenStream(new ApexLexer(parser.cis))
        tokenStream.fill()

        val tokensR = tokenStream.getHiddenTokensToRight(0).asScala
        val countsR = tokensR.flatMap(t => parseTokenToDependencyLimit(t))
        val tokensL = tokenStream.getHiddenTokensToLeft(1).asScala
        val countsL = tokensL.flatMap(t => parseTokenToDependencyLimit(t))

        val counts = countsL ++ countsR
        if (counts.isEmpty && dependencyLimitParseExceptions.isEmpty) Left(None)
        else if (dependencyLimitParseExceptions.nonEmpty)
          Left(Some(dependencyLimitParseExceptions.last))
        else Right(counts.max)
      case Left(err) => Left(Some(err))
    }
  }

  private def getDependencyLimit(token: Token): Either[Option[String], Int] = {
    val tokenText = token.getText.filterNot((x: Char) => x.isWhitespace)
    val start     = tokenText.indexOf(maxCountMarker)
    if (start == -1) return Left(None)
    val end = tokenText.indexOf(")", start + maxCountMarkerLength)
    if (end == -1) return Left(None)
    val text = tokenText.substring(start + maxCountMarkerLength, end)

    Try(text.toInt) match {
      case Success(value) if value >= 0 => Right(value)
      case Success(_)                   => Left(Some(s"'$text' must be >=0"))
      case Failure(_)                   => Left(Some(s"'$text' is not an integer value"))
    }
  }
}

object MaxDependencyCountParser {
  private final val maxCountMarker       = "MaxDependencyCount("
  private final val maxCountMarkerLength = maxCountMarker.length
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy