override fun createDslElement()

in gradle-dsl-kotlin/src/com/android/tools/idea/gradle/dsl/kotlin/KotlinDslWriter.kt [118:367]


  override fun createDslElement(element: GradleDslElement): PsiElement? {
    if (element is GradleDslInfixExpression) return createDslInfixExpression(element)
    val psiElement = element.psiElement
    if (psiElement != null) return psiElement
    var anchorAfter = element.anchor
    var isRealList = false // This is to keep track if we're creating a real list (listOf()).
    var isNamedPropertyMap = false
    var isVarOrProperty = false

    if (element.isNewEmptyBlockElement) return null  // Avoid creation of an empty block.
    if (anchorAfter == null) return null // we don't have a parent?  return null.
    val dslParent = anchorAfter.parentDslElement ?: return null

    var addBefore = false
    if (needToCreateParent(dslParent)) {
      addBefore = true
      anchorAfter = GradleDslAnchor.Start(dslParent)
    }

    var parentPsiElement = getParentPsi(dslParent) ?: return null

    val project = parentPsiElement.project
    val psiFactory = KtPsiFactory(project)

    val externalNameInfo = maybeTrimForParent(element, this)
    val joinedName = externalNameInfo.externalNameParts.joinToString(".")
    val quotedName = maybeQuoteBits(externalNameInfo.externalNameParts)
    var statementText : String
    val syntax = externalNameInfo.syntax.takeUnless { it == UNKNOWN } ?: element.externalSyntax
    element.externalSyntax = syntax
    // TODO(xof): this is a bit horrible, and if there are any other examples where we need to adjust the syntax (as opposed to name)
    //  of something depending on its context, try to figure out a useful generalization.
    if (dslParent is DependenciesDslElement && (syntax == METHOD) && !KTS_KNOWN_CONFIGURATIONS.contains(joinedName)) {
      statementText = "\"${joinedName}\""
    }
    else if (element is GradleDslNamedDomainElement) {
      statementText = when {
        // use an existing methodName if we have one
        element.methodName != null -> "${element.methodName}(\"$joinedName\")"
        // use getByName() if the element is implicitly provided, otherwise create()
        dslParent is GradleDslNamedDomainContainer -> when {
          dslParent.implicitlyExists(joinedName) -> "getByName(\"$joinedName\")"
          else -> "create(\"$joinedName\")"
        }
        // should never happen (named domain element added to something that isn't a named domain container)
        else -> {
          val log = logger<KotlinDslWriter>()
          log.warn("NamedDomainElement $element added to non-NamedDomainContainer $dslParent", Throwable())
          "getByName(\"$joinedName\")"
        }
      }
    }
    else {
      statementText = joinedName
    }

    if (element.isBlockElement) {
      if (element is MavenRepositoryDslElement && element.getContainedElements(true).isEmpty()) {
        statementText += "()"
      }
      else {
        statementText += " {\n}"  // Can't create expression with another new line after.
      }
    }
    else if (syntax == ASSIGNMENT || syntax == AUGMENTED_ASSIGNMENT || syntax == SET_METHOD) {
      if (element.elementType == PropertyType.REGULAR) {
        if (element.parent is ExtDslElement) {
          // This is about a regular extra property and should have a dedicated syntax.
          // TODO(b/148769031): For now, we need to be careful about psi to dsl translation in both ways and reflect the dsl logic back to
          //  the psi elements.
          if (element.fullName.startsWith("ext.")) statementText = "extra[\"${joinedName}\"] = \"abc\""
          else {
            statementText = "val $quotedName by extra(\"abc\")"
            isVarOrProperty = true
          }
        }
        else {
          when (syntax) {
            ASSIGNMENT -> statementText += " = \"abc\""
            AUGMENTED_ASSIGNMENT -> statementText += " += \"abc\""
            SET_METHOD -> statementText += ".set(\"abc\")" // Gradle pre-8.2 doesn't(?) provide Kotlin property setters
            METHOD -> {}
            UNKNOWN -> {}
          }
        }
      }
      else if (element.elementType == PropertyType.VARIABLE) {
        statementText = "val ${quotedName} = \"abc\""
        isVarOrProperty = true
      }
    }
    else if (element is GradleDslExpressionList) {
      if (dslParent is GradleDslMethodCall && element.elementType == PropertyType.DERIVED) {
        // This is when we have not a proper list element (listOf()) but rather a methodCall arguments. In such case we need to skip
        // creating the list and use the KtValueArgumentList of the parent.
        return (dslParent.psiElement as? KtCallExpression)?.valueArgumentList  // TODO add more tests to verify the code consistency.
      }
      else if (element.name.isEmpty()){
        // This is the case where we are handling a list element
        statementText += "listOf()"
        isRealList = true
      }
      else {
        statementText += "()"
      }
    }
    else if (element is GradleDslExpressionMap) {
      if (element.asNamedArgs) {
        statementText += "()"
      }
      else if (element.name.isEmpty()) {
        statementText += "mapOf()"
      }
      else if (element.elementType == PropertyType.DERIVED && element.isLiteralMap) {
        // This is the case of maps within other maps
        statementText = "\"${StringUtil.unquoteString(element.name)}\" to \"abc\""
      }
      else {
        statementText += "(mapOf())"
        isNamedPropertyMap = true
      }
    }
    else {
      statementText += "()"
    }

    val statement = if (isVarOrProperty) psiFactory.createProperty(statementText) else psiFactory.createExpression(statementText)
    when (statement) {
      is KtBinaryExpression -> {
        statement.right?.delete()
      }
      is KtCallExpression -> {
        if (element.isBlockElement) {
          // Add new line to separate blocks statements.
          statement.addAfter(psiFactory.createNewLine(), statement.lastChild)
        }
      }
      is KtProperty -> {
        // If we created a local variable, we need to delete the right value to allow adding the right one.
        if (element.elementType == PropertyType.VARIABLE) {
          statement.initializer?.delete()
        }
        else {
          // This is the case os an extra property, and we will need to delete the value from the extra() callExpression.
          val delegateExpression = statement.delegateExpression as? KtCallExpression ?: return null
          delegateExpression.valueArgumentList?.removeArgument(0)
        }
      }
      is KtDotQualifiedExpression -> {
        val call = statement.getChildOfType<KtCallExpression>() ?: return null
        call.valueArgumentList?.removeArgument(0)
      }
    }

    val lineTerminator = psiFactory.createNewLine()
    val addedElement : PsiElement
    var anchor = getPsiElementForAnchor(parentPsiElement, anchorAfter)

    when (parentPsiElement) {
      is KtFile -> {
        // If the anchor is null, we would add the new element to the beginning of the file which is correct, unless the file starts
        // with a comment : in such case we need to add the element right after the comment and not before.
        val fileBlock = parentPsiElement.script?.blockExpression
        if (fileBlock != null) {
          parentPsiElement = fileBlock
          anchor = getPsiElementForAnchor(parentPsiElement, anchorAfter)
        }

        val firstRealChild = fileBlock?.firstChild
        if (addBefore) {
          addedElement = parentPsiElement.addBefore(statement, anchor)
          if (!isWhiteSpaceOrNls(addedElement.prevSibling)) {
            parentPsiElement.addBefore(lineTerminator, addedElement)
          }
        }
        else if (fileBlock != null && anchor == null && firstRealChild?.node?.elementType == BLOCK_COMMENT) {
          addedElement = fileBlock.addAfter(statement, firstRealChild)
        }
        else {
          addedElement = parentPsiElement.addAfter(statement, anchor)
          if (!isWhiteSpaceOrNls(addedElement.nextSibling)) {
            parentPsiElement.addAfter(lineTerminator, addedElement)
          }
        }

        if (element.isBlockElement && !isWhiteSpaceOrNls(addedElement.prevSibling)) {
          parentPsiElement.addBefore(lineTerminator, addedElement)
        }
        if (addBefore) {
          parentPsiElement.addAfter(lineTerminator, addedElement)
        }
        else {
          parentPsiElement.addBefore(lineTerminator, addedElement)
        }
      }
      is KtBlockExpression -> {
        addedElement = parentPsiElement.addAfter(statement, anchor)
        when (anchorAfter) {
          is GradleDslAnchor.After -> parentPsiElement.addBefore(lineTerminator, addedElement)
          is GradleDslAnchor.Start -> parentPsiElement.addAfter(lineTerminator, addedElement)
        }
      }
      is KtValueArgumentList -> {
        val argumentValue = psiFactory.createArgument(statement)
        addedElement = parentPsiElement.addArgumentAfter(argumentValue, anchor as? KtValueArgument)
      }
      is KtCallExpression -> {
        val argumentList = parentPsiElement.valueArgumentList ?: return null
        val argumentValue = psiFactory.createArgument(statement)
        addedElement = argumentList.addArgumentAfter(argumentValue, anchor as? KtValueArgument)?.getArgumentExpression() ?: return null
      }
      else -> {
        addedElement = parentPsiElement.addAfter(statement, anchor)
        parentPsiElement.addBefore(lineTerminator, addedElement)
      }
    }

    if (element.isBlockElement) {
      val blockExpression = getKtBlockExpression(addedElement)
      if (blockExpression != null) {
        element.psiElement = blockExpression
      }
    }
    else if (addedElement is KtBinaryExpression) {
      addedElement.addAfter(psiFactory.createWhiteSpace(), addedElement.lastChild)
      element.psiElement = addedElement
    }
    else if (addedElement is KtCallExpression) {
      if (element is GradleDslExpressionList && !isRealList) {
        element.psiElement = addedElement.valueArgumentList
      }
      else if (element is GradleDslExpressionMap && isNamedPropertyMap) {
        element.psiElement = addedElement.valueArguments[0].getArgumentExpression()
      }
      else {
        element.psiElement = addedElement
      }
    }
    else if (addedElement is KtValueArgument) {
      element.psiElement = addedElement.getArgumentExpression()
    }
    else if (addedElement is KtProperty) {
      element.psiElement = addedElement
    }
    else if (addedElement is KtDotQualifiedExpression) {
      element.psiElement = addedElement.getChildOfType<KtCallExpression>() //?.valueArguments?.get(0)?.getArgumentExpression()
    }

    return element.psiElement
  }