import * as exec from 'child_process' ; import { ComponentDetails } from 'jfrog-client-js' ; import * as path from 'path' ; import Set from 'typescript-collections/dist/lib/Set' ; import * as vscode from 'vscode' ; import { ContextKeys } from '../constants/contextKeys' ; import { FocusType } from '../focus/abstractFocus' ; import { LogManager } from '../log/logManager' ; import { MavenTreeNode } from '../treeDataProviders/dependenciesTree/dependenciesRoot/mavenTree' ; import { DependenciesTreeNode } from '../treeDataProviders/dependenciesTree/dependenciesTreeNode' ; import { TreesManager } from '../treeDataProviders/treesManager' ; import { PomTree } from './pomTree' ; import { ScanUtils } from './scanUtils' ; export class MavenUtils { public static readonly DOCUMENT_SELECTOR : any = { scheme : 'file' , pattern : '**/pom.xml' } ; public static readonly MAVEN_GAV_READER : string = path . join ( ScanUtils . RESOURCES_DIR , 'maven-gav-reader.jar' ) ; public static readonly PKG_TYPE : string = 'maven' ; private static mavenGavReaderInstalled : boolean ; static pathToNode : Map < string , MavenTreeNode > = new Map < string , MavenTreeNode > ( ) ; param document public static getDependenciesPos ( document ) { let res = [ ] ; let mavenPomContent = document . getText ( ) ; let dependenciesMatch = mavenPomContent . match ( '<dependencies>' ) ; if ( ! dependenciesMatch ) { return res ; } res . push ( document . positionAt ( < number > dependenciesMatch . index ) ) ; res . push ( new vscode . Position ( res [ 0 ] . line , res [ 0 ] . character + dependenciesMatch [ 0 ] . length ) ) ; return res ; } param document param dependenciesTreeNode public static getDependencyPos ( document , dependenciesTreeNode , focusType ) { if ( ! dependenciesTreeNode ) { return [ ] ; } let res = [ ] ; let pomXmlContent = document . getText ( ) ; let [ groupId , artifactId , version ] = MavenUtils . getGavArray ( dependenciesTreeNode ) ; let dependencyTag = MavenUtils . getDependencyTag ( pomXmlContent , groupId , artifactId ) ; if ( dependencyTag ) { let startIndex = document . positionAt ( pomXmlContent . indexOf ( dependencyTag ) ) ; let arr = dependencyTag . split ( /\r?\n/ ) . filter ( line => line . trim ( ) !== '' ) ; for ( let i = 0 ; i < arr . length ; i ++ ) { let depInfo = arr [ i ] . trim ( ) . toLowerCase ( ) ; if ( this . isDependencyMatch ( groupId , artifactId , version , depInfo , focusType ) ) { res . push ( new vscode . Position ( startIndex . line + i , arr [ i ] . indexOf ( '<' ) ) ) ; res . push ( new vscode . Position ( startIndex . line + i , arr [ i ] . length ) ) ; } } return res ; } if ( ! ( dependenciesTreeNode instanceof MavenTreeNode ) ) { return MavenUtils . getDependencyPos ( document , dependenciesTreeNode . parent , focusType ) ; } return [ ] ; } public static isDependencyMatch ( groupId , artifactId , version , depInfo , focusType ) { switch ( focusType ) { case FocusType . Dependency : return ( depInfo === '<groupid>' + groupId + '</groupid>' || depInfo === '<artifactid>' + artifactId + '</artifactid>' || depInfo === '<version>' + version + '</version>' ) ; case FocusType . DependencyVersion : return depInfo === '<version>' + version + '</version>' ; } } param pomXmlContent param groupId param artifactId public static getDependencyTag ( pomXmlContent , groupId , artifactId ) { let dependencyMatch = pomXmlContent . match ( /<dependency>(.|\s)*?<\/dependency>/gi ) ?. filter ( group => group . includes ( groupId ) && group . includes ( artifactId ) ) ; if ( dependencyMatch && dependencyMatch . length > 0 ) { return dependencyMatch [ 0 ] ; } return '' ; } param dependenciesTreeNode public static getGavArray ( dependenciesTreeNode ) { return dependenciesTreeNode . generalInfo . getComponentId ( ) . toLowerCase ( ) . split ( ':' ) ; } return public static getPomDetails ( pathToPomXml , logManager , pomIdCache ) { let gav = pomIdCache . get ( pathToPomXml ) ; if ( ! ! gav ) { return gav ; } try { let mvnGavRes = ScanUtils . executeCmd ( 'mvn com.jfrog.ide:maven-gav-reader:gav -q' , path . dirname ( pathToPomXml ) ) ; mvnGavRes . toString ( ) . split ( /\r\n|\r|\n/ ) . filter ( mvnGav => ! ! mvnGav ) . map ( mvnGav => mvnGav . replace ( /\\/g , '\\\\' ) ) . forEach ( mvnGav => { let mvnGavJson = JSON . parse ( mvnGav ) ; let pomXmlPath = mvnGavJson [ 'pomPath' ] ; let gav = mvnGavJson [ 'gav' ] ; let parentGav = mvnGavJson [ 'parentGav' ] ; pomIdCache . set ( pomXmlPath , [ gav , parentGav ] ) ; } ) ; return pomIdCache . get ( pathToPomXml ) || [ '' , '' ] ; } catch ( error ) { logManager . logMessage ( 'Could not get parse pom.xml GAV.\n' + 'Try Install it by running "mvn clean install" from ' + pathToPomXml + '.' , 'ERR' ) ; logManager . logMessage ( ( < any > error ) . stdout ?. toString ( ) . replace ( /(\[.*?\])/g , '' ) , 'ERR' ) ; } return [ '' , '' ] ; } param pomXmls param componentsToScan param treesManager param root param quickScan public static async createDependenciesTrees ( pomXmls , componentsToScan , treesManager , root , quickScan ) { if ( ! pomXmls ) { treesManager . logManager . logMessage ( 'No pom.xml files found in workspaces.' , 'DEBUG' ) ; return ; } treesManager . logManager . logMessage ( 'pom.xml files to scan: [' + pomXmls . toString ( ) + ']' , 'DEBUG' ) ; if ( ! MavenUtils . verifyMavenInstalled ( ) ) { vscode . window . showErrorMessage ( 'Could not scan Maven project dependencies, because "mvn" is not in the PATH.' ) ; return ; } treesManager . logManager . logMessage ( 'Generating Maven Dependency Tree' , 'INFO' ) ; let prototypeTree = MavenUtils . buildPrototypePomTree ( pomXmls , treesManager . logManager ) ; for ( let ProjectTree of prototypeTree ) { try { treesManager . logManager . logMessage ( 'Analyzing pom.xml at ' + ProjectTree . pomPath , 'INFO' ) ; ProjectTree . runMavenDependencyTree ( ) ; let dependenciesTreeNode = new MavenTreeNode ( ProjectTree . pomPath , componentsToScan , treesManager , root ) ; await dependenciesTreeNode . refreshDependencies ( quickScan , ProjectTree ) ; if ( dependenciesTreeNode . children . length === 0 ) { root . children . splice ( root . children . indexOf ( dependenciesTreeNode ) , 1 ) ; } else { this . updateContextValue ( dependenciesTreeNode ) ; } } catch ( error ) { treesManager . logManager . logMessage ( 'Could not get dependencies tree from pom.xml.\n' + 'Try Install it by running "mvn clean install" from ' + ProjectTree . pomPath + '.' , 'ERR' ) ; treesManager . logManager . logMessage ( ( < any > error ) . stdout ?. toString ( ) . replace ( /(\[.*?\])/g , '' ) , 'ERR' ) ; } } } param node public static async updateContextValue ( node , dependenciesMatch ? , parentCanReachPom ? ) { let nodeCanReachPom = this . isPomReachable ( node , dependenciesMatch , parentCanReachPom ) ; if ( ! nodeCanReachPom ) { node . contextValue ?. replace ( ContextKeys . SHOW_IN_PROJECT_DESC_ENABLED , '' ) ; } else if ( ! ( node . parent instanceof MavenTreeNode ) && node . parent ?. label ) { node . contextValue += ContextKeys . EXCLUDE_DEPENDENCY_ENABLED ; } if ( node instanceof MavenTreeNode ) { const text = ( await MavenUtils . openPomXml ( node ) ) ?. getText ( ) ; dependenciesMatch = text ?. match ( /<dependency>(.|\s)*?<\/dependency>/gi ) ; } node . children . forEach ( async ( c ) => await this . updateContextValue ( c , dependenciesMatch , nodeCanReachPom ) ) ; } param node param pomDependencies param parentCanReachPom private static isPomReachable ( node , pomDependencies ? , parentCanReachPom ? ) { if ( node instanceof MavenTreeNode ) { return true ; } if ( this . isNodeInPom ( node , pomDependencies ) ) { return true ; } return ! this . isNodeDirectDependency ( node ) && ! ! parentCanReachPom ; } private static isNodeDirectDependency ( node ) { return node . parent instanceof MavenTreeNode ; } private static isNodeInPom ( node , pomDependencies ? ) { if ( ! pomDependencies || pomDependencies . length === 0 ) { return false ; } const [ groupId , artifactId ] = node . generalInfo . getComponentId ( ) . toLowerCase ( ) . split ( ':' ) ; if ( groupId === '' || artifactId === '' ) { return false ; } let dependencyMatch = pomDependencies ?. filter ( group => group . includes ( groupId ) && group . includes ( artifactId ) ) ; return ! ! dependencyMatch && dependencyMatch . length > 0 ; } param dependenciesTreeNode public static async openPomXml ( dependenciesTreeNode ) { while ( dependenciesTreeNode . parent && dependenciesTreeNode instanceof MavenTreeNode === false ) { dependenciesTreeNode = dependenciesTreeNode . parent ; } let fsPath = ( < MavenTreeNode > dependenciesTreeNode ) . workspaceFolder ; if ( ! fsPath ) { return ; } let openPath = vscode . Uri . file ( path . join ( fsPath , 'pom.xml' ) ) ; if ( ! openPath ) { return ; } return await vscode . workspace . openTextDocument ( openPath ) ; } param pomArray param logManager public static buildPrototypePomTree ( pomArray , logManager ) { let prototypeTree = [ ] ; let pomIdCache = new Map < string , [ string , string ] > ( ) ; if ( ! MavenUtils . mavenGavReaderInstalled ) { MavenUtils . installMavenGavReader ( ) ; } pomArray . sort ( ( pomPath1 , pomPath2 ) => pomPath1 . fsPath . length - pomPath2 . fsPath . length ) . forEach ( pom => { const [ pomGav , parentGav ] = MavenUtils . getPomDetails ( pom . fsPath , logManager , pomIdCache ) ; if ( ! ! pomGav ) { let index = MavenUtils . searchPomGav ( prototypeTree , pomGav ) ; let currNode ; if ( index > - 1 ) { currNode = prototypeTree [ index ] ; prototypeTree . splice ( index , 1 ) ; } else { currNode = new PomTree ( pomGav ) ; } currNode . pomPath = path . dirname ( pom . fsPath ) ; currNode . parentGav = parentGav ; MavenUtils . addPrototypeNode ( prototypeTree , currNode ) ; } } ) ; for ( let i = 0 ; i < prototypeTree . length ; i ++ ) { if ( ! prototypeTree [ i ] . pomPath ) { const oldRoot = prototypeTree . splice ( i , 1 ) ; prototypeTree . push ( ... oldRoot [ 0 ] . children ) ; } } return prototypeTree ; } param pomArray param node static addPrototypeNode ( pomArray , node ) { if ( ! ! node . parentGav ) { const parentNode = MavenUtils . getPrototypeNode ( pomArray , node . parentGav ) ; if ( ! ! parentNode ) { parentNode . addChild ( node ) ; } else { const parentPom = new PomTree ( node . parentGav ) ; parentPom . addChild ( node ) ; pomArray . push ( parentPom ) ; } } else { pomArray . push ( node ) ; } } public static verifyMavenInstalled ( ) { try { exec . execSync ( 'mvn -version' ) ; } catch ( error ) { return false ; } return true ; } public static getPrototypeNode ( prototypeTreeArray , pomGav ) { for ( const prototypeTree of prototypeTreeArray ) { const node = prototypeTree . deepSearch ( pomGav ) ; if ( node ) { return node ; } } return ; } public static searchPomGav ( pomTreeArray , pomGav ) { return pomTreeArray . findIndex ( pomTree => pomTree . pomGav === pomGav ) ; } param rawDependency public static getProjectInfo ( rawDependency ) { return MavenUtils . getDependencyInfo ( rawDependency . replace ( /\s/g , '' ) + ':dummyScope' ) ; } param rawDependency returns public static getDependencyInfo ( rawDependency ) { let result = rawDependency . split ( ':' ) ; let startIndex = result [ 0 ] . search ( /\w/ ) ; return [ result [ 0 ] . slice ( startIndex ) , result [ 1 ] , result [ result . length - 2 ] , result [ result . length - 1 ] . split ( ' ' ) [ 0 ] ] ; } public static filterParentDependencies ( childDependencies , parentDeps ? ) { if ( parentDeps ) { const rawParentDep = parentDeps . join ( ' ' ) ; return childDependencies . filter ( childDep => { const index = childDep . search ( /\w/ ) ; const regex = new RegExp ( `^.*${ childDep . slice ( index ) }.*$` , 'mg' ) ; return ! ! rawParentDep . match ( regex ) === false ; } ) ; } return ; } /**
     * Install Maven GAV Reader to maven local repository.
     */ public static installMavenGavReader ( ) { ScanUtils . executeCmd ( 'mvn org.apache.maven.plugins:maven-install-plugin:2.5.2:install-file -Dfile=' + MavenUtils . MAVEN_GAV_READER ) ; MavenUtils . mavenGavReaderInstalled = true ; } }