diff --git a/src/features/dfdElements/outputPortBehaviorValidation.ts b/src/features/dfdElements/outputPortBehaviorValidation.ts index 023b74d..f46f7ba 100644 --- a/src/features/dfdElements/outputPortBehaviorValidation.ts +++ b/src/features/dfdElements/outputPortBehaviorValidation.ts @@ -20,14 +20,23 @@ interface PortBehaviorValidationError { */ @injectable() export class PortBehaviorValidator { - // Regex that validates a set statement. + // Regex that validates assignments + // Matches "Assignment({input_Pins};TERM_REGEX;{out_Label})" + private static readonly ASSIGNMENT_REGEX = + /^Assignment\(\{(([A-Za-z0-9_][A-Za-z0-9_\|]+(,\s*[A-Za-z0-9_\|]+)*)?)\};(\s*|!|TRUE|FALSE|\|\||&&|\(|\)|([A-Za-z0-9_]*\.[A-Za-z0-9_]*))+;\{(((([A-Za-z0-9_]+)\.[A-Za-z0-9_]+)+(,\s*([A-Za-z0-9_]+\.[A-Za-z0-9_]+))*)?)\}\)+$/; + + // Regex that validates forwarding + // Matches "Forwarding({input_pins})" + private static readonly FORWARDING_REGEX = + /^Forwarding\(\{[A-Za-z0-9_][A-Za-z0-9_\|]+(,\s*[A-Za-z0-9_][A-Za-z0-9_\|]+)*\}\)$/; + + // Regex that validates a term // Has the label type and label value that should be set as capturing groups. - private static readonly SET_REGEX = - /^set +([A-Za-z][A-Za-z0-9_]*)\.([A-Za-z][A-Za-z0-9_]*) *= *(?: +|!|TRUE|FALSE|\|\||&&|\(|\)|[A-Za-z][A-Za-z0-9_\|]*(?:\.[A-Za-z][A-Za-z0-9_]*){2})+$/; - // Regex that is used to extract all inputs, their label types and label values from a set statement. - // Each input is a match with the input name, label type and label value as capturing groups. - private static readonly SET_REGEX_EXPRESSION_INPUTS = - /([A-Za-z][A-Za-z0-9_\|]*)\.([A-Za-z][A-Za-z0-9_]*)\.([A-Za-z][A-Za-z0-9_]*)/g; + private static readonly TERM_REGEX = + /^(\s*|!|TRUE|FALSE|\|\||&&|\(|\)|([A-Za-z0-9_]+\.[A-Za-z0-9_]+(?![A-Za-z0-9_]*\.[A-Za-z0-9_]*)))+$/g; + + private static readonly LABEL_REGEX = /([A-Za-z0-9_]+)\.([A-Za-z0-9_]+)/g; + // Regex matching alphanumeric characters. public static readonly REGEX_ALPHANUMERIC = /[A-Za-z0-9_\|]+/; @@ -74,11 +83,11 @@ export class PortBehaviorValidator { return; } - if (line.startsWith("forward")) { + if (line.startsWith("Forwarding")) { return this.validateForwardStatement(line, lineNumber, port); } - if (line.startsWith("set")) { + if (line.startsWith("Assignment")) { return this.validateSetStatement(line, lineNumber, port); } @@ -95,7 +104,17 @@ export class PortBehaviorValidator { lineNumber: number, port: DfdOutputPortImpl, ): PortBehaviorValidationError[] | undefined { - const inputsString = line.substring("forward".length); + const match = line.match(PortBehaviorValidator.FORWARDING_REGEX); + if (!match) { + return [ + { + line: lineNumber, + message: "invalid forwarding(Template:Forwarding({in_ports})", + }, + ]; + } + + const inputsString = line.substring("Forwarding({".length, line.length - 2); const inputs = inputsString.split(",").map((input) => input.trim()); if (inputs.filter((input) => input !== "").length === 0) { return [ @@ -197,37 +216,12 @@ export class PortBehaviorValidator { lineNumber: number, port: DfdOutputPortImpl, ): PortBehaviorValidationError[] | undefined { - const match = line.match(PortBehaviorValidator.SET_REGEX); + const match = line.match(PortBehaviorValidator.ASSIGNMENT_REGEX); if (!match) { return [ { line: lineNumber, - message: "invalid set statement", - }, - ]; - } - - // Check that the label type and value that this statement tries to set are valid. - const setLabelType = match[1]; - const setLabelValue = match[2]; - const labelType = this.labelTypeRegistry?.getLabelTypes().find((type) => type.name === setLabelType); - if (!labelType) { - return [ - { - line: lineNumber, - message: `unknown label type: ${setLabelType}`, - colStart: line.indexOf(setLabelType), - colEnd: line.indexOf(setLabelType) + setLabelType.length, - }, - ]; - } - if (!labelType.values.find((value) => value.text === setLabelValue)) { - return [ - { - line: lineNumber, - message: `unknown label value of label type ${setLabelType}: ${setLabelValue}`, - colStart: line.indexOf(setLabelValue), - colEnd: line.indexOf(setLabelValue) + setLabelValue.length, + message: "invalid assignment(Template:Assignment({in_ports}; term; {out_label})", }, ]; } @@ -246,7 +240,7 @@ export class PortBehaviorValidator { return [ { line: lineNumber, - message: "invalid set statement: missing opening parenthesis", + message: "invalid assignment: missing opening parenthesis", colStart: strIdx, colEnd: strIdx + 1, }, @@ -258,60 +252,41 @@ export class PortBehaviorValidator { return [ { line: lineNumber, - message: "invalid set statement: missing closing parenthesis", + message: "invalid assignment: missing closing parenthesis", }, ]; } // Extract all used inputs, label types and the corresponding label values. - const expression = line.split("=")[1].trim(); // get everything after the = - if (expression.length === 0) { + var term = line.split(";")[1].trim(); // get everything after the ; + if (term.length === 0) { return [ { line: lineNumber, - message: "invalid set statement: missing expression", + message: "invalid assignment: missing term", }, ]; } + if (term.indexOf(";") !== -1) { + term = term.split(";")[0]; + } - const matches = [...expression.matchAll(PortBehaviorValidator.SET_REGEX_EXPRESSION_INPUTS)]; - - const node = port.parent; - if (!(node instanceof DfdNodeImpl)) { - throw new Error("Expected port parent to be a DfdNodeImpl."); + const termMatch = term.match(PortBehaviorValidator.TERM_REGEX); + if (!termMatch) { + return [ + { + line: lineNumber, + message: "invalid term", + }, + ]; } - const availableInputs = node.getAvailableInputs(); - // Check for each input access that the input exists and that the label type and value are valid. + const matches = [...term.matchAll(PortBehaviorValidator.LABEL_REGEX)]; const inputAccessErrors = []; - for (const inputMatch of matches) { - const inputName = inputMatch[1]; - const inputLabelType = inputMatch[2]; - const inputLabelValue = inputMatch[3]; - - if (!availableInputs.includes(inputName)) { - // Find all occurrences of the unavailable input. - let idx = line.indexOf(inputName); - while (idx !== -1) { - // Check that this is not a substring of another input. - if ( - // before must not be alphanumeric => start of this string must be the beginning of the input name - line[idx - 1]?.match(PortBehaviorValidator.REGEX_ALPHANUMERIC) && - line[idx + inputName.length] === "." // must be followed by a dot to access the label type of the input - ) { - inputAccessErrors.push({ - line: lineNumber, - message: `invalid/unknown input: ${inputName}`, - colStart: idx, - colEnd: idx + inputName.length, - }); - } - - idx = line.indexOf(inputName, idx + 1); - } - continue; - } + for (const inputMatch of matches) { + const inputLabelType = inputMatch[1]; + const inputLabelValue = inputMatch[2]; const inputLabelTypeObject = this.labelTypeRegistry ?.getLabelTypes() @@ -335,7 +310,11 @@ export class PortBehaviorValidator { idx = line.indexOf(inputLabelType, idx + 1); } - } else if (!inputLabelTypeObject.values.find((value) => value.text === inputLabelValue)) { + } else if ( + inputLabelValue === undefined || + inputLabelValue === "" || + !inputLabelTypeObject.values.find((value) => value.text === inputLabelValue) + ) { let idx = line.indexOf(inputLabelValue); while (idx !== -1) { // Check that this is not a substring of another label value. @@ -357,8 +336,140 @@ export class PortBehaviorValidator { idx = line.indexOf(inputLabelValue, idx + 1); } } + + console.log(inputMatch); + + if (inputMatch[3] !== undefined) { + inputAccessErrors.push({ + line: lineNumber, + message: `invalid label definition`, + }); + } + } + + const node = port.parent; + if (!(node instanceof DfdNodeImpl)) { + throw new Error("Expected port parent to be a DfdNodeImpl."); + } + const availableInputs = node.getAvailableInputs(); + + const innerContent = line.substring("Assignment(".length, line.length - 1); + + // Step 2: Split by the semicolons to separate the blocks + const parts = innerContent.split(";").map((part) => part.trim()); + + const inPorts = parts[0] + .substring(1, parts[0].length - 1) + .split(",") + .map((variable) => variable.trim()); + const outLabel = parts[2] + .substring(1, parts[2].length - 1) + .split(",") + .map((variable) => variable.trim()); + + // Check for each input access that the input exists and that the label type and value are valid. + + for (const inPortName of inPorts) { + if (!availableInputs.includes(inPortName) && inPortName !== "") { + // Find all occurrences of the unavailable input. + let idx = line.indexOf(inPortName); + inputAccessErrors.push({ + line: lineNumber, + message: `invalid/unknown input: ${inPortName}`, + colStart: idx, + colEnd: idx + inPortName.length, + }); + + continue; + } + } + + for (const typeValuePair of outLabel) { + if (typeValuePair === "") continue; + + const inputLabelType = typeValuePair.split(".")[0].trim(); + const inputLabelTypeObject = this.labelTypeRegistry + ?.getLabelTypes() + .find((type) => type.name === inputLabelType); + if (!inputLabelTypeObject) { + let idx = line.indexOf(inputLabelType); + while (idx !== -1) { + // Check that this is not a substring of another label type. + if ( + // must start after a dot and end before a dot + line[idx - 1] === "." && + line[idx + inputLabelType.length] === "." + ) { + inputAccessErrors.push({ + line: lineNumber, + message: `unknown label type: ${inputLabelType}`, + colStart: idx, + colEnd: idx + inputLabelType.length, + }); + } + + idx = line.indexOf(inputLabelType, idx + 1); + } + } + + if (typeValuePair.indexOf(".") !== -1) { + if (typeValuePair.split(".")[1] === null || typeValuePair.split(".")[1] === "") continue; + const inputLabelValue = typeValuePair.split(".")[1].trim(); + + const inputLabelTypeObject = this.labelTypeRegistry + ?.getLabelTypes() + .find((type) => type.name === inputLabelType); + if (!inputLabelTypeObject) { + let idx = line.indexOf(inputLabelType); + while (idx !== -1) { + // Check that this is not a substring of another label type. + if ( + // must start after a dot and end before a dot + line[idx - 1] === "." && + line[idx + inputLabelType.length] === "." + ) { + inputAccessErrors.push({ + line: lineNumber, + message: `unknown label type: ${inputLabelType}`, + colStart: idx, + colEnd: idx + inputLabelType.length, + }); + } + + idx = line.indexOf(inputLabelType, idx + 1); + } + } else if (!inputLabelTypeObject.values.find((value) => value.text === inputLabelValue)) { + let idx = line.indexOf(inputLabelValue); + while (idx !== -1) { + // Check that this is not a substring of another label value. + if ( + // must start after a dot and end at the end of the alphanumeric text + line[idx - 1] === "." && + // Might be at the end of the line + (!line[idx + inputLabelValue.length] || + !line[idx + inputLabelValue.length].match(PortBehaviorValidator.REGEX_ALPHANUMERIC)) + ) { + inputAccessErrors.push({ + line: lineNumber, + message: `unknown label value of label type ${inputLabelType}: ${inputLabelValue}`, + colStart: idx, + colEnd: idx + inputLabelValue.length, + }); + } + + idx = line.indexOf(inputLabelValue, idx + 1); + } + } + } + + if (typeValuePair.split(".")[2] !== undefined) { + inputAccessErrors.push({ + line: lineNumber, + message: `invalid label definition`, + }); + } } - return inputAccessErrors.length > 0 ? inputAccessErrors : undefined; + return inputAccessErrors.length > 0 ? inputAccessErrors : []; } } diff --git a/src/features/dfdElements/outputPortEditUi.ts b/src/features/dfdElements/outputPortEditUi.ts index 6d7a28e..61dfb39 100644 --- a/src/features/dfdElements/outputPortEditUi.ts +++ b/src/features/dfdElements/outputPortEditUi.ts @@ -82,7 +82,7 @@ export class OutputPortEditUIMouseListener extends MouseListener { } // More information and playground website for testing: https://microsoft.github.io/monaco-editor/monarch.html -const statementKeywords = ["forward", "set"]; +const statementKeywords = ["Forwarding({})", "Assignment({})"]; const constantsKeywords = ["TRUE", "FALSE"]; const dfdBehaviorLanguageMonarchDefinition: monaco.languages.IMonarchLanguage = { keywords: [...statementKeywords, ...constantsKeywords], @@ -140,7 +140,7 @@ class MonacoEditorDfdBehaviorCompletionProvider implements monaco.languages.Comp // Auto open completions after typing a dot. Useful for the set statement where // components are delimited by dots. - triggerCharacters = ["."]; + triggerCharacters = [".", ";", "{"]; provideCompletionItems( model: monaco.editor.ITextModel, @@ -159,7 +159,8 @@ class MonacoEditorDfdBehaviorCompletionProvider implements monaco.languages.Comp suggestions: statementKeywords.map((keyword) => ({ label: keyword, kind: monaco.languages.CompletionItemKind.Keyword, - insertText: keyword, + insertText: keyword.slice(0, -2) + "$0" + keyword.slice(-2), + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, // Treat insertText as a snippet // Replace full line with new statement start keyword range: new monaco.Range( position.lineNumber, @@ -180,15 +181,37 @@ class MonacoEditorDfdBehaviorCompletionProvider implements monaco.languages.Comp const availableInputs = parent.getAvailableInputs().filter((input) => input !== undefined) as string[]; + const curlyBracketCompletion = { + label: "{", + kind: monaco.languages.CompletionItemKind.Snippet, + insertText: "$0}", // Automatically add closing curly bracket and position cursor inside + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + range: new monaco.Range(position.lineNumber, position.column, position.lineNumber, position.column), + }; + + // Add the curly bracket completion when the user types a curly bracket + if ( + model.getValueInRange( + new monaco.Range(position.lineNumber, position.column - 1, position.lineNumber, position.column), + ) === "{" && + model.getValueInRange( + new monaco.Range(position.lineNumber, position.column, position.lineNumber, position.column + 1), + ) !== "}" + ) { + return { + suggestions: [curlyBracketCompletion], + }; + } + // Suggestions per statement type switch (statementType?.word) { - case "set": + case "Assignment": return { - suggestions: this.getSetStatementCompletions(model, position, availableInputs), + suggestions: this.getAssignmentCompletions(model, position, availableInputs), }; - case "forward": + case "Forwarding": return { - suggestions: this.getInputCompletions(model, position, availableInputs), + suggestions: this.getForwardingCompletions(model, position, availableInputs), }; } @@ -198,53 +221,111 @@ class MonacoEditorDfdBehaviorCompletionProvider implements monaco.languages.Comp }; } - private getSetStatementCompletions( + private getAssignmentCompletions( model: monaco.editor.ITextModel, position: monaco.Position, availableInputs: string[], ): monaco.languages.CompletionItem[] { const line = model.getLineContent(position.lineNumber); + const column = position.column; + + // Check if we're inside the input list (i.e., inside first curly braces `{}`) + const openBraceIndex = line.indexOf("{"); + const closingBraceIndex = line.indexOf("}"); + + // If the first semicolon hasn't been typed yet, assume we're inside the input list + if (openBraceIndex !== -1 && (closingBraceIndex === -1 || column <= closingBraceIndex + 1)) { + // Inside `{List of available inputs}` section + return this.getInputCompletions(model, position, availableInputs); + } + + // If the second semicolon hasn't been typed yet, assume we're typing in the term section or outPorts list + const firstSemicolonIndex = line.indexOf(";"); + const secondSemicolonIndex = line.indexOf(";", firstSemicolonIndex + 1); + const secondOpenBraceIndex = line.indexOf("{", openBraceIndex + 1); + + if (secondSemicolonIndex !== -1 && column > secondSemicolonIndex + 1) { + // If the second semicolon hasn't been typed but we're inside the second curly brace, assume it's outPorts + if (secondOpenBraceIndex !== -1 && column > secondOpenBraceIndex) { + // We're inside the `{List of outPorts}` section + return this.getOutLabelCompletions(model, position); + } else { + return [ + { + label: "{", + kind: monaco.languages.CompletionItemKind.Snippet, + insertText: "{$0}", // Automatically add closing curly bracket and position cursor inside + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + range: new monaco.Range( + position.lineNumber, + position.column, + position.lineNumber, + position.column, + ), + }, + ]; + } + } + + if (line.charAt(column - 2) === ".") { + // If the last character is a ".", return only getOutLabelCompletions + return this.getOutLabelCompletions(model, position); + } - // Find the start of the current expression - // -1 because the column is to the right of the last char => last filled column is -1 + const constantsCompletions = this.getConstantsCompletions(model, position); + const outLabelCompletions = this.getOutLabelCompletions(model, position); + + // Return combined completions + return [...constantsCompletions, ...outLabelCompletions]; + } + + private getForwardingCompletions( + model: monaco.editor.ITextModel, + position: monaco.Position, + availableInputs: string[], + ): monaco.languages.CompletionItem[] { + const line = model.getLineContent(position.lineNumber); + const column = position.column; + + // Check if we're inside the input list (i.e., inside first curly braces `{}`) + const openBraceIndex = line.indexOf("{"); + + // If the first semicolon hasn't been typed yet, assume we're inside the input list + if (openBraceIndex !== -1 && column > openBraceIndex) { + // Inside `{List of available inputs}` section + return this.getInputCompletions(model, position, availableInputs); + } else { + return []; + } + } + + private getOutLabelCompletions( + model: monaco.editor.ITextModel, + position: monaco.Position, + ): monaco.languages.CompletionItem[] { + const line = model.getLineContent(position.lineNumber); + + // Find the start of the current expression (Type or value) let currentExpressionStart = position.column - 1; while (currentExpressionStart > 0) { - const currentChar = line[currentExpressionStart - 1]; // column is 1-based but array is 0-based => -1 - - if (currentChar !== "." && !currentChar.match(PortBehaviorValidator.REGEX_ALPHANUMERIC)) { + const currentChar = line[currentExpressionStart - 1]; // column is 1-based, array is 0-based + if (currentChar !== "." && !currentChar.match(/[A-Za-z0-9_]/)) { break; } - currentExpressionStart--; } - const currentExpression = line.substring(currentExpressionStart - 1, position.column); + const currentExpression = line.substring(currentExpressionStart, position.column); const expressionParts = currentExpression.split("."); - // Check whether the position is the assignment target (aka the left side of the "=" or missing equals) - const equalsIdx = line.indexOf("="); - const isTargetLabel = equalsIdx == -1 || equalsIdx > currentExpressionStart; - if (isTargetLabel) { - // Left hand side: labelType.labelValue (is for the target node, so we don't need to specify) - if (expressionParts.length === 1) { + switch (expressionParts.length) { + case 1: + // If there's only one part, we're completing the `Type` return this.getLabelTypeCompletions(model, position); - } else { - return this.getLabelValueCompletions(model, position, expressionParts[0]); - } - } else { - // Right hand side: input.labelType.labelValue or constant - switch (expressionParts.length) { - case 1: - return [ - ...this.getInputCompletions(model, position, availableInputs), - ...this.getConstantsCompletions(model, position), - ]; - case 2: - return this.getLabelTypeCompletions(model, position); - case 3: - const labelTypeName = expressionParts[1]; - return this.getLabelValueCompletions(model, position, labelTypeName); - } + case 2: + // If there's already a dot, we complete the `value` for the specific `Type` + const labelTypeName = expressionParts[0]; + return this.getLabelValueCompletions(model, position, labelTypeName); } return []; diff --git a/src/features/serialize/defaultDiagram.json b/src/features/serialize/defaultDiagram.json index 340033b..58312fd 100644 --- a/src/features/serialize/defaultDiagram.json +++ b/src/features/serialize/defaultDiagram.json @@ -1,10 +1,16 @@ { "model": { - "scroll": { + "canvasBounds": { "x": 0, - "y": 0 + "y": 0, + "width": 1278, + "height": 1324 + }, + "scroll": { + "x": 181.68489464915504, + "y": -12.838536201820945 }, - "zoom": 3.854984894259819, + "zoom": 6.057478948161569, "position": { "x": 0, "y": 0 @@ -13,6 +19,7 @@ "width": -1, "height": -1 }, + "features": {}, "type": "graph", "id": "root", "children": [ @@ -39,8 +46,8 @@ "ports": [ { "position": { - "x": 31, - "y": 38.5 + "x": 58.5, + "y": 7 }, "size": { "width": -1, @@ -50,16 +57,15 @@ "selected": false, "hoverFeedback": false, "opacity": 1, - "behavior": "set Sensitivity.Personal = TRUE", "features": {}, - "id": "4wbyft", - "type": "port:dfd-output", + "id": "nhcrad", + "type": "port:dfd-input", "children": [] }, { "position": { - "x": 58.5, - "y": 7 + "x": 31, + "y": 38.5 }, "size": { "width": -1, @@ -69,9 +75,10 @@ "selected": false, "hoverFeedback": false, "opacity": 1, + "behavior": "Assignment({};TRUE;{Sensitivity.Personal})", "features": {}, - "id": "nhcrad", - "type": "port:dfd-input", + "id": "4wbyft", + "type": "port:dfd-output", "children": [] }, { @@ -87,7 +94,7 @@ "selected": false, "hoverFeedback": false, "opacity": 1, - "behavior": "set Sensitivity.Public = TRUE", + "behavior": "Assignment({};TRUE;{Sensitivity.Public})", "features": {}, "id": "wksxi8", "type": "port:dfd-output", @@ -101,8 +108,8 @@ }, { "position": { - "x": 422.5, - "y": 59 + "x": 249, + "y": 67 }, "size": { "width": -1, @@ -112,18 +119,13 @@ "selected": false, "hoverFeedback": false, "opacity": 1, - "text": "Database", - "labels": [ - { - "labelTypeId": "gvia09", - "labelTypeValueId": "5hnugm" - } - ], + "text": "view", + "labels": [], "ports": [ { "position": { "x": -3.5, - "y": 5 + "y": 13 }, "size": { "width": -1, @@ -133,40 +135,40 @@ "selected": false, "hoverFeedback": false, "opacity": 1, - "behavior": "set Sensitivity.Public = TRUE", "features": {}, - "id": "1j7bn5", - "type": "port:dfd-output", + "id": "ti4ri7", + "type": "port:dfd-input", "children": [] }, { "position": { - "x": -3.5, - "y": 28 + "x": 58.5, + "y": 13 }, "size": { "width": -1, "height": -1 }, "strokeWidth": 0, - "selected": true, - "hoverFeedback": true, + "selected": false, + "hoverFeedback": false, "opacity": 1, + "behavior": "Forwarding({request})", "features": {}, - "id": "scljwi", - "type": "port:dfd-input", + "id": "bsqjm", + "type": "port:dfd-output", "children": [] } ], "features": {}, - "id": "8j2r1g", - "type": "node:storage", + "id": "0bh7yh", + "type": "node:function", "children": [] }, { "position": { "x": 249, - "y": 67 + "y": 22 }, "size": { "width": -1, @@ -176,13 +178,13 @@ "selected": false, "hoverFeedback": false, "opacity": 1, - "text": "view", + "text": "display", "labels": [], "ports": [ { "position": { "x": 58.5, - "y": 13 + "y": 15 }, "size": { "width": -1, @@ -192,40 +194,40 @@ "selected": false, "hoverFeedback": false, "opacity": 1, - "behavior": "forward request", "features": {}, - "id": "bsqjm", - "type": "port:dfd-output", + "id": "0hfzu", + "type": "port:dfd-input", "children": [] }, { "position": { "x": -3.5, - "y": 13 + "y": 9 }, "size": { "width": -1, "height": -1 }, "strokeWidth": 0, - "selected": false, + "selected": true, "hoverFeedback": false, "opacity": 1, + "behavior": "Forwarding({items})", "features": {}, - "id": "ti4ri7", - "type": "port:dfd-input", + "id": "y1p7qq", + "type": "port:dfd-output", "children": [] } ], "features": {}, - "id": "0bh7yh", + "id": "4myuyr", "type": "node:function", "children": [] }, { "position": { - "x": 249, - "y": 22 + "x": 364, + "y": 152 }, "size": { "width": -1, @@ -235,13 +237,13 @@ "selected": false, "hoverFeedback": false, "opacity": 1, - "text": "display", + "text": "encrypt", "labels": [], "ports": [ { "position": { - "x": 58.5, - "y": 15 + "x": -3.5, + "y": 15.5 }, "size": { "width": -1, @@ -252,14 +254,14 @@ "hoverFeedback": false, "opacity": 1, "features": {}, - "id": "0hfzu", + "id": "kqjy4g", "type": "port:dfd-input", "children": [] }, { "position": { - "x": -3.5, - "y": 9 + "x": 29, + "y": -3.5 }, "size": { "width": -1, @@ -269,48 +271,22 @@ "selected": false, "hoverFeedback": false, "opacity": 1, - "behavior": "forward items", + "behavior": "Forwarding({data})\nAssignment({};TRUE;{Encryption.Encrypted})", "features": {}, - "id": "y1p7qq", + "id": "3wntc", "type": "port:dfd-output", "children": [] } ], "features": {}, - "id": "4myuyr", + "id": "3n988k", "type": "node:function", "children": [] }, - { - "routingPoints": [], - "selected": false, - "hoverFeedback": false, - "opacity": 1, - "features": {}, - "id": "n81f3b", - "type": "edge:arrow", - "sourceId": "1j7bn5", - "targetId": "0hfzu", - "text": "items", - "children": [] - }, - { - "routingPoints": [], - "selected": false, - "hoverFeedback": false, - "opacity": 1, - "features": {}, - "id": "hi397b", - "type": "edge:arrow", - "sourceId": "y1p7qq", - "targetId": "nhcrad", - "text": "items", - "children": [] - }, { "position": { - "x": 364, - "y": 152 + "x": 104, + "y": 157 }, "size": { "width": -1, @@ -320,12 +296,12 @@ "selected": false, "hoverFeedback": false, "opacity": 1, - "text": "encrypt", + "text": "buy", "labels": [], "ports": [ { "position": { - "x": 29, + "x": 19, "y": -3.5 }, "size": { @@ -336,16 +312,15 @@ "selected": false, "hoverFeedback": false, "opacity": 1, - "behavior": "forward data\nset Encryption.Encrypted = TRUE", "features": {}, - "id": "3wntc", - "type": "port:dfd-output", + "id": "2331e8", + "type": "port:dfd-input", "children": [] }, { "position": { - "x": -3.5, - "y": 15.5 + "x": 58.5, + "y": 10.5 }, "size": { "width": -1, @@ -355,20 +330,21 @@ "selected": false, "hoverFeedback": false, "opacity": 1, + "behavior": "Forwarding({data})", "features": {}, - "id": "kqjy4g", - "type": "port:dfd-input", + "id": "vnkg73", + "type": "port:dfd-output", "children": [] } ], "features": {}, - "id": "3n988k", + "id": "z9v1jp", "type": "node:function", "children": [] }, { "position": { - "x": 104, + "x": 233.5, "y": 157 }, "size": { @@ -379,13 +355,13 @@ "selected": false, "hoverFeedback": false, "opacity": 1, - "text": "buy", + "text": "process", "labels": [], "ports": [ { "position": { - "x": 19, - "y": -3.5 + "x": -3.5, + "y": 10.5 }, "size": { "width": -1, @@ -396,13 +372,13 @@ "hoverFeedback": false, "opacity": 1, "features": {}, - "id": "2331e8", + "id": "xyepdb", "type": "port:dfd-input", "children": [] }, { "position": { - "x": 58.5, + "x": 59.5, "y": 10.5 }, "size": { @@ -413,35 +389,22 @@ "selected": false, "hoverFeedback": false, "opacity": 1, - "behavior": "forward data", + "behavior": "Forwarding({data})", "features": {}, - "id": "vnkg73", + "id": "eedb56", "type": "port:dfd-output", "children": [] } ], "features": {}, - "id": "z9v1jp", + "id": "js61f", "type": "node:function", "children": [] }, - { - "routingPoints": [], - "selected": false, - "hoverFeedback": false, - "opacity": 1, - "features": {}, - "id": "vq8g3l", - "type": "edge:arrow", - "sourceId": "4wbyft", - "targetId": "2331e8", - "text": "data", - "children": [] - }, { "position": { - "x": 233.5, - "y": 157 + "x": 422.5, + "y": 59 }, "size": { "width": -1, @@ -451,13 +414,18 @@ "selected": false, "hoverFeedback": false, "opacity": 1, - "text": "process", - "labels": [], + "text": "Database", + "labels": [ + { + "labelTypeId": "gvia09", + "labelTypeValueId": "5hnugm" + } + ], "ports": [ { "position": { "x": -3.5, - "y": 10.5 + "y": 23 }, "size": { "width": -1, @@ -468,14 +436,14 @@ "hoverFeedback": false, "opacity": 1, "features": {}, - "id": "xyepdb", + "id": "scljwi", "type": "port:dfd-input", "children": [] }, { "position": { - "x": 59.5, - "y": 10.5 + "x": -3.5, + "y": 0.5 }, "size": { "width": -1, @@ -485,16 +453,29 @@ "selected": false, "hoverFeedback": false, "opacity": 1, - "behavior": "forward data", + "behavior": "Assignment({};TRUE;{Sensitivity.Public})", "features": {}, - "id": "eedb56", + "id": "1j7bn5", "type": "port:dfd-output", "children": [] } ], "features": {}, - "id": "js61f", - "type": "node:function", + "id": "8j2r1g", + "type": "node:storage", + "children": [] + }, + { + "routingPoints": [], + "selected": false, + "hoverFeedback": false, + "opacity": 1, + "features": {}, + "id": "vq8g3l", + "type": "edge:arrow", + "sourceId": "4wbyft", + "targetId": "2331e8", + "text": "data", "children": [] }, { @@ -523,6 +504,32 @@ "text": "data", "children": [] }, + { + "routingPoints": [], + "selected": false, + "hoverFeedback": false, + "opacity": 1, + "features": {}, + "id": "ojjvtp", + "type": "edge:arrow", + "sourceId": "3wntc", + "targetId": "scljwi", + "text": "data", + "children": [] + }, + { + "routingPoints": [], + "selected": false, + "hoverFeedback": false, + "opacity": 1, + "features": {}, + "id": "c9n88l", + "type": "edge:arrow", + "sourceId": "bsqjm", + "targetId": "scljwi", + "text": "request", + "children": [] + }, { "routingPoints": [], "selected": false, @@ -543,11 +550,11 @@ "hoverFeedback": false, "opacity": 1, "features": {}, - "id": "ojjvtp", + "id": "n81f3b", "type": "edge:arrow", - "sourceId": "3wntc", - "targetId": "scljwi", - "text": "data", + "sourceId": "1j7bn5", + "targetId": "0hfzu", + "text": "items", "children": [] }, { @@ -556,11 +563,11 @@ "hoverFeedback": false, "opacity": 1, "features": {}, - "id": "c9n88l", + "id": "hi397b", "type": "edge:arrow", - "sourceId": "bsqjm", - "targetId": "scljwi", - "text": "request", + "sourceId": "y1p7qq", + "targetId": "nhcrad", + "text": "items", "children": [] } ]