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

com.blackducksoftware.integration.hub.detect.bomtool.gradle.GradleDependenciesParser.groovy Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2017 Black Duck Software, Inc.
 * http://www.blackducksoftware.com/
 *
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package com.blackducksoftware.integration.hub.detect.bomtool.gradle

import org.apache.commons.lang3.StringUtils
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component

import com.blackducksoftware.integration.hub.bdio.graph.MutableDependencyGraph
import com.blackducksoftware.integration.hub.bdio.graph.MutableMapDependencyGraph
import com.blackducksoftware.integration.hub.bdio.model.dependency.Dependency
import com.blackducksoftware.integration.hub.bdio.model.externalid.ExternalId
import com.blackducksoftware.integration.hub.bdio.model.externalid.ExternalIdFactory
import com.blackducksoftware.integration.hub.detect.model.BomToolType
import com.blackducksoftware.integration.hub.detect.model.DetectCodeLocation
import com.blackducksoftware.integration.hub.detect.model.DetectProject

import groovy.transform.TypeChecked

@TypeChecked
@Component
class GradleDependenciesParser {
    private final Logger logger = LoggerFactory.getLogger(GradleDependenciesParser.class)

    static final String DEPENDENCY_INDICATOR = '+---'
    static final String LAST_CHILD_INDICATOR = '\\---'
    static final String COMPONENT_PREFIX = '--- '
    static final String SEEN_ELSEWHERE_SUFFIX = ' (*)'
    static final String WINNING_INDICATOR = ' -> '

    String rootProjectSourcePath = ""
    String rootProjectGroup  = ""
    String rootProjectName  = ""
    String rootProjectVersionName  = ""

    String projectSourcePath = ""
    String projectGroup = ""
    String projectName = ""
    String projectVersionName = ""

    public ExternalIdFactory externalIdFactory;

    public GradleDependenciesParser(ExternalIdFactory externalIdFactory){
        this.externalIdFactory = externalIdFactory;
    }

    DetectCodeLocation parseDependencies(DetectProject detectProject, InputStream dependenciesInputStream) {
        //DependencyNode tempRoot = new DependencyNode("project", "version", externalIdFactory.createMavenExternalId("group", "project", "version"))

        MutableDependencyGraph graph = new MutableMapDependencyGraph();
        //DependencyNodeBuilder dependencyNodeBuilder = new DependencyNodeBuilder(tempRoot)
        boolean processingMetaData = false
        boolean processingConfiguration = false
        String configurationName = null
        String previousLine = null
        Stack nodeStack = new Stack<>()
        Dependency previousNode = null
        int treeLevel = 0

        dependenciesInputStream.eachLine("UTF-8") { line ->
            /**
             * The meta data section will be at the end of the file after all of the 'gradle dependencies' output
             */
            if (line.startsWith('DETECT META DATA START')) {
                processingMetaData = true
                return
            }
            if (line.startsWith('DETECT META DATA END')) {
                processingMetaData = false
                return
            }
            if (processingMetaData) {
                processMetaDataLine(line)
                return
            }

            if (StringUtils.isBlank(line)) {
                processingConfiguration = false
                configurationName = null
                previousLine = null
                nodeStack = new Stack()
                previousNode = null
                treeLevel = 0
                return
            }
            if (!processingConfiguration && line.startsWith(DEPENDENCY_INDICATOR)) {
                processingConfiguration = true
                configurationName = previousLine
                if (previousLine.contains(' -  ')) {
                    configurationName = previousLine.substring(0, previousLine.indexOf(' - ')).trim()
                } else {
                    configurationName = previousLine.trim()
                }
                logger.info("processing of configuration ${configurationName} started")
            }
            if (!processingConfiguration) {
                previousLine = line
                return
            }

            Dependency lineNode = createDependencyNodeFromOutputLine(line)
            if (lineNode == null) {
                previousLine = line
                return
            }

            int lineTreeLevel = getLineLevel(line)
            if (lineTreeLevel == treeLevel + 1) {
                nodeStack.push(previousNode)
            } else if (lineTreeLevel < treeLevel) {
                (treeLevel - lineTreeLevel).times { nodeStack.pop() }
            }  else if (lineTreeLevel != treeLevel) {
                logger.error "The tree level (${treeLevel}) and this line (${line}) with count ${lineTreeLevel} can't be reconciled."
            }
            if (nodeStack.size() == 0){
                graph.addChildToRoot(lineNode);
            }else{
                graph.addChildWithParents(lineNode, [nodeStack.peek()])
            }
            previousNode = lineNode
            treeLevel = lineTreeLevel
            previousLine = line
        }

        detectProject.setProjectNameIfNotSet(rootProjectName)
        detectProject.setProjectVersionNameIfNotSet(rootProjectVersionName)

        ExternalId id = externalIdFactory.createMavenExternalId(projectGroup, projectName, projectVersionName);
        new DetectCodeLocation(BomToolType.GRADLE, projectSourcePath, projectName, projectVersionName,
                id, graph)
    }

    public int getLineLevel(String line) {
        if (line.startsWith(DEPENDENCY_INDICATOR) || line.startsWith(LAST_CHILD_INDICATOR)) {
            return 0
        }
        String modifiedLine = ""
        int indexToCut = line.length()
        if (line.contains(DEPENDENCY_INDICATOR)) {
            indexToCut =line.indexOf(DEPENDENCY_INDICATOR)
        } else if (line.contains(LAST_CHILD_INDICATOR)) {
            indexToCut =line.indexOf(LAST_CHILD_INDICATOR)
        }
        modifiedLine = line.substring(0, indexToCut)
        if (!modifiedLine.startsWith('|')) {
            modifiedLine = '|' + modifiedLine
        }
        modifiedLine = modifiedLine.replace('     ', '    |')
        modifiedLine = modifiedLine.replace('||', '|')
        if (modifiedLine.endsWith('|')) {
            modifiedLine = modifiedLine.substring(0, modifiedLine.length() - 5)
        }
        int matches = StringUtils.countMatches(modifiedLine, '|')

        return matches
    }

    Dependency createDependencyNodeFromOutputLine(String outputLine) {
        if (StringUtils.isBlank(outputLine) || !outputLine.contains(COMPONENT_PREFIX)) {
            return null
        }

        String cleanedOutput = StringUtils.trimToEmpty(outputLine)
        cleanedOutput = cleanedOutput.substring(cleanedOutput.indexOf(COMPONENT_PREFIX) + COMPONENT_PREFIX.length())
        if (cleanedOutput.endsWith(SEEN_ELSEWHERE_SUFFIX)) {
            cleanedOutput = cleanedOutput[0..(-1 * (SEEN_ELSEWHERE_SUFFIX.length() + 1))]
        }

        String[] gav = cleanedOutput.split(':')
        if (cleanedOutput.contains(WINNING_INDICATOR)) {
            // WINNING_INDICATOR can point to an entire GAV not just a version
            String winningSection = cleanedOutput.substring(cleanedOutput.indexOf(WINNING_INDICATOR) + WINNING_INDICATOR.length())
            if (winningSection.contains(':')) {
                gav = winningSection.split(':')
            } else {
                gav[2] = winningSection
            }
        }
        if (gav.length != 3) {
            logger.error "The line can not be reasonably split in to the neccessary parts: ${outputLine}"
            return null
        }
        String group = gav[0]
        String artifact = gav[1]
        String version = gav[2]

        new Dependency(artifact, version, externalIdFactory.createMavenExternalId(group, artifact, version))
    }

    private void processMetaDataLine(String metaDataLine) {
        if (metaDataLine.startsWith('rootProjectPath:')) {
            rootProjectSourcePath = metaDataLine.substring('rootProjectPath:'.length()).trim()
        } else if (metaDataLine.startsWith('rootProjectGroup:')) {
            rootProjectGroup = metaDataLine.substring('rootProjectGroup:'.length()).trim()
        } else if (metaDataLine.startsWith('rootProjectName:')) {
            rootProjectName = metaDataLine.substring('rootProjectName:'.length()).trim()
        } else if (metaDataLine.startsWith('rootProjectVersion:')) {
            rootProjectVersionName = metaDataLine.substring('rootProjectVersion:'.length()).trim()
        } else if (metaDataLine.startsWith('projectPath:')) {
            projectSourcePath = metaDataLine.substring('projectPath:'.length()).trim()
        } else if (metaDataLine.startsWith('projectGroup:')) {
            projectGroup = metaDataLine.substring('projectGroup:'.length()).trim()
        } else if (metaDataLine.startsWith('projectName:')) {
            projectName = metaDataLine.substring('projectName:'.length()).trim()
        } else if (metaDataLine.startsWith('projectVersion:')) {
            projectVersionName = metaDataLine.substring('projectVersion:'.length()).trim()
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy