Serialization fixed

This commit is contained in:
barsdeveloper
2021-11-08 22:28:26 +01:00
parent ad8c34cdab
commit 39e26bc0c2
12 changed files with 195 additions and 48 deletions

119
dist/ueblueprint.js vendored
View File

@@ -1,4 +1,5 @@
class Primitive { class Primitive {
toString() { toString() {
return "Unimplemented for " + this.constructor.name return "Unimplemented for " + this.constructor.name
} }
@@ -139,6 +140,7 @@ class Utility {
} }
class Entity { class Entity {
constructor(options = {}) { constructor(options = {}) {
/** /**
* *
@@ -266,16 +268,28 @@ class LocalizedTextEntity extends Entity {
getAttributes() { getAttributes() {
return LocalizedTextEntity.attributes return LocalizedTextEntity.attributes
} }
}
toString() {
"NSLOCTEXT(" + `"${this.namespace}"` + ", " + `"${this.key}"` + ", " + `"${this.value}"` + ")"; class PathSymbol extends Primitive {
}
constructor(value) {
super();
this.value = new String(value).valueOf();
}
valueOf() {
this.value;
}
toString() {
return this.value
}
} }
class PinReferenceEntity extends Entity { class PinReferenceEntity extends Entity {
static attributes = { static attributes = {
objectName: String, objectName: PathSymbol,
pinGuid: Guid pinGuid: Guid
} }
@@ -405,8 +419,8 @@ class Grammar {
String = _ => P.regex(/(?:[^"\\]|\\")*/).wrap(P.string('"'), P.string('"')).desc('string (with possibility to escape the quote using \")') String = _ => P.regex(/(?:[^"\\]|\\")*/).wrap(P.string('"'), P.string('"')).desc('string (with possibility to escape the quote using \")')
Word = _ => P.regex(/[a-zA-Z]+/).desc("a word") Word = _ => P.regex(/[a-zA-Z]+/).desc("a word")
Guid = _ => P.regex(/[0-9a-zA-Z]{32}/).map(v => new Guid(v)).desc("32 digit hexadecimal (accepts all the letters for safety) value") Guid = _ => P.regex(/[0-9a-zA-Z]{32}/).map(v => new Guid(v)).desc("32 digit hexadecimal (accepts all the letters for safety) value")
PathSymbol = _ => P.regex(/[0-9a-zA-Z_]+/) PathSymbol = _ => P.regex(/[0-9a-zA-Z_]+/).map(v => new PathSymbol(v))
ReferencePath = r => P.seq(P.string("/"), r.PathSymbol.sepBy1(P.string(".")).tieWith(".")) ReferencePath = r => P.seq(P.string("/"), r.PathSymbol.map(v => v.toString()).sepBy1(P.string(".")).tieWith("."))
.tie() .tie()
.atLeast(2) .atLeast(2)
.tie() .tie()
@@ -591,6 +605,7 @@ class Serializer {
if (value === null) { if (value === null) {
return "()" return "()"
} }
const serialize = v => SerializerFactory.getSerializer(Utility.getType(v)).write(v);
// This is an exact match (and not instanceof) to hit also primitive types (by accessing value.constructor they are converted to objects automatically) // This is an exact match (and not instanceof) to hit also primitive types (by accessing value.constructor they are converted to objects automatically)
switch (value?.constructor) { switch (value?.constructor) {
case Function: case Function:
@@ -602,8 +617,11 @@ class Serializer {
case String: case String:
return `"${value}"` return `"${value}"`
} }
if (value instanceof Array) {
return `(${value.map(v => serialize(v) + ",")})`
}
if (value instanceof Entity) { if (value instanceof Entity) {
return SerializerFactory.getSerializer(Utility.getType(value)).write(value) return serialize(value)
} }
if (value instanceof Primitive) { if (value instanceof Primitive) {
return value.toString() return value.toString()
@@ -619,7 +637,8 @@ class Serializer {
const value = object[property]; const value = object[property];
if (object[property]?.constructor === Object) { if (object[property]?.constructor === Object) {
// Recursive call when finding an object // Recursive call when finding an object
result += this.subWrite(fullKey, value, this.prefix, this.separator); result += (result.length ? this.separator : "")
+ this.subWrite(fullKey, value);
} else if (this.showProperty(fullKey, value)) { } else if (this.showProperty(fullKey, value)) {
result += (result.length ? this.separator : "") result += (result.length ? this.separator : "")
+ this.prefix + this.prefix
@@ -628,7 +647,7 @@ class Serializer {
+ this.writeValue(value); + this.writeValue(value);
} }
} }
if (this.trailingSeparator && result.length) { if (this.trailingSeparator && result.length && fullKey.length === 0) {
// append separator at the end if asked and there was printed content // append separator at the end if asked and there was printed content
result += this.separator; result += this.separator;
} }
@@ -647,9 +666,9 @@ class Serializer {
class GeneralSerializer extends Serializer { class GeneralSerializer extends Serializer {
constructor(keyword, entityType, prefix, separator, trailingSeparator, attributeValueConjunctionSign, attributeKeyPrinter) { constructor(wrap, entityType, prefix, separator, trailingSeparator, attributeValueConjunctionSign, attributeKeyPrinter) {
super(entityType, prefix, separator, trailingSeparator, attributeValueConjunctionSign, attributeKeyPrinter); super(entityType, prefix, separator, trailingSeparator, attributeValueConjunctionSign, attributeKeyPrinter);
this.keyword = keyword ?? ""; this.wrap = wrap ?? (v => `(${v})`);
} }
read(value) { read(value) {
@@ -663,7 +682,7 @@ class GeneralSerializer extends Serializer {
} }
write(object) { write(object) {
let result = `${this.keyword}(${this.subWrite([], object)})`; let result = this.wrap(this.subWrite([], object));
return result return result
} }
} }
@@ -714,7 +733,7 @@ class ObjectSerializer extends Serializer {
* @returns * @returns
*/ */
write(object) { write(object) {
let result = `Begin Object Class=${object.Class} Name=${object.Name} let result = `Begin Object Class=${object.Class} Name="${object.Name}"
${this.subWrite([], object) ${this.subWrite([], object)
+ object + object
.CustomProperties.map(pin => this.separator + this.prefix + "CustomProperties " + SerializerFactory.getSerializer(PinEntity).write(pin)) .CustomProperties.map(pin => this.separator + this.prefix + "CustomProperties " + SerializerFactory.getSerializer(PinEntity).write(pin))
@@ -1604,9 +1623,7 @@ class Paste extends Context {
constructor(target, blueprint, options = {}) { constructor(target, blueprint, options = {}) {
options.wantsFocusCallback = true; options.wantsFocusCallback = true;
super(target, blueprint, options); super(target, blueprint, options);
this.serializer = new ObjectSerializer(); this.serializer = new ObjectSerializer();
let self = this; let self = this;
this.pasteHandle = e => self.pasted(e.clipboardData.getData("Text")); this.pasteHandle = e => self.pasted(e.clipboardData.getData("Text"));
} }
@@ -1630,8 +1647,11 @@ class Paste extends Context {
}); });
let mousePosition = this.blueprint.entity.mousePosition; let mousePosition = this.blueprint.entity.mousePosition;
nodes.forEach(node => { nodes.forEach(node => {
node.location[0] += mousePosition[0] - left; const locationOffset = [
node.location[1] += mousePosition[1] - top; mousePosition[0] - left,
mousePosition[1] - top
];
node.addLocation(locationOffset);
}); });
this.blueprint.addNode(...nodes); this.blueprint.addNode(...nodes);
} }
@@ -1747,6 +1767,30 @@ class Zoom extends MouseWheel {
} }
} }
class Copy extends Context {
constructor(target, blueprint, options = {}) {
options.wantsFocusCallback = true;
super(target, blueprint, options);
this.serializer = new ObjectSerializer();
let self = this;
this.copyHandle = _ => self.copied();
}
blueprintFocused() {
document.body.addEventListener("copy", this.copyHandle);
}
blueprintUnfocused() {
document.body.removeEventListener("copy", this.copyHandle);
}
copied() {
const value = this.blueprint.getNodes(true).map(node => this.serializer.write(node.entity)).join("\n");
navigator.clipboard.writeText(value);
}
}
/** @typedef {import("./graph/GraphNode").default} GraphNode */ /** @typedef {import("./graph/GraphNode").default} GraphNode */
class Blueprint extends GraphElement { class Blueprint extends GraphElement {
@@ -1807,6 +1851,7 @@ class Blueprint extends GraphElement {
this.querySelector('[data-nodes]').append(...this.entity.nodes); this.querySelector('[data-nodes]').append(...this.entity.nodes);
this.copyObject = new Copy(this.getGridDOMElement(), this);
this.pasteObject = new Paste(this.getGridDOMElement(), this); this.pasteObject = new Paste(this.getGridDOMElement(), this);
this.dragObject = new DragScroll(this.getGridDOMElement(), this, { this.dragObject = new DragScroll(this.getGridDOMElement(), this, {
@@ -2027,8 +2072,18 @@ class Blueprint extends GraphElement {
* *
* @returns {GraphNode[]} Nodes * @returns {GraphNode[]} Nodes
*/ */
getNodes() { getNodes(selected = false) {
return this.entity.nodes if (selected) {
return this.entity.nodes.filter(
/**
*
* @param {GraphNode} node
*/
node => node.selected
)
} else {
return this.entity.nodes
}
} }
/** /**
@@ -2098,9 +2153,25 @@ class GraphLink extends GraphElement {
customElements.define('u-link', GraphLink); customElements.define('u-link', GraphLink);
SerializerFactory.registerSerializer(ObjectEntity, new ObjectSerializer()); SerializerFactory.registerSerializer(
SerializerFactory.registerSerializer(PinEntity, new GeneralSerializer("Pin ", PinEntity, "", ",", true)); ObjectEntity,
SerializerFactory.registerSerializer(FunctionReferenceEntity, new GeneralSerializer("", FunctionReferenceEntity, "", ",", false)); new ObjectSerializer()
SerializerFactory.registerSerializer(LocalizedTextEntity, new GeneralSerializer("NSLOCTEXT", LocalizedTextEntity, "", ",", false, "", _ => "")); );
SerializerFactory.registerSerializer(
PinEntity,
new GeneralSerializer(v => `Pin (${v})`, PinEntity, "", ",", true)
);
SerializerFactory.registerSerializer(
FunctionReferenceEntity,
new GeneralSerializer(v => `(${v})`, FunctionReferenceEntity, "", ",", false)
);
SerializerFactory.registerSerializer(
LocalizedTextEntity,
new GeneralSerializer(v => `NSLOCTEXT(${v})`, LocalizedTextEntity, "", ",", false, "", _ => "")
);
SerializerFactory.registerSerializer(
PinReferenceEntity,
new GeneralSerializer(v => v, PinReferenceEntity, "", " ", false, "", _ => "")
);
export { Blueprint, GraphLink, GraphNode }; export { Blueprint, GraphLink, GraphNode };

View File

@@ -9,6 +9,7 @@ import Select from "./input/Select"
import Unfocus from "./input/Unfocus" import Unfocus from "./input/Unfocus"
import Utility from "./Utility" import Utility from "./Utility"
import Zoom from "./input/Zoom" import Zoom from "./input/Zoom"
import Copy from "./input/Copy"
/** @typedef {import("./graph/GraphNode").default} GraphNode */ /** @typedef {import("./graph/GraphNode").default} GraphNode */
export default class Blueprint extends GraphElement { export default class Blueprint extends GraphElement {
@@ -70,6 +71,7 @@ export default class Blueprint extends GraphElement {
this.querySelector('[data-nodes]').append(...this.entity.nodes) this.querySelector('[data-nodes]').append(...this.entity.nodes)
this.copyObject = new Copy(this.getGridDOMElement(), this)
this.pasteObject = new Paste(this.getGridDOMElement(), this) this.pasteObject = new Paste(this.getGridDOMElement(), this)
this.dragObject = new DragScroll(this.getGridDOMElement(), this, { this.dragObject = new DragScroll(this.getGridDOMElement(), this, {
@@ -290,8 +292,18 @@ export default class Blueprint extends GraphElement {
* *
* @returns {GraphNode[]} Nodes * @returns {GraphNode[]} Nodes
*/ */
getNodes() { getNodes(selected = false) {
return this.entity.nodes if (selected) {
return this.entity.nodes.filter(
/**
*
* @param {GraphNode} node
*/
node => node.selected
)
} else {
return this.entity.nodes
}
} }
/** /**

View File

@@ -11,8 +11,4 @@ export default class LocalizedTextEntity extends Entity {
getAttributes() { getAttributes() {
return LocalizedTextEntity.attributes return LocalizedTextEntity.attributes
} }
toString() {
"NSLOCTEXT(" + `"${this.namespace}"` + ", " + `"${this.key}"` + ", " + `"${this.value}"` + ")"
}
} }

View File

@@ -1,10 +1,11 @@
import Entity from "./Entity" import Entity from "./Entity"
import Guid from "./primitive/Guid" import Guid from "./primitive/Guid"
import PathSymbol from "./primitive/PathSymbol"
export default class PinReferenceEntity extends Entity { export default class PinReferenceEntity extends Entity {
static attributes = { static attributes = {
objectName: String, objectName: PathSymbol,
pinGuid: Guid pinGuid: Guid
} }

View File

@@ -0,0 +1,17 @@
import Primitive from "./Primitive";
export default class PathSymbol extends Primitive {
constructor(value) {
super()
this.value = new String(value).valueOf()
}
valueOf() {
this.value
}
toString() {
return this.value
}
}

View File

@@ -8,10 +8,27 @@ import SerializerFactory from "./serialization/SerializerFactory"
import Blueprint from "./Blueprint" import Blueprint from "./Blueprint"
import GraphNode from "./graph/GraphNode" import GraphNode from "./graph/GraphNode"
import GraphLink from "./graph/GraphLink" import GraphLink from "./graph/GraphLink"
import PinReferenceEntity from "./entity/PinReferenceEntity"
SerializerFactory.registerSerializer(ObjectEntity, new ObjectSerializer()) SerializerFactory.registerSerializer(
SerializerFactory.registerSerializer(PinEntity, new GeneralSerializer("Pin ", PinEntity, "", ",", true)) ObjectEntity,
SerializerFactory.registerSerializer(FunctionReferenceEntity, new GeneralSerializer("", FunctionReferenceEntity, "", ",", false)) new ObjectSerializer()
SerializerFactory.registerSerializer(LocalizedTextEntity, new GeneralSerializer("NSLOCTEXT", LocalizedTextEntity, "", ",", false, "", _ => "")) )
SerializerFactory.registerSerializer(
PinEntity,
new GeneralSerializer(v => `Pin (${v})`, PinEntity, "", ",", true)
)
SerializerFactory.registerSerializer(
FunctionReferenceEntity,
new GeneralSerializer(v => `(${v})`, FunctionReferenceEntity, "", ",", false)
)
SerializerFactory.registerSerializer(
LocalizedTextEntity,
new GeneralSerializer(v => `NSLOCTEXT(${v})`, LocalizedTextEntity, "", ",", false, "", _ => "")
)
SerializerFactory.registerSerializer(
PinReferenceEntity,
new GeneralSerializer(v => v, PinReferenceEntity, "", " ", false, "", _ => "")
)
export { Blueprint as Blueprint, GraphNode as GraphNode, GraphLink as GraphLink } export { Blueprint as Blueprint, GraphNode as GraphNode, GraphLink as GraphLink }

26
js/input/Copy.js Normal file
View File

@@ -0,0 +1,26 @@
import Context from "./Context"
import ObjectSerializer from "../serialization/ObjectSerializer"
export default class Copy extends Context {
constructor(target, blueprint, options = {}) {
options.wantsFocusCallback = true
super(target, blueprint, options)
this.serializer = new ObjectSerializer()
let self = this
this.copyHandle = _ => self.copied()
}
blueprintFocused() {
document.body.addEventListener("copy", this.copyHandle)
}
blueprintUnfocused() {
document.body.removeEventListener("copy", this.copyHandle)
}
copied() {
const value = this.blueprint.getNodes(true).map(node => this.serializer.write(node.entity)).join("\n")
navigator.clipboard.writeText(value)
}
}

View File

@@ -7,9 +7,7 @@ export default class Paste extends Context {
constructor(target, blueprint, options = {}) { constructor(target, blueprint, options = {}) {
options.wantsFocusCallback = true options.wantsFocusCallback = true
super(target, blueprint, options) super(target, blueprint, options)
this.serializer = new ObjectSerializer() this.serializer = new ObjectSerializer()
let self = this let self = this
this.pasteHandle = e => self.pasted(e.clipboardData.getData("Text")) this.pasteHandle = e => self.pasted(e.clipboardData.getData("Text"))
} }
@@ -33,8 +31,11 @@ export default class Paste extends Context {
}) })
let mousePosition = this.blueprint.entity.mousePosition let mousePosition = this.blueprint.entity.mousePosition
nodes.forEach(node => { nodes.forEach(node => {
node.location[0] += mousePosition[0] - left const locationOffset = [
node.location[1] += mousePosition[1] - top mousePosition[0] - left,
mousePosition[1] - top
]
node.addLocation(locationOffset)
}) })
this.blueprint.addNode(...nodes) this.blueprint.addNode(...nodes)
} }

View File

@@ -3,9 +3,9 @@ import Serializer from "./Serializer"
export default class GeneralSerializer extends Serializer { export default class GeneralSerializer extends Serializer {
constructor(keyword, entityType, prefix, separator, trailingSeparator, attributeValueConjunctionSign, attributeKeyPrinter) { constructor(wrap, entityType, prefix, separator, trailingSeparator, attributeValueConjunctionSign, attributeKeyPrinter) {
super(entityType, prefix, separator, trailingSeparator, attributeValueConjunctionSign, attributeKeyPrinter) super(entityType, prefix, separator, trailingSeparator, attributeValueConjunctionSign, attributeKeyPrinter)
this.keyword = keyword ?? "" this.wrap = wrap ?? (v => `(${v})`)
} }
read(value) { read(value) {
@@ -19,7 +19,7 @@ export default class GeneralSerializer extends Serializer {
} }
write(object) { write(object) {
let result = `${this.keyword}(${this.subWrite([], object)})` let result = this.wrap(this.subWrite([], object))
return result return result
} }
} }

View File

@@ -8,6 +8,7 @@ import Parsimmon from "parsimmon"
import PinEntity from "../entity/PinEntity" import PinEntity from "../entity/PinEntity"
import PinReferenceEntity from "../entity/PinReferenceEntity" import PinReferenceEntity from "../entity/PinReferenceEntity"
import Utility from "../Utility" import Utility from "../Utility"
import PathSymbol from "../entity/primitive/PathSymbol"
let P = Parsimmon let P = Parsimmon
@@ -24,8 +25,8 @@ export default class Grammar {
String = _ => P.regex(/(?:[^"\\]|\\")*/).wrap(P.string('"'), P.string('"')).desc('string (with possibility to escape the quote using \")') String = _ => P.regex(/(?:[^"\\]|\\")*/).wrap(P.string('"'), P.string('"')).desc('string (with possibility to escape the quote using \")')
Word = _ => P.regex(/[a-zA-Z]+/).desc("a word") Word = _ => P.regex(/[a-zA-Z]+/).desc("a word")
Guid = _ => P.regex(/[0-9a-zA-Z]{32}/).map(v => new Guid(v)).desc("32 digit hexadecimal (accepts all the letters for safety) value") Guid = _ => P.regex(/[0-9a-zA-Z]{32}/).map(v => new Guid(v)).desc("32 digit hexadecimal (accepts all the letters for safety) value")
PathSymbol = _ => P.regex(/[0-9a-zA-Z_]+/) PathSymbol = _ => P.regex(/[0-9a-zA-Z_]+/).map(v => new PathSymbol(v))
ReferencePath = r => P.seq(P.string("/"), r.PathSymbol.sepBy1(P.string(".")).tieWith(".")) ReferencePath = r => P.seq(P.string("/"), r.PathSymbol.map(v => v.toString()).sepBy1(P.string(".")).tieWith("."))
.tie() .tie()
.atLeast(2) .atLeast(2)
.tie() .tie()

View File

@@ -49,7 +49,7 @@ export default class ObjectSerializer extends Serializer {
* @returns * @returns
*/ */
write(object) { write(object) {
let result = `Begin Object Class=${object.Class} Name=${object.Name} let result = `Begin Object Class=${object.Class} Name="${object.Name}"
${this.subWrite([], object) ${this.subWrite([], object)
+ object + object
.CustomProperties.map(pin => this.separator + this.prefix + "CustomProperties " + SerializerFactory.getSerializer(PinEntity).write(pin)) .CustomProperties.map(pin => this.separator + this.prefix + "CustomProperties " + SerializerFactory.getSerializer(PinEntity).write(pin))

View File

@@ -23,6 +23,7 @@ export default class Serializer {
if (value === null) { if (value === null) {
return "()" return "()"
} }
const serialize = v => SerializerFactory.getSerializer(Utility.getType(v)).write(v)
// This is an exact match (and not instanceof) to hit also primitive types (by accessing value.constructor they are converted to objects automatically) // This is an exact match (and not instanceof) to hit also primitive types (by accessing value.constructor they are converted to objects automatically)
switch (value?.constructor) { switch (value?.constructor) {
case Function: case Function:
@@ -34,8 +35,11 @@ export default class Serializer {
case String: case String:
return `"${value}"` return `"${value}"`
} }
if (value instanceof Array) {
return `(${value.map(v => serialize(v) + ",")})`
}
if (value instanceof Entity) { if (value instanceof Entity) {
return SerializerFactory.getSerializer(Utility.getType(value)).write(value) return serialize(value)
} }
if (value instanceof Primitive) { if (value instanceof Primitive) {
return value.toString() return value.toString()
@@ -51,7 +55,8 @@ export default class Serializer {
const value = object[property] const value = object[property]
if (object[property]?.constructor === Object) { if (object[property]?.constructor === Object) {
// Recursive call when finding an object // Recursive call when finding an object
result += this.subWrite(fullKey, value, this.prefix, this.separator) result += (result.length ? this.separator : "")
+ this.subWrite(fullKey, value)
} else if (this.showProperty(fullKey, value)) { } else if (this.showProperty(fullKey, value)) {
result += (result.length ? this.separator : "") result += (result.length ? this.separator : "")
+ this.prefix + this.prefix
@@ -60,7 +65,7 @@ export default class Serializer {
+ this.writeValue(value) + this.writeValue(value)
} }
} }
if (this.trailingSeparator && result.length) { if (this.trailingSeparator && result.length && fullKey.length === 0) {
// append separator at the end if asked and there was printed content // append separator at the end if asked and there was printed content
result += this.separator result += this.separator
} }