import { filter , sortBy , map , isEmpty , flatMap } from "lodash" ; import { XMLElement , XMLToken , SourcePosition } from "@xml-tools/ast" ; import { UI5SemanticModel } from "@ui5-language-assistant/semantic-model-types" ; import { getUI5AggregationByXMLElement } from "@ui5-language-assistant/logic-utils" ; import { validations , buildMessage , } from "@ui5-language-assistant/user-facing-text" ; import { InvalidAggregationCardinalityIssue } from "../../../api" ; const { INVALID_AGGREGATION_CARDINALITY } = validations ; export function validateExplicitAggregationCardinality ( xmlElement , model ) { const aggregation = getUI5AggregationByXMLElement ( xmlElement , model ) ; if ( aggregation === undefined || xmlElement . syntax . openName === undefined ) { return [ ] ; } const allSubElementsAtSameDepth = getAllSubElementsAtSameDepth ( xmlElement ) ; if ( allSubElementsAtSameDepth . length <= 1 || aggregation . cardinality === "0..n" ) { return [ ] ; } const firstSubElementAtSameDepth = getFirstSubElementAtSameDepth ( allSubElementsAtSameDepth ) ; const redundantAggregationSubElements = filter ( xmlElement . subElements , ( _ ) => ! isFirstSubElementAtSameDepth ( _ , firstSubElementAtSameDepth ) ) ; const invalidAggregationCardinalityIssues = map ( redundantAggregationSubElements , ( _ ) : InvalidAggregationCardinalityIssue => ( { kind : "InvalidAggregationCardinality" , message : buildMessage ( INVALID_AGGREGATION_CARDINALITY . msg , aggregation . name ) , severity : "error" , offsetRange : { start : getSubElementPosition ( _ ) . startOffset , end : getSubElementPosition ( _ ) . endOffset , } , } ) ) ; return invalidAggregationCardinalityIssues ; } function getFirstSubElementAtSameDepth ( allSubElementsAtSameDepth ) { const sortedSubElementsAtSameDepth = sortBy ( allSubElementsAtSameDepth , ( _ ) => getSubElementPosition ( _ ) ) ; return sortedSubElementsAtSameDepth [ 0 ] ; } function isFirstSubElementAtSameDepth ( subElement , firstSubElement ) { return ( getSubElementPosition ( subElement ) . startOffset === getSubElementPosition ( firstSubElement ) . startOffset ) ; } function getSubElementPosition ( xmlElement ) { return xmlElement . syntax . openName ?? xmlElement . position ; } param aggregationElem function getAllSubElementsAtSameDepth ( aggregationElem ) { if ( aggregationElem . parent . type !== "XMLElement" ) { return [ ] ; } const aggregationParent = aggregationElem . parent ; const sameNameAggregations = filter ( aggregationParent . subElements , ( _ ) => _ . name === aggregationElem . name && ! isEmpty ( _ . subElements ) ) ; const allSubElementsAtSameDepth = flatMap ( sameNameAggregations , ( _ ) => { return _ . subElements ; } ) ; return allSubElementsAtSameDepth ; }