import { map , merge , reduce , has , partial , sortBy , cloneDeep , isArray , isFunction , } from "lodash" ; import * as model from "@ui5-language-assistant/semantic-model-types" ; import { Json } from "../api" ; import * as apiJson from "./api-json" ; import { isLibraryFile } from "./validate" ; import { fixLibrary } from "./fix-api-json" ; import { error , hasProperty , newMap } from "./utils" ; export function convertToSemanticModel ( libraries , jsonSymbols , strict , printValidationErrors ) { const model = { version : "" , includedLibraries : [ ] , classes : newMap ( ) , enums : newMap ( ) , functions : newMap ( ) , namespaces : newMap ( ) , typedefs : newMap ( ) , interfaces : newMap ( ) , } ; const libsArray = map ( libraries , ( fileContent , libraryName ) => ( { libraryName , fileContent , } ) ) ; const sortedLibs = sortBy ( libsArray , "libraryName" ) ; model . includedLibraries = map ( sortedLibs , ( _ ) => _ . libraryName ) ; reduce ( sortedLibs , ( model , { libraryName , fileContent } ) => { fixLibrary ( libraryName , fileContent ) ; if ( isLibraryFile ( libraryName , fileContent , strict , printValidationErrors ) ) { const libSemanticModel = convertLibraryToSemanticModel ( libraryName , fileContent , jsonSymbols , strict ) ; addLibraryToModel ( libSemanticModel , model ) ; } else if ( strict ) { throw new Error ( `Entry for ${ libraryName } is not a valid library file` ) ; } return model ; } , model ) ; return model ; } function addLibraryToModel ( library , model ) { merge ( model , library ) ; } function convertLibraryToSemanticModel ( libName , lib , jsonSymbols , strict ) { const model = { version : lib . version , includedLibraries : [ ] , classes : newMap ( ) , interfaces : newMap ( ) , enums : newMap ( ) , functions : newMap ( ) , namespaces : newMap ( ) , typedefs : newMap ( ) , } ; if ( lib . symbols === undefined ) { return model ; } for ( const symbol of lib . symbols ) { const fqn = symbol . name ; if ( has ( jsonSymbols , fqn ) ) { error ( `${ libName }: Duplicate symbol found: ${ symbol . kind } ${ fqn }. First occurrence is a ${ jsonSymbols [ fqn ] . kind }.` , strict ) ; continue ; } jsonSymbols [ fqn ] = symbol ; switch ( symbol . kind ) { case "namespace" : case "member" : { model . namespaces [ fqn ] = convertNamespace ( libName , symbol ) ; break ; } case "class" : { model . classes [ fqn ] = convertClass ( libName , symbol ) ; break ; } case "enum" : { model . enums [ fqn ] = convertEnum ( libName , symbol ) ; break ; } case "function" : { model . functions [ fqn ] = convertFunction ( libName , symbol ) ; break ; } case "interface" : { model . interfaces [ fqn ] = convertInterface ( libName , symbol ) ; break ; } case "typedef" : { model . typedefs [ fqn ] = convertTypedef ( libName , symbol ) ; break ; } } } return model ; } function convertNamespace ( libName , symbol ) { const base = convertSymbol ( libName , symbol ) ; const namespace = { ... base , kind : "UI5Namespace" , fields : [ ] , methods : [ ] , events : [ ] , namespaces : newMap ( ) , classes : newMap ( ) , } ; namespace . methods = map ( symbol . methods , partial ( convertMethod , libName , namespace ) ) ; namespace . fields = map ( symbol . properties , partial ( convertField , libName , namespace ) ) ; namespace . events = map ( symbol . events , partial ( convertEvent , libName , namespace ) ) ; return namespace ; } function convertClass ( libName , symbol ) { const base = convertSymbol ( libName , symbol ) ; const clazz = { ... base , kind : "UI5Class" , abstract : symbol . abstract ?? false , ctor : undefined , extends : undefined , implements : [ ] , aggregations : [ ] , associations : [ ] , events : [ ] , methods : [ ] , properties : [ ] , fields : [ ] , defaultAggregation : undefined , } ; if ( symbol [ "ui5-metadata" ] !== undefined ) { clazz . aggregations = map ( symbol [ "ui5-metadata" ] . aggregations , ( _ ) => convertAggregation ( libName , _ , clazz ) ) ; clazz . associations = map ( symbol [ "ui5-metadata" ] . associations , ( _ ) => convertAssociation ( libName , _ , clazz ) ) ; clazz . properties = map ( symbol [ "ui5-metadata" ] . properties , partial ( convertProperty , libName , clazz ) ) ; clazz . properties = clazz . properties . concat ( map ( symbol [ "ui5-metadata" ] . specialSettings , partial ( convertProperty , libName , clazz ) ) ) ; } clazz . ctor = symbol . constructor === undefined || isFunction ( symbol . constructor ) ? undefined : convertConstructor ( libName , clazz , symbol . constructor ) ; clazz . events = map ( symbol . events , partial ( convertEvent , libName , clazz ) ) ; clazz . methods = map ( symbol . methods , partial ( convertMethod , libName , clazz ) ) ; clazz . fields = map ( symbol . properties , partial ( convertProperty , libName , clazz ) ) ; return clazz ; } function convertInterface ( libName , symbol ) { const base = convertSymbol ( libName , symbol ) ; const interfacee = { ... base , kind : "UI5Interface" , events : [ ] , methods : [ ] , } ; interfacee . events = map ( symbol . events , partial ( convertEvent , libName , interfacee ) ) ; interfacee . methods = map ( symbol . methods , partial ( convertMethod , libName , interfacee ) ) ; return interfacee ; } function convertFunction ( libName , symbol ) { const base = convertSymbol ( libName , symbol ) ; const func = { ... base , kind : "UI5Function" , } ; return func ; } function convertEnum ( libName , symbol ) { const base = convertSymbol ( libName , symbol ) ; const enumm = { ... base , kind : "UI5Enum" , fields : [ ] , } ; enumm . fields = map ( symbol . properties , partial ( convertEnumValue , libName , enumm ) ) ; return enumm ; } function convertTypedef ( libName , symbol ) { const base = convertSymbol ( libName , symbol ) ; const typedef = { ... base , kind : "UI5Typedef" , } ; return typedef ; } function convertMeta ( libName , jsonMeta ) { const meta = { library : libName , description : jsonMeta . description , since : jsonMeta . since , deprecatedInfo : jsonMeta . deprecated ? { isDeprecated : true , since : jsonMeta . deprecated . since , text : jsonMeta . deprecated . text , } : undefined , experimentalInfo : jsonMeta . experimental ? { isExperimental : true , since : jsonMeta . experimental . since , text : jsonMeta . experimental . text , } : undefined , visibility : jsonMeta . visibility ?? "public" , } ; return meta ; } function convertSymbol ( libName , jsonSymbol ) { const meta = convertMeta ( libName , jsonSymbol ) ; const baseNode = { ... meta , kind : "" , name : jsonSymbol . basename , parent : undefined , } ; return baseNode ; } function convertConstructor ( libName , parent , jsonConstructor ) { const constructor = { kind : "UI5Constructor" , library : libName , description : jsonConstructor . description , visibility : jsonConstructor . visibility ?? "public" , deprecatedInfo : undefined , experimentalInfo : undefined , since : undefined , name : "" , parent : parent , } ; return constructor ; } function convertMethod ( libName , parent , jsonMethod ) { const meta = convertMeta ( libName , jsonMethod ) ; const method = { kind : "UI5Method" , ... meta , name : jsonMethod . name , parent : parent , } ; return method ; } function convertField ( libName , parent , jsonProperty ) { const meta = convertMeta ( libName , jsonProperty ) ; const field = { kind : "UI5Field" , ... meta , name : jsonProperty . name , parent : parent , type : jsonProperty . type === undefined ? undefined : { kind : "UnresolvedType" , name : jsonProperty . type , } , } ; return field ; } function convertProperty ( libName , parent , jsonProperty ) { const meta = convertMeta ( libName , jsonProperty ) ; const defaultValue = hasProperty ( jsonProperty , "defaultValue" ) ? cloneDeep ( jsonProperty . defaultValue ) : undefined ; const property = { kind : "UI5Prop" , ... meta , name : jsonProperty . name , parent : parent , type : jsonProperty . type === undefined ? undefined : { kind : "UnresolvedType" , name : jsonProperty . type , } , default : defaultValue , } ; return property ; } function convertEnumValue ( libName , parent , jsonProperty ) { const meta = convertMeta ( libName , jsonProperty ) ; const enumValue = { kind : "UI5EnumValue" , ... meta , name : jsonProperty . name , parent : parent , } ; return enumValue ; } function convertAggregation ( libName , jsonAggregation , parent ) { const meta = convertMeta ( libName , jsonAggregation ) ; const aggregation = { kind : "UI5Aggregation" , ... meta , name : jsonAggregation . name , parent : parent , type : jsonAggregation . type === undefined ? undefined : { kind : "UnresolvedType" , name : jsonAggregation . type , } , altTypes : isArray ( jsonAggregation . altTypes ) ? map ( jsonAggregation . altTypes , ( _ ) => ( { kind : "UnresolvedType" , name : _ , } ) ) : [ ] , cardinality : jsonAggregation . cardinality ?? "0..n" , } ; return aggregation ; } function convertAssociation ( libName , jsonAssociation , parent ) { const meta = convertMeta ( libName , jsonAssociation ) ; const association = { kind : "UI5Association" , ... meta , name : jsonAssociation . name , parent : parent , type : jsonAssociation . type === undefined ? undefined : { kind : "UnresolvedType" , name : jsonAssociation . type , } , cardinality : jsonAssociation . cardinality ?? "0..1" , } ; return association ; } function convertEvent ( libName , parent , jsonEvent ) { const meta = convertMeta ( libName , jsonEvent ) ; const event = { kind : "UI5Event" , ... meta , name : jsonEvent . name , parent : parent , } ; return event ; }