/********************************************************************************
 * Copyright (C) 2018 Red Hat, Inc. and others.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the Eclipse
 * Public License v. 2.0 are satisfied: GNU General Public License, version 2
 * with the GNU Classpath Exception which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 ********************************************************************************/ import { injectable , inject } from '@theia/core/shared/inversify' ; import { VariableRegistry } from './variable' ; import URI from '@theia/core/lib/common/uri' ; import { JSONExt , ReadonlyJSONValue } from '@theia/core/shared/@phosphor/coreutils' ; export interface VariableResolveOptions { context ? : URI ; /**
     * Used for resolving inputs, see https://code.visualstudio.com/docs/editor/variables-reference#_input-variables
     */ configurationSection ? : string ; } /**
 * The variable resolver service should be used to resolve variables in strings.
 */ @ injectable ( ) export class VariableResolverService { protected static VAR_REGEXP = /\$\{(.*?)\}/g ; @ inject ( VariableRegistry ) protected readonly variableRegistry : VariableRegistry ; param value param options returns resolveArray ( value , options = { } ) { return this . resolve ( value , options ) ; } param value param options returns async resolve < T > ( value , options = { } ) { const context = new VariableResolverService . Context ( this . variableRegistry , options ) ; const resolved = await this . doResolve ( value , context ) ; return resolved as any ; } protected async doResolve ( value , context ) { if ( value === undefined || value === null ) { return value ; } if ( typeof value === 'string' ) { return this . doResolveString ( value , context ) ; } if ( Array . isArray ( value ) ) { return this . doResolveArray ( value , context ) ; } if ( typeof value === 'object' ) { return this . doResolveObject ( value , context ) ; } return value ; } protected async doResolveObject ( obj , context ) { const result = { } ; for ( const name of Object . keys ( obj ) ) { const value = ( obj as any ) [ name ] ; const resolved = await this . doResolve ( value , context ) ; result [ name ] = resolved ; } return result ; } protected async doResolveArray ( values , context ) { const result = [ ] ; for ( const value of values ) { const resolved = await this . doResolve ( value , context ) ; result . push ( resolved ) ; } return result ; } protected async doResolveString ( value , context ) { await this . resolveVariables ( value , context ) ; return value . replace ( VariableResolverService . VAR_REGEXP , ( match , varName ) => { const varValue = context . get ( varName ) ; return varValue !== undefined ? varValue : match ; } ) ; } protected async resolveVariables ( value , context ) { const variableRegExp = new RegExp ( VariableResolverService . VAR_REGEXP ) ; let match ; while ( ( match = variableRegExp . exec ( value ) ) !== null ) { const variableName = match [ 1 ] ; await context . resolve ( variableName ) ; } } } export namespace VariableResolverService { export class Context { protected readonly resolved = new Map < string , string | undefined > ( ) ; constructor ( protected readonly variableRegistry , protected readonly options ) { } get ( name ) { return this . resolved . get ( name ) ; } async resolve ( name ) { if ( this . resolved . has ( name ) ) { return ; } try { let variableName = name ; let argument ; const parts = name . split ( ':' ) ; if ( parts . length > 1 ) { variableName = parts [ 0 ] ; argument = parts [ 1 ] ; } const variable = this . variableRegistry . getVariable ( variableName ) ; const value = variable && await variable . resolve ( this . options . context , argument , this . options . configurationSection ) ; const stringValue = value !== undefined && value !== null && JSONExt . isPrimitive ( value as ReadonlyJSONValue ) ? String ( value ) : undefined ; this . resolved . set ( name , stringValue ) ; } catch ( e ) { console . error ( `Failed to resolved '${ name }' variable` , e ) ; this . resolved . set ( name , undefined ) ; } } } }