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

io.github.pixee.maven.operator.POMScanner.kt Maven / Gradle / Ivy

package io.github.pixee.maven.operator

import org.dom4j.Element
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.io.File
import java.nio.file.Path
import java.nio.file.Paths
import java.util.*
import kotlin.io.path.notExists

object POMScanner {
    private val LOGGER: Logger = LoggerFactory.getLogger(POMScanner::class.java)

    private val RE_WINDOWS_PATH = Regex("""^\p{Alpha}:""")

    @JvmStatic
    fun scanFrom(originalFile: File, topLevelDirectory: File): ProjectModelFactory {
        val originalDocument = ProjectModelFactory.load(originalFile)

        val parentPoms: List = try {
            getParentPoms(originalFile)
        } catch (e: Exception) {
            LOGGER.warn("While trying embedder: ", e)

            return legacyScanFrom(originalFile, topLevelDirectory)
        }

        return originalDocument
            .withParentPomFiles(parentPoms.map { POMDocumentFactory.load(it) })
    }

    @JvmStatic
    fun legacyScanFrom(originalFile: File, topLevelDirectory: File): ProjectModelFactory {
        val pomFile: POMDocument = POMDocumentFactory.load(originalFile)
        val parentPomFiles: MutableList = arrayListOf()

        val pomFileQueue: Queue = LinkedList()

        val relativePathElement =
            pomFile.pomDocument.rootElement.element("parent")?.element("relativePath")

        if (relativePathElement != null && relativePathElement.textTrim.isNotEmpty()) {
            pomFileQueue.add(relativePathElement)
        }

        var lastFile: File = originalFile

        fun resolvePath(baseFile: File, relativePath: String): Path {
            var parentDir = baseFile

            if (parentDir.isFile) {
                parentDir = parentDir.parentFile
            }

            val result = File(File(parentDir, relativePath).toURI().normalize().path)

            lastFile = if (result.isDirectory) {
                result
            } else {
                result.parentFile
            }

            return Paths.get(result.absolutePath)
        }

        val prevPaths: MutableSet = linkedSetOf()

        while (pomFileQueue.isNotEmpty()) {
            val relativePathElement = pomFileQueue.poll()

            if (relativePathElement.textTrim.isEmpty()) {
                break
            }

            val relativePath = fixPomRelativePath(relativePathElement.text)

            if (!isRelative(relativePath))
                throw InvalidPathException(pomFile.file, relativePath)

            if (prevPaths.contains(relativePath)) {
                throw InvalidPathException(pomFile.file, relativePath, loop = true)
            } else {
                prevPaths.add(relativePath)
            }

            val newPath = resolvePath(lastFile, relativePath)

            if (newPath.notExists())
                throw InvalidPathException(pomFile.file, relativePath)

            if (!newPath.startsWith(topLevelDirectory.absolutePath))
                throw InvalidPathException(pomFile.file, relativePath)

            val newPomFile = POMDocumentFactory.load(newPath.toFile())

            parentPomFiles.add(newPomFile)

            val newRelativePathElement =
                newPomFile.pomDocument.rootElement.element("parent")?.element("relativePath")

            if (newRelativePathElement != null) {
                pomFileQueue.add(newRelativePathElement)
            }
        }

        return ProjectModelFactory.loadFor(
            pomFile = pomFile,
            parentPomFiles = parentPomFiles
        )
    }

    private fun fixPomRelativePath(text: String?): String {
        if (null == text)
            return ""

        val name = File(text).name

        if (-1 == name.indexOf(".")) {
            return "$text/pom.xml"
        }

        return text
    }

    private fun isRelative(path: String): Boolean {
        if (path.matches(RE_WINDOWS_PATH)) {
            return false
        }

        return !(path.startsWith("/") || path.startsWith("~"))
    }


    private fun getParentPoms(originalFile: File): List {
        val embedderFacadeResponse = EmbedderFacade.invokeEmbedder(
            EmbedderFacadeRequest(offline = true, pomFile = originalFile)
        )

        val res = embedderFacadeResponse.modelBuildingResult

        val rawModels = res.modelIds.map { res.getRawModel(it) }.toList()

        val parentPoms: List =
            if (rawModels.size > 1) {
                rawModels.subList(1, rawModels.size).mapNotNull { it.pomFile }.toList()
            } else
                emptyList()
        return parentPoms
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy