Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Augment xpath/dpath validation #884

Merged
merged 1 commit into from
Dec 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 45 additions & 50 deletions src/language/semantics/dfdlExt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,10 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { EOL } from 'os'
import * as vscode from 'vscode'
import {
XPathLexer,
ExitCondition,
LexPosition,
Token,
BaseToken,
} from './xpLexer'
import {
XslLexer,
GlobalInstructionData,
GlobalInstructionType,
} from './xslLexer'
import { XPathLexer, ExitCondition, LexPosition, Token } from './xpLexer'
import { XslLexer } from './xslLexer'
import { XsltTokenDiagnostics } from './xsltTokenDiagnostics'
import { DocumentChangeHandler } from './documentChangeHandler'

Expand Down Expand Up @@ -53,40 +44,13 @@ export class XPathSemanticTokensProvider
this.collection = collection
}

private static globalInstructionData: GlobalInstructionData[] = []

public static getGlobalInstructionData() {
return XPathSemanticTokensProvider.globalInstructionData
}

public static setVariableNames = (names: string[]) => {
const data: GlobalInstructionData[] = []

names.forEach((name) => {
const token: BaseToken = {
line: 1,
startCharacter: 0,
length: 1,
value: name,
tokenType: 0,
}
const variableInstruction: GlobalInstructionData = {
type: GlobalInstructionType.Variable,
name: name,
token: token,
idNumber: 0,
}
data.push(variableInstruction)
})
XPathSemanticTokensProvider.globalInstructionData = data
}

async provideDocumentSemanticTokens(
document: vscode.TextDocument,
token: vscode.CancellationToken
): Promise<vscode.SemanticTokens> {
this.xpLexer.documentTokens = []
let variables: string[] = this.findAllVariables(document.getText())
let [variables, namespaces]: [string[], string[]] =
this.findAllVariablesandNamespaces(document.getText())
let tokens: Token[] = []

const tokenPositions = this.findAllXPath(document.getText())
Expand Down Expand Up @@ -118,7 +82,10 @@ export class XPathSemanticTokensProvider

// This was moved to inside the loop. If it isn't, the sections of XPath will be treated
// as a single XPath section instead of multiples
setTimeout(() => this.reportProblems(tmpTokens, document, variables), 0)
setTimeout(
() => this.reportProblems(tmpTokens, document, variables, namespaces),
0
)

// Reset the xpLexer. If this is not done, existing tokens will not be flushed
// and will be re-added to the tokens list. This might not affect the operation, but it does
Expand Down Expand Up @@ -201,14 +168,27 @@ export class XPathSemanticTokensProvider
}

// Find the names of all variables in the file
private findAllVariables(document: String | undefined): string[] {
private findAllVariablesandNamespaces(
document: String | undefined
): [string[], string[]] {
if (document === undefined) {
return []
return [[], []]
}

const lines = document.split('\n')
const lines = document.split(EOL)

const variableRegex = /(dfdl:defineVariable.*name=\")(.*?)\"/
const variables: string[] = []
// These are built-in predefined variables. Double check if we still need to hardcode these
// once we have the ability to create a DFDL model outside of a Parse operation.
const variables: string[] = [
'dfdl:encoding',
'dfdl:byteOrder',
'dfdl:binaryFloatRep',
'dfdl:outputNewLine',
]

const namespaceRegex = /xmlns:(.*?)=\"/g
const namespaces: string[] = []

// Capture and return a list of variable names
for (let i = 0; i < lines.length; i++) {
Expand All @@ -217,24 +197,39 @@ export class XPathSemanticTokensProvider
if (variableMatch) {
variables.push(variableMatch[2])
}

// If a DFDL schema has multiple namespaces defined on the same line, we need to make sure we are globally
// matching for the namespace pattern so that we don't miss any
let namespaceMatch: RegExpExecArray | null = null

// Continue matching until we don't find any more namespaces on the same line
do {
namespaceMatch = namespaceRegex.exec(lines[i])

if (namespaceMatch) {
namespaces.push(namespaceMatch[1])
}
} while (namespaceMatch)
}

return variables
return [variables, namespaces]
}

// This function will produce the error/warning list for vscode
private reportProblems(
allTokens: Token[],
document: vscode.TextDocument,
variables: string[]
variables: string[],
namespaces: string[]
) {
let diagnostics = XsltTokenDiagnostics.calculateDiagnostics(
document,
allTokens,
DocumentChangeHandler.lastXMLDocumentGlobalData,
XPathSemanticTokensProvider.globalInstructionData,
[],
variables
[],
variables,
namespaces
)
diagnostics.forEach((diag) => {
this.diagnosticList.push(diag)
Expand Down
1 change: 1 addition & 0 deletions src/language/semantics/functionData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export class FunctionData {
'collection#1',
'compare#2',
'compare#3',
'concat#2',
'concat#3',
'contains#2',
'contains#3',
Expand Down
18 changes: 14 additions & 4 deletions src/language/semantics/xsltTokenDiagnostics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,8 @@ export class XsltTokenDiagnostics {
globalInstructionData: GlobalInstructionData[],
importedInstructionData: GlobalInstructionData[],
symbols: vscode.DocumentSymbol[],
globalVariables: string[]
globalVariables: string[],
xmlNamespaces: string[]
): vscode.Diagnostic[] => {
let inScopeVariablesList: VariableData[] = []
let xpathVariableCurrentlyBeingDefined: boolean
Expand All @@ -330,7 +331,7 @@ export class XsltTokenDiagnostics {
let topLevelSymbols: vscode.DocumentSymbol[] = symbols
let tagIdentifierName: string = ''
let lastTokenIndex = allTokens.length - 1
let inheritedPrefixes: string[] = []
let inheritedPrefixes: string[] = xmlNamespaces
let globalVariableData: VariableData[] = []
let checkedGlobalVarNames: string[] = []
let checkedGlobalFnNames: string[] = []
Expand Down Expand Up @@ -567,7 +568,8 @@ export class XsltTokenDiagnostics {
inScopeXPathVariablesList,
xpathStack,
inScopeVariablesList,
elementStack
elementStack,
xmlNamespaces
)
if (unResolvedToken !== null) {
unresolvedXsltVariableReferences.push(unResolvedToken)
Expand Down Expand Up @@ -1624,7 +1626,8 @@ export class XsltTokenDiagnostics {
inScopeXPathVariablesList: VariableData[],
xpathStack: XPathData[],
inScopeVariablesList: VariableData[],
elementStack: ElementData[]
elementStack: ElementData[],
xmlNamespaces: string[]
): BaseToken | null {
let fullVarName = XsltTokenDiagnostics.getTextForToken(
token.line,
Expand Down Expand Up @@ -1666,6 +1669,13 @@ export class XsltTokenDiagnostics {
importedResolved =
globalVarName !== varName && importedVariables.indexOf(varName) > -1
}
// Parse the namespaces from the DFDL document that contains the XPath segments. If the variable is
// defined in an external namespace that we are importing, don't flag it. This is a temporary solution
// that we can remove once we have the ability to create a DFDL model outside of a Parse operation.
if (!resolved && !importedResolved && varName.includes(':')) {
let splits = varName.split(':')
resolved = xmlNamespaces.includes(splits[0]) ? token : undefined
}
if (!resolved && !importedResolved) {
result = token
}
Expand Down
Loading