import { reject , flatMap , map , pickBy } from "lodash" ; import { accept , XMLAstVisitor , XMLAttribute , XMLDocument , XMLToken , } from "@xml-tools/ast" ; import { validations , buildMessage , } from "@ui5-language-assistant/user-facing-text" ; import { NonUniqueIDIssue } from "../../../api" ; import { isPossibleCustomClass } from "../../utils/ui5-classes" ; const { NON_UNIQUE_ID } = validations ; export function validateNonUniqueID ( xmlDoc ) { const idCollector = new IdsCollectorVisitor ( ) ; accept ( xmlDoc , idCollector ) ; const idsToXMLElements = idCollector . idsToXMLElements ; const duplicatedIdsRecords = pickBy ( idsToXMLElements , ( _ ) => _ . length > 1 ) ; const allIDsIssues = flatMap ( duplicatedIdsRecords , buildIssuesForSingleID ) ; return allIDsIssues ; } function buildIssuesForSingleID ( duplicatedAttributes , id ) { const issuesForID = map ( duplicatedAttributes , ( currDupAttrib , currAttribIdx ) => { const currDupIdValToken = currDupAttrib . syntax . value ; const relatedOtherDupIDAttribs = reject ( duplicatedAttributes , ( _ , arrIdx ) => arrIdx === currAttribIdx ) ; return { kind : "NonUniqueIDIssue" as const , message : buildMessage ( NON_UNIQUE_ID . msg , id ) , severity : "error" as const , offsetRange : { start : currDupIdValToken . startOffset , end : currDupIdValToken . endOffset , } , identicalIDsRanges : map ( relatedOtherDupIDAttribs , ( _ ) => ( { start : _ . syntax . value . startOffset , end : _ . syntax . value . endOffset , } ) ) , } ; } ) ; return issuesForID ; } type DuplicatedIDXMLAttribute = XMLAttribute & { syntax : { value : XMLToken ; } ; } ; class IdsCollectorVisitor implements XMLAstVisitor { public idsToXMLElements : Record < string , DuplicatedIDXMLAttribute [ ] > = Object . create ( null ) ; visitXMLAttribute ( attrib ) { if ( attrib . key === "id" && attrib . value !== null && attrib . value !== "" && attrib . syntax . value !== undefined && attrib . parent . name !== null && isPossibleCustomClass ( attrib . parent ) ) { if ( this . idsToXMLElements [ attrib . value ] === undefined ) { this . idsToXMLElements [ attrib . value ] = [ attrib ] ; } else { this . idsToXMLElements [ attrib . value ] . push ( attrib ) ; } } } }