import * as parser from "jsonc-parser" ; import * as vscode from "vscode" ; import { DigitalTwinConstants } from "./digitalTwinConstants" ; import { ClassNode , DigitalTwinGraph , PropertyNode , VersionNode } from "./digitalTwinGraph" ; /**
 * Type of json node
 */ export enum JsonNodeType { Object = "object" , Array = "array" , String = "string" , Number = "number" , Boolean = "boolean" , Property = "property" } /**
 * DigitalTwin model content
 */ export interface ModelContent { jsonNode : parser . Node ; version : number ; } /**
 * Property pair includes name and value
 */ export interface PropertyPair { name : parser . Node ; value : parser . Node ; } /**
 * Utility for IntelliSense
 */ export class IntelliSenseUtility { param context static async initGraph ( context ) { IntelliSenseUtility . graph = await DigitalTwinGraph . getInstance ( context ) ; } /**
     * check if IntelliSense has been enabled
     */ static enabled ( ) { return IntelliSenseUtility . graph && IntelliSenseUtility . graph . initialized ( ) ; } /**
     * get entry node of DigitalTwin model
     */ static getEntryNode ( ) { return IntelliSenseUtility . graph . getPropertyNode ( DigitalTwinConstants . ENTRY_NODE ) ; } param name static getPropertyNode ( name ) { return IntelliSenseUtility . graph . getPropertyNode ( name ) ; } param name static getClasNode ( name ) { return IntelliSenseUtility . graph . getClassNode ( name ) ; } param classNode static getClassType ( classNode ) { return classNode . label || classNode . id ; } param propertyNode static getValidTypes ( propertyNode ) { if ( ! propertyNode . range ) { return [ ] ; } return propertyNode . range . map ( classNode => { if ( classNode . label ) { return classNode . label ; } else { const index = classNode . id . lastIndexOf ( DigitalTwinConstants . SCHEMA_SEPARATOR ) ; return index === - 1 ? classNode . id : classNode . id . slice ( index + 1 ) ; } } ) ; } param text static parseDigitalTwinModel ( text ) { const jsonNode = parser . parseTree ( text ) ; const contextPath = [ DigitalTwinConstants . CONTEXT ] ; const contextNode = parser . findNodeAtLocation ( jsonNode , contextPath ) ; if ( ! contextNode ) { return undefined ; } const version = IntelliSenseUtility . getDigitalTwinVersion ( contextNode ) ; if ( ! version ) { return undefined ; } return { jsonNode , version } ; } param node static getDigitalTwinVersion ( node ) { if ( ! IntelliSenseUtility . graph ) { return 0 ; } if ( node . type === JsonNodeType . String ) { return IntelliSenseUtility . graph . getVersion ( node . value as string ) ; } else if ( node . type === JsonNodeType . Array && node . children ) { for ( const child of node . children ) { if ( child . type !== JsonNodeType . String ) { return 0 ; } const version = IntelliSenseUtility . graph . getVersion ( child . value as string ) ; if ( version ) { return version ; } } } return 0 ; } param name static isReservedName ( name ) { return name . startsWith ( DigitalTwinConstants . RESERVED ) ; } param classNode static isLanguageNode ( classNode ) { return classNode . id === DigitalTwinConstants . LANGUAGE ; } param classNode static isObjectClass ( classNode ) { if ( classNode . isAbstract || classNode . enums || ! classNode . label ) { return false ; } return true ; } param node static parseProperty ( node ) { if ( node . type !== JsonNodeType . Property || ! node . children || node . children . length !== 2 ) { return undefined ; } return { name : node . children [ 0 ] , value : node . children [ 1 ] } ; } param document param node static getNodeRange ( document , node ) { return new vscode . Range ( document . positionAt ( node . offset ) , document . positionAt ( node . offset + node . length ) ) ; } param propertyNode param version static getEnums ( propertyNode , version ) { const enums = [ ] ; for ( const classNode of IntelliSenseUtility . getRangeOfPropertyByVersion ( propertyNode , version ) ) { if ( classNode . enums ) { enums . push ( ... classNode . enums ) ; } else if ( classNode . isAbstract ) { for ( const child of IntelliSenseUtility . getChildrenOfClassByVersion ( classNode , version ) ) { if ( child . enums ) { enums . push ( ... child . enums ) ; } } } } return enums ; } param propertyNode param version static getObjectClasses ( propertyNode , version ) { const classes = [ ] ; for ( const classNode of IntelliSenseUtility . getRangeOfPropertyByVersion ( propertyNode , version ) ) { if ( IntelliSenseUtility . isObjectClass ( classNode ) ) { classes . push ( classNode ) ; } else if ( classNode . isAbstract ) { for ( const child of IntelliSenseUtility . getChildrenOfClassByVersion ( classNode , version ) ) { if ( ! child . enums ) { classes . push ( child ) ; } } } } return classes ; } param propertyNode param version static getRangeOfPropertyByVersion ( propertyNode , version ) { if ( ! propertyNode . range ) { return [ ] ; } return propertyNode . range . filter ( node => IntelliSenseUtility . isAvailableByVersion ( version , node . version ) ) ; } param classNode param version static getChildrenOfClassByVersion ( classNode , version ) { if ( ! classNode . children ) { return [ ] ; } return classNode . children . filter ( node => IntelliSenseUtility . isAvailableByVersion ( version , node . version ) ) ; } param classNode param version static getPropertiesOfClassByVersion ( classNode , version ) { if ( ! classNode . properties ) { return [ ] ; } return classNode . properties . filter ( node => IntelliSenseUtility . isAvailableByVersion ( version , node . version ) ) ; } param version param versionNode static isAvailableByVersion ( version , versionNode ) { if ( ! versionNode ) { return true ; } if ( versionNode . includeSince && versionNode . includeSince > version ) { return false ; } if ( versionNode . excludeSince && versionNode . excludeSince <= version ) { return false ; } return true ; } param propertyPair static resolvePropertyName ( propertyPair ) { let propertyName = propertyPair . name . value as string ; if ( propertyName !== DigitalTwinConstants . SCHEMA ) { return propertyName ; } const node = propertyPair . name ; if ( node . parent && node . parent . parent ) { const outPropertyPair = IntelliSenseUtility . getOuterPropertyPair ( node . parent . parent ) ; if ( outPropertyPair ) { const name = outPropertyPair . name . value as string ; if ( name === DigitalTwinConstants . IMPLEMENTS ) { propertyName = DigitalTwinConstants . INTERFACE_SCHEMA ; } } } return propertyName ; } param node static getOuterPropertyPair ( node ) { if ( node . type !== JsonNodeType . Object ) { return undefined ; } let outerProperty = node . parent ; if ( outerProperty && outerProperty . type === JsonNodeType . Array ) { outerProperty = outerProperty . parent ; } return outerProperty ? IntelliSenseUtility . parseProperty ( outerProperty ) : undefined ; } param document static isJsonFile ( document ) { return document . languageId === DigitalTwinConstants . JSON_LANGUAGE_ID ; } private static graph : DigitalTwinGraph ; private constructor ( ) { } }