This commit is contained in:
dilin-MS 2020-04-28 09:53:39 +08:00
Родитель fb2505469c
Коммит 973696312a
4 изменённых файлов: 627 добавлений и 173 удалений

Просмотреть файл

@ -8,6 +8,7 @@ export class Constants {
public static readonly CHANNEL_NAME = "DTDL";
public static readonly UTF8 = "utf8";
public static readonly EMPTY_STRING = "";
public static readonly DEFAULT_SEPARATOR = ",";
public static readonly COMPLETION_TRIGGER = '"';
public static readonly JSON_SPACE = 2;
public static readonly DEFAULT_TIMER_MS = 1000;

Просмотреть файл

@ -5,33 +5,462 @@ import * as parser from "jsonc-parser";
import * as vscode from "vscode";
import { Constants } from "../common/constants";
import { DigitalTwinConstants } from "./digitalTwinConstants";
import { ClassNode, PropertyNode } from "./digitalTwinGraph";
import { ClassNode, NodeType, PropertyNode } from "./digitalTwinGraph";
import { IntelliSenseUtility, JsonNodeType, ModelContent, PropertyPair } from "./intelliSenseUtility";
import { LANGUAGE_CODE } from "./languageCode";
interface Suggestion {
isProperty: boolean;
label: string;
insertText: string;
withSeparator: boolean;
}
/**
* Completion item provider for DigitalTwin IntelliSense
*/
export class DigitalTwinCompletionItemProvider implements vscode.CompletionItemProvider {
/**
* get text for json parser after completion
* @param document text document
* @param position position
*/
private static getTextForParse(document: vscode.TextDocument, position: vscode.Position): string {
const text: string = document.getText();
const offset: number = document.offsetAt(position);
export class DigitalTwinCompletionItemProvider
implements vscode.CompletionItemProvider {
private static getDocumentNode(document: vscode.TextDocument, position: vscode.Position): parser.Node {
let text: string = document.getText();
let textNode: parser.Node = parser.parseTree(text);
if (textNode && textNode.type === JsonNodeType.Property) {
const offset: number = document.offsetAt(position);
text = DigitalTwinCompletionItemProvider.completeText(text, offset);
textNode = parser.parseTree(text);
}
return textNode;
}
private static completeText(text: string, offset: number): string {
if (text[offset] === Constants.COMPLETION_TRIGGER) {
const edit: parser.Edit = {
offset,
length: 1,
content: Constants.COMPLETION_TRIGGER + DigitalTwinConstants.DEFAULT_DELIMITER,
};
return parser.applyEdits(text, [edit]);
text = parser.applyEdits(text, [
{
offset,
length: 1,
content: Constants.COMPLETION_TRIGGER + Constants.DEFAULT_SEPARATOR,
},
]);
}
return text;
}
private static getSuggestion(stringNode: parser.Node): Suggestion[] {
const jsonPropertyNode: parser.Node | undefined = stringNode.parent;
if (!jsonPropertyNode || jsonPropertyNode.type !== JsonNodeType.Property || !jsonPropertyNode.children) {
return [];
}
if (stringNode === jsonPropertyNode.children[0]) {
const suggestWithValue: boolean = jsonPropertyNode.children.length < 2;
return DigitalTwinCompletionItemProvider.suggestKey(jsonPropertyNode, suggestWithValue);
} else {
return DigitalTwinCompletionItemProvider.suggestValue(jsonPropertyNode);
}
}
private static suggestKey(
jsonPropertyNode: parser.Node,
suggestWithValue: boolean,
): Suggestion[] {
const existedProperties = DigitalTwinCompletionItemProvider.getExistedProperties(jsonPropertyNode);
const type = DigitalTwinCompletionItemProvider.tryGetType(jsonPropertyNode);
if (!type) {
// type is neither defined nor inferable. User should destinate @type first.
return DigitalTwinCompletionItemProvider.suggestTypeKey(suggestWithValue);
} else {
return DigitalTwinCompletionItemProvider.suggestPropertiesCandidates(type, existedProperties, suggestWithValue);
}
}
private static getExistedProperties(jsonPropertyNode: parser.Node): string[] {
const existedProperties: string[] = [];
const parentObjectNode: parser.Node | undefined = jsonPropertyNode.parent;
if (!parentObjectNode || parentObjectNode.type !== JsonNodeType.Object || !parentObjectNode.children) {
return existedProperties;
}
for (const child of parentObjectNode.children) {
if (child === jsonPropertyNode) {
continue;
}
const propertyPair: PropertyPair|undefined = IntelliSenseUtility.parseProperty(child);
if (propertyPair) {
const jsonPropertyKey = propertyPair.name.value;
existedProperties.push(jsonPropertyKey);
}
}
return existedProperties;
}
private static tryGetType(jsonPropertyNode: parser.Node): string|undefined {
const parentObjectNode: parser.Node | undefined = jsonPropertyNode.parent;
if (!parentObjectNode || parentObjectNode.type !== JsonNodeType.Object || !parentObjectNode.children) {
return undefined;
}
let type: string|undefined = DigitalTwinCompletionItemProvider.tryGetTypeByTypeProperty(parentObjectNode);
if (!type) {
type = DigitalTwinCompletionItemProvider.tryGetTypeByOuterProperty(jsonPropertyNode);
}
return type;
}
private static tryGetTypeByTypeProperty(objectNode: parser.Node): string|undefined {
if (objectNode.children) {
for (const child of objectNode.children) {
const property: PropertyPair|undefined = IntelliSenseUtility.parseProperty(child);
if (property && property.name.value === DigitalTwinConstants.TYPE) {
return property.value.value as string;
}
}
}
return undefined;
}
private static tryGetTypeByOuterProperty(jsonPropertyNode: parser.Node): string|undefined {
const outerPropertyClassNode: ClassNode|undefined =
IntelliSenseUtility.getOuterPropertyClassNode(jsonPropertyNode);
if (outerPropertyClassNode && !outerPropertyClassNode.isAbstract) {
return outerPropertyClassNode.name;
} else {
return undefined;
}
}
private static suggestTypeKey(suggestWithValue: boolean): Suggestion[] {
const suggestions: Suggestion[] = [];
const dummyTypeNode: PropertyNode = {
id: "dummy-type",
name: DigitalTwinConstants.TYPE,
nodeKind: Constants.EMPTY_STRING,
type: Constants.EMPTY_STRING,
constraint: {},
};
suggestions.push({
isProperty: true,
label: DigitalTwinCompletionItemProvider.formatLabel(dummyTypeNode.name, true),
insertText: DigitalTwinCompletionItemProvider.getInsertedTextForProperty(dummyTypeNode, suggestWithValue),
withSeparator: suggestWithValue,
});
return suggestions;
}
/**
* get insert text for property
* @param propertyNode DigitalTwin property node
* @param includeValue identify if insert text includes property value
*/
private static getInsertedTextForProperty(propertyNode: PropertyNode, includeValue: boolean): string {
const name: string = propertyNode.name;
if (!includeValue) {
return `"${name}"`;
}
// provide value snippet according to property type
let value = "$1";
if (propertyNode.isPlural && propertyNode.type !== NodeType.RdfLangString) {
value = "[$1]";
} else if (propertyNode.type) {
const typeClassNode: ClassNode|undefined = IntelliSenseUtility.getClassNodeByClassId(propertyNode.type);
if (typeClassNode && DigitalTwinCompletionItemProvider.isObjectClass(typeClassNode)) {
value = "{$1}";
} else {
let type: string|undefined = propertyNode.type;
if (typeClassNode) {
type = DigitalTwinCompletionItemProvider.tryGetUniqueTypeByTypeClassNode(typeClassNode);
}
switch (type) {
case NodeType.Boolean:
case NodeType.XsdBoolean:
value = "${1:false}";
break;
case NodeType.Integer:
case NodeType.XsdInteger:
value = "${1:0}";
break;
case NodeType.String:
case NodeType.XsdString:
case NodeType.RdfLangString:
value = '"$1"';
break;
}
}
}
return `"${name}": ${value}`;
}
private static isObjectClass(typeClassNode: ClassNode): boolean {
return (!typeClassNode.isAbstract && !typeClassNode.instances);
}
private static tryGetUniqueTypeByTypeClassNode(typeClassNode: ClassNode): string|undefined {
if (!typeClassNode.isAbstract && typeClassNode.instances && typeClassNode.instances.length === 1) {
return typeClassNode.instances[0];
}
return undefined;
}
private static suggestPropertiesCandidates(
type: string,
existedPropertyNames: string[],
suggestWithValue: boolean,
): Suggestion[] {
const suggestions: Suggestion[] = [];
DigitalTwinCompletionItemProvider.
suggestReservedProperties(type, existedPropertyNames, suggestWithValue, suggestions);
DigitalTwinCompletionItemProvider.
suggestUnreservedProperties(type, existedPropertyNames, suggestWithValue, suggestions);
return suggestions;
}
private static suggestUnreservedProperties(
type: string,
existedPropertyNames: string[],
suggestWithValue: boolean,
suggestions: Suggestion[],
): void {
const propertiesCandidates: Set<PropertyNode> =
DigitalTwinCompletionItemProvider.getPropertiesCandidates(type, existedPropertyNames);
for (const propertyCandidate of propertiesCandidates) {
const isRequired: boolean = propertyCandidate.isRequired ? true : false;
suggestions.push({
isProperty: true,
label: DigitalTwinCompletionItemProvider.formatLabel(propertyCandidate.name, isRequired),
insertText: DigitalTwinCompletionItemProvider.getInsertedTextForProperty(propertyCandidate, suggestWithValue),
withSeparator: suggestWithValue,
});
}
}
private static getPropertiesCandidates(type: string, existedPropertyNames: string[]): Set<PropertyNode> {
const propertiesCandidates: Set<PropertyNode> = new Set<PropertyNode>();
let typeClassNode: ClassNode|undefined = IntelliSenseUtility.getClassNodeByClassName(type);
if (!typeClassNode) {
// type is not a class name, it can be a property name
typeClassNode = IntelliSenseUtility.getClassNodeByPropertyName(type);
}
if (typeClassNode && typeClassNode.properties) {
for (const property of typeClassNode.properties) {
const propertyNode: PropertyNode|undefined = IntelliSenseUtility.getPropertyNodeById(property);
if (propertyNode && !existedPropertyNames.includes(propertyNode.name)) {
propertiesCandidates.add(propertyNode);
}
}
}
return propertiesCandidates;
}
private static suggestReservedProperties(
type: string,
existedPropertyNames: string[],
suggestWithValue: boolean,
suggestions: Suggestion[],
): void {
if (existedPropertyNames.includes(DigitalTwinConstants.ID)) {
return;
}
// Suggest @id
const isIdRequired: boolean = IntelliSenseUtility.isPartitionNode(type);
const dummyIdNode: PropertyNode = {
id: "dummy-id",
name: DigitalTwinConstants.ID,
nodeKind: Constants.EMPTY_STRING,
type: Constants.EMPTY_STRING,
constraint: {},
};
suggestions.push({
isProperty: true,
label: DigitalTwinCompletionItemProvider.formatLabel(dummyIdNode.name, isIdRequired),
insertText: DigitalTwinCompletionItemProvider.getInsertedTextForProperty(dummyIdNode, suggestWithValue),
withSeparator: suggestWithValue,
});
}
/**
* suggest completion item for property value
* @param jsonPropertyNode json node
* @param position position
* @param range overwrite range
* @param separator separator after completion text
*/
private static suggestValue(jsonPropertyNode: parser.Node): Suggestion[] {
const suggestions: Suggestion[] = [];
if (!jsonPropertyNode.children || jsonPropertyNode.children.length < 1) {
return suggestions;
}
const propertyObserveName = jsonPropertyNode.children[0].value as string;
let possibleValues: string[];
if (propertyObserveName === DigitalTwinConstants.TYPE) {
possibleValues = DigitalTwinCompletionItemProvider.getPossibleTypeValues(jsonPropertyNode);
} else {
possibleValues =
DigitalTwinCompletionItemProvider.getPossibleNonTypeValues(propertyObserveName, jsonPropertyNode);
}
for (const value of possibleValues) {
suggestions.push({
isProperty: false,
label: value,
insertText: `"${value}"`,
withSeparator: true,
});
}
return suggestions;
}
private static getPossibleTypeValues(jsonPropertyNode: parser.Node): string[] {
const valueCandidates: string[] = [];
const outerPropertyClassNode: ClassNode|undefined =
IntelliSenseUtility.getOuterPropertyClassNode(jsonPropertyNode);
if (!outerPropertyClassNode) {
const entryNode = IntelliSenseUtility.getEntryNode();
if (entryNode?.constraint.in) {
valueCandidates.concat(entryNode?.constraint.in);
}
return valueCandidates;
}
if (outerPropertyClassNode.isAbstract) {
if (outerPropertyClassNode.children) {
for (const child of outerPropertyClassNode.children) {
const childClassNode = IntelliSenseUtility.getClassNodeByClassId(child);
if (childClassNode) {
valueCandidates.push(childClassNode.name);
}
}
}
} else {
valueCandidates.push(outerPropertyClassNode.name);
}
return valueCandidates;
}
private static getPossibleNonTypeValues(propertyObserveName: string, jsonPropertyNode: parser.Node): string[] {
let valueCandidates: string[] = [];
const propertyNode: PropertyNode|undefined =
DigitalTwinCompletionItemProvider.getPropertyNodeByPropertyObserveName(propertyObserveName, jsonPropertyNode);
if (!propertyNode) {
return valueCandidates;
}
if (propertyNode.type) {
valueCandidates = DigitalTwinCompletionItemProvider.getAllInstancesOfClass(propertyNode.type);
if (valueCandidates.length > 0 && propertyNode.constraint.in) {
valueCandidates =
DigitalTwinCompletionItemProvider.takeIntersectionOfInItems(valueCandidates, propertyNode.constraint.in);
}
}
return valueCandidates;
}
private static getPropertyNodeByPropertyObserveName(
propertyObserveName: string,
jsonPropertyNode: parser.Node,
): PropertyNode|undefined {
let propertyNode: PropertyNode|undefined;
const propertyId: string|undefined = IntelliSenseUtility.getIdByName(propertyObserveName);
if (propertyId) {
propertyNode = IntelliSenseUtility.getPropertyNodeById(propertyId);
} else {
// property name is shared by multiple property.
propertyNode =
DigitalTwinCompletionItemProvider.getPropertyNodeByOuterProperty(jsonPropertyNode, propertyObserveName);
}
return propertyNode;
}
private static takeIntersectionOfInItems(instances: string[], inItems: string[]): string[] {
const result: string[] = [];
for (const possibleValue of inItems) {
if (instances.includes(possibleValue)) {
result.push(possibleValue);
}
}
return result;
}
private static getPropertyNodeByOuterProperty(
jsonPropertyNode: parser.Node,
propertyObserveName: string,
): PropertyNode|undefined {
const type = DigitalTwinCompletionItemProvider.tryGetType(jsonPropertyNode);
if (!type) {
return undefined;
}
const outerPropertyClassNode: ClassNode|undefined = IntelliSenseUtility.getClassNodeByClassName(type);
// IntelliSenseUtility.getOuterPropertyClassNode(jsonPropertyNode);
if (outerPropertyClassNode && outerPropertyClassNode.properties) {
for (const propertyId of outerPropertyClassNode.properties) {
const propertyNode: PropertyNode|undefined = IntelliSenseUtility.getPropertyNodeById(propertyId);
if (propertyNode?.name === propertyObserveName) {
return propertyNode;
}
}
}
return undefined;
}
private static getAllInstancesOfClass(className: string): string[] {
const instances: string[] = [];
const classes: string[] = [];
classes.push(className);
while (classes.length > 0) {
const classId = classes.values().next().value;
// classes.delete(classId);
const typeClassNode: ClassNode|undefined = IntelliSenseUtility.getClassNodeByClassId(classId);
if (!typeClassNode) {
continue;
}
if (typeClassNode.children) {
for (const child of typeClassNode.children) {
classes.push(child);
}
} else {
if (typeClassNode.instances) {
for (const instance of typeClassNode.instances) {
instances.push(instance);
}
} else {
instances.push(typeClassNode.name);
}
}
}
return instances;
}
/**
* create completion item
* @param label label
@ -41,17 +470,17 @@ export class DigitalTwinCompletionItemProvider implements vscode.CompletionItemP
* @param range overwrite range for completion text
*/
private static createCompletionItem(
label: string,
isProperty: boolean,
insertText: string,
suggestion: Suggestion,
position: vscode.Position,
range: vscode.Range,
separator: string,
): vscode.CompletionItem {
const insertTextTemp = suggestion.insertText + (suggestion.withSeparator ? separator : Constants.EMPTY_STRING);
const completionItem: vscode.CompletionItem = {
label,
kind: isProperty ? vscode.CompletionItemKind.Property : vscode.CompletionItemKind.Value,
insertText: new vscode.SnippetString(insertText),
// the start of range should not be before position, otherwise completion item will not be shown
label: suggestion.label,
kind: suggestion.isProperty ? vscode.CompletionItemKind.Property : vscode.CompletionItemKind.Value,
insertText: new vscode.SnippetString(insertTextTemp),
// the start of range should be after position, otherwise completion item will not be shown
range: new vscode.Range(position, range.end),
};
if (position.isAfter(range.start)) {
@ -72,10 +501,17 @@ export class DigitalTwinCompletionItemProvider implements vscode.CompletionItemP
node: parser.Node,
): vscode.Range {
let range: vscode.Range;
if (node.type === JsonNodeType.String || node.type === JsonNodeType.Number || node.type === JsonNodeType.Boolean) {
if (
node.type === JsonNodeType.String ||
node.type === JsonNodeType.Number ||
node.type === JsonNodeType.Boolean
) {
range = IntelliSenseUtility.getNodeRange(document, node);
} else {
const word: string = DigitalTwinCompletionItemProvider.getCurrentWord(document, position);
const word: string = DigitalTwinCompletionItemProvider.getCurrentWord(
document,
position,
);
const start: number = document.offsetAt(position) - word.length;
range = new vscode.Range(document.positionAt(start), position);
}
@ -87,10 +523,16 @@ export class DigitalTwinCompletionItemProvider implements vscode.CompletionItemP
* @param document text document
* @param position position
*/
private static getCurrentWord(document: vscode.TextDocument, position: vscode.Position): string {
private static getCurrentWord(
document: vscode.TextDocument,
position: vscode.Position,
): string {
let i: number = position.character - 1;
const text: string = document.lineAt(position.line).text;
while (i >= 0 && DigitalTwinConstants.WORD_STOP.indexOf(text.charAt(i)) === -1) {
while (
i >= 0 &&
DigitalTwinConstants.WORD_STOP.indexOf(text.charAt(i)) === -1
) {
i--;
}
return text.slice(i + 1, position.character);
@ -116,152 +558,55 @@ export class DigitalTwinCompletionItemProvider implements vscode.CompletionItemP
}
}
/**
* suggest completion item for property
* @param node json node
* @param position position
* @param range overwrite range
* @param includeValue identifiy if includes property value
* @param separator separator after completion text
*/
private static suggestProperty(
node: parser.Node,
position: vscode.Position,
range: vscode.Range,
includeValue: boolean,
separator: string,
): vscode.CompletionItem[] {
const completionItems: vscode.CompletionItem[] = [];
return completionItems;
}
/**
* get the type of json object node and record existing properties
* @param node json node
* @param exist existing properties
*/
private static getObjectType(node: parser.Node, exist: Set<string>): ClassNode | undefined {
const parent: parser.Node | undefined = node.parent;
if (!parent || parent.type !== JsonNodeType.Object || !parent.children) {
return undefined;
}
let propertyName: string;
let objectType: ClassNode | undefined;
let propertyPair: PropertyPair | undefined;
for (const child of parent.children) {
if (child === node) {
continue;
}
propertyPair = IntelliSenseUtility.parseProperty(child);
if (!propertyPair || !propertyPair.name.value) {
continue;
}
propertyName = propertyPair.name.value as string;
exist.add(propertyName);
// get from @type property
if (propertyName === DigitalTwinConstants.TYPE) {
const propertyValue: parser.Node = propertyPair.value;
if (propertyValue.type === JsonNodeType.String) {
} else if (propertyValue.type === JsonNodeType.Array && propertyValue.children) {
// support semantic type array
for (const element of propertyValue.children) {
if (element.type === JsonNodeType.String) {
const type: string = element.value as string;
}
}
}
}
}
// infer from outer property
if (!objectType) {
const propertyNode: PropertyNode | undefined = DigitalTwinCompletionItemProvider.getOuterPropertyNode(parent);
if (propertyNode) {
const classes: ClassNode[] = [];
if (classes.length === 1) {
objectType = classes[0];
}
}
}
return objectType;
}
/**
* get outer DigitalTwin property node from current node
* @param node json node
*/
private static getOuterPropertyNode(node: parser.Node): PropertyNode | undefined {
const propertyPair: PropertyPair | undefined = IntelliSenseUtility.getOuterPropertyPair(node);
if (!propertyPair) {
return undefined;
}
return undefined;
}
/**
* format property label with required information
* @param label label
* @param required required properties
*/
private static formatLabel(label: string, required: Set<string>): string {
return required.has(label) ? `${label} ${DigitalTwinConstants.REQUIRED_PROPERTY_LABEL}` : label;
}
/**
* suggest completion item for property value
* @param node json node
* @param position position
* @param range overwrite range
* @param separator separator after completion text
*/
private static suggestValue(
node: parser.Node,
position: vscode.Position,
range: vscode.Range,
separator: string,
): vscode.CompletionItem[] {
const completionItems: vscode.CompletionItem[] = [];
return completionItems;
}
/**
* provide completion items
* @param document text document
* @param position position
* @param token cancellation token
* @param context completion context
*/
public provideCompletionItems(
private static getCompletionItems(
document: vscode.TextDocument,
position: vscode.Position,
token: vscode.CancellationToken,
context: vscode.CompletionContext,
): vscode.ProviderResult<vscode.CompletionItem[] | vscode.CompletionList> {
const text: string = DigitalTwinCompletionItemProvider.getTextForParse(document, position);
const modelContent: ModelContent | undefined = IntelliSenseUtility.parseDigitalTwinModel(text);
if (!modelContent) {
return undefined;
}
if (!IntelliSenseUtility.isGraphInitialized()) {
return undefined;
}
const node: parser.Node | undefined = parser.findNodeAtOffset(modelContent.jsonNode, document.offsetAt(position));
if (!node || node.type !== JsonNodeType.String) {
return undefined;
}
const range: vscode.Range = DigitalTwinCompletionItemProvider.evaluateOverwriteRange(document, position, node);
stringNode: parser.Node,
suggestions: Suggestion[],
): vscode.CompletionItem[] {
const range: vscode.Range =
DigitalTwinCompletionItemProvider.evaluateOverwriteRange(document, position, stringNode);
const separator: string = DigitalTwinCompletionItemProvider.evaluateSeparatorAfter(
document.getText(),
document.offsetAt(range.end),
);
const parent: parser.Node | undefined = node.parent;
if (!parent || parent.type !== JsonNodeType.Property || !parent.children) {
const completionItems: vscode.CompletionItem[] = suggestions.map((s) =>
DigitalTwinCompletionItemProvider.createCompletionItem(s, position, range, separator),
);
return completionItems;
}
/**
* format property label with required information
* @param jsonPropertyKey label
* @param required required properties
*/
private static formatLabel(jsonPropertyKey: string, isRequired: boolean): string {
const requiredInfo: string = isRequired ? ` ${DigitalTwinConstants.REQUIRED_PROPERTY_LABEL}` : "";
return `${jsonPropertyKey}` + requiredInfo;
}
public provideCompletionItems(
document: vscode.TextDocument,
position: vscode.Position,
): vscode.ProviderResult<vscode.CompletionItem[] | vscode.CompletionList> {
const documentNode: parser.Node = DigitalTwinCompletionItemProvider.getDocumentNode(document, position);
if (!IntelliSenseUtility.isDigitalTwinDefinition(documentNode)) {
return undefined;
}
if (node === parent.children[0]) {
const includeValue: boolean = parent.children.length < 2;
return DigitalTwinCompletionItemProvider.suggestProperty(parent, position, range, includeValue, separator);
} else {
return DigitalTwinCompletionItemProvider.suggestValue(parent, position, range, separator);
if (!IntelliSenseUtility.isGraphInitialized()) {
return undefined;
}
const stringNode: parser.Node | undefined = parser.findNodeAtOffset(documentNode, document.offsetAt(position));
if (!stringNode || stringNode.type !== JsonNodeType.String) {
return undefined;
}
const suggestions: Suggestion[] = DigitalTwinCompletionItemProvider.getSuggestion(stringNode);
return DigitalTwinCompletionItemProvider.getCompletionItems(document, position, stringNode, suggestions);
}
}

Просмотреть файл

@ -34,6 +34,16 @@ export interface PropertyNode {
constraint: ConstraintNode;
}
export enum NodeType {
RdfLangString = "http://www.w3.org/1999/02/22-rdf-syntax-ns#langString",
XsdString = "http://www.w3.org/2001/XMLSchema#string",
String = "string",
XsdBoolean = "http://www.w3.org/2001/XMLSchema#boolean",
Boolean = "boolean",
XsdInteger = "http://www.w3.org/2001/XMLSchema#integer",
Integer = "integer",
}
/**
* Constraint node of DigitalTwin graph
*/
@ -186,6 +196,34 @@ export class DigitalTwinGraph {
return children;
}
public getClassNodeByClassId(classId: string): ClassNode|undefined {
return this.classNodes.get(classId);
}
public getClassNodeByClassName(className: string): ClassNode|undefined {
const classId = this.getIdByName(className);
if (classId) {
return this.classNodes.get(classId);
}
return undefined;
}
public getIdByName(id: string): string|undefined {
return this.dtdlContext.get(id);
}
public getPropertyNodeById(propertyId: string): PropertyNode|undefined {
return this.propertyNodes.get(propertyId);
}
public getPropertyNodeByName(propertyName: string): PropertyNode|undefined {
const propertyId = this.getIdByName(propertyName);
if (propertyId) {
return this.propertyNodes.get(propertyId);
}
return undefined;
}
/**
* get obverse children of abstract class
* @param abstractClass abstract class node

Просмотреть файл

@ -224,13 +224,14 @@ export class IntelliSenseUtility {
/**
* parse json node, return property pair
* @param node json node
* @param jsonPropertyNode json node
*/
public static parseProperty(node: parser.Node): PropertyPair | undefined {
if (node.type !== JsonNodeType.Property || !node.children || node.children.length !== 2) {
public static parseProperty(jsonPropertyNode: parser.Node): PropertyPair | undefined {
if (jsonPropertyNode.type !== JsonNodeType.Property
|| !jsonPropertyNode.children || jsonPropertyNode.children.length !== 2) {
return undefined;
}
return { name: node.children[0], value: node.children[1] };
return { name: jsonPropertyNode.children[0], value: jsonPropertyNode.children[1] };
}
/**
@ -265,19 +266,88 @@ export class IntelliSenseUtility {
/**
* get outer property pair from current node
* @param node json node
* @param objectNode json node
*/
public static getOuterPropertyPair(node: parser.Node): PropertyPair | undefined {
if (node.type !== JsonNodeType.Object) {
public static getOuterPropertyPair(objectNode: parser.Node): PropertyPair | undefined {
if (objectNode.type !== JsonNodeType.Object) {
return undefined;
}
let outerProperty: parser.Node | undefined = node.parent;
let outerProperty: parser.Node | undefined = objectNode.parent;
if (outerProperty && outerProperty.type === JsonNodeType.Array) {
outerProperty = outerProperty.parent;
}
return outerProperty ? IntelliSenseUtility.parseProperty(outerProperty) : undefined;
}
public static isDigitalTwinDefinition(documentNode: parser.Node): boolean {
const contextPath: string[] = [DigitalTwinConstants.CONTEXT];
const contextNode: parser.Node | undefined = parser.findNodeAtLocation(documentNode, contextPath);
if (contextNode && IntelliSenseUtility.isDigitalTwinContext(contextNode)) {
return true;
} else {
return false;
}
}
public static getOuterPropertyClassNode(jsonPropertyNode: parser.Node): ClassNode|undefined {
const outerPropertyObserveName = IntelliSenseUtility.getOuterPropertyObserveName(jsonPropertyNode);
if (!outerPropertyObserveName) {
return undefined;
}
return IntelliSenseUtility.getClassNodeByPropertyName(outerPropertyObserveName);
}
public static getOuterPropertyObserveName(jsonPropertyNode: parser.Node): string|undefined {
const parentObjectNode: parser.Node | undefined = jsonPropertyNode.parent;
if (!parentObjectNode || parentObjectNode.type !== JsonNodeType.Object || !parentObjectNode.children) {
return undefined;
}
const outerPropertyPair: PropertyPair|undefined = IntelliSenseUtility.getOuterPropertyPair(parentObjectNode);
return outerPropertyPair?.name.value;
}
public static getClassNodeByPropertyName(propertyName: string): ClassNode|undefined {
const propertyNode: PropertyNode|undefined = IntelliSenseUtility.getPropertyNodeByName(propertyName);
if (!propertyNode || !propertyNode.type) {
return undefined;
}
return IntelliSenseUtility.getClassNodeByClassId(propertyNode.type);
}
public static getPropertyNodeByName(propertyName: string): PropertyNode|undefined {
return IntelliSenseUtility.graph.getPropertyNodeByName(propertyName);
}
public static getPropertyNodeById(propertyId: string): PropertyNode|undefined {
return IntelliSenseUtility.graph.getPropertyNodeById(propertyId);
}
public static getClassNodeByClassId(classDtmi: string): ClassNode|undefined {
return IntelliSenseUtility.graph.getClassNodeByClassId(classDtmi);
}
public static getClassNodeByClassName(className: string): ClassNode|undefined {
return IntelliSenseUtility.graph.getClassNodeByClassName(className);
}
public static getIdByName(id: string): string|undefined {
return IntelliSenseUtility.graph.getIdByName(id);
}
/**
* check if name is a reserved name
* @param name name
*/
public static isReservedName(name: string): boolean {
return name.startsWith(DigitalTwinConstants.RESERVED);
}
public static isPartitionNode(propertyName: string): boolean {
return propertyName === DigitalTwinConstants.INTERFACE;
}
private static graph: DigitalTwinGraph;
/**