Type initialization using objects

This commit is contained in:
barsdeveloper
2023-01-02 00:22:50 +01:00
parent d391480f2c
commit 984bf32b51
43 changed files with 1511 additions and 831 deletions

139
cypress/e2e/entities.cy.js Normal file
View File

@@ -0,0 +1,139 @@
/// <reference types="cypress" />
import ComplexEntity from "../fixtures/ComplexEntity"
import SimpleEntity from "../fixtures/SimpleEntity"
import SimpleObject from "../fixtures/SimpleObject"
describe("Entity initialization", () => {
before(() => {
expect(SimpleEntity,).to.be.a("function")
expect(ComplexEntity).to.be.a("function")
})
context("SimpleEntity", () => {
const entity = new SimpleEntity()
it("has 8 keys", () => expect(Object.keys(entity).length).to.equal(8))
it("has someNumber equal to 567", () => expect(entity)
.to.have.property("someNumber")
.which.is.a("number")
.and.is.equal(567)
)
it("has someString equal to alpha", () => expect(entity)
.to.have.property("someString")
.which.is.a("string")
.and.is.equal("alpha")
)
it("has someString2 equal to beta", () => expect(entity)
.to.have.property("someString2")
.which.is.a("string")
.and.is.equal("beta")
)
it("has someBoolean true", () => expect(entity)
.to.have.property("someBoolean")
.which.is.a("boolean")
.and.is.true
)
it("has someBoolean2 false", () => expect(entity)
.to.have.property("someBoolean2")
.which.is.a("boolean")
.and.is.false
)
it("has someObjectString equal to gamma", () => expect(entity)
.to.have.property("someObjectString")
.which.is.a("string")
.and.is.equal("gamma")
)
it("has someArray with numbers", () => expect(entity)
.to.have.property("someArray")
.which.is.an("array")
.and.is.deep.equal([400, 500, 600, 700, 800])
)
it("has someSet with numbers", () => expect(entity)
.to.have.property("someSet")
.which.is.instanceOf(Set)
.and.is.deep.equal(new Set([10, 20, 30, 40, 50, 60, 70]))
)
})
context("ComplexEntity", () => {
const entity = new ComplexEntity()
it("has 8 keys", () => expect(Object.keys(entity).length).to.equal(8))
it("has alpha equal to 32", () => expect(entity)
.to.have.property("alpha")
.which.is.a("number")
.and.is.equal(32)
)
it("has bravo equal to 78", () => expect(entity)
.to.have.property("bravo")
.which.is.a("number")
.and.is.equal(78)
)
it("has charlie equal to beta", () => expect(entity)
.to.have.property("charlie")
.which.is.a("string")
.and.is.equal("Charlie")
)
it("has delta null", () => expect(entity)
.to.have.property("delta")
.which.is.null
)
it("has echo equal to echo", () => expect(entity)
.to.have.property("echo")
.which.is.a("string")
.and.is.equal("echo")
)
it("has foxtrot false", () => expect(entity)
.to.have.property("foxtrot")
.which.is.a("boolean")
.and.is.false
)
it("has golf empty array", () => expect(entity)
.to.have.property("golf")
.which.is.an("array")
.and.is.empty
)
it("has hotel null", () => expect(entity)
.to.have.property("hotel")
.which.is.null
)
it("has india empty array", () => expect(entity)
.to.have.property("india")
.which.is.an("array")
.and.is.empty
)
it("has juliett array of strings", () => expect(entity)
.to.have.property("juliett")
.which.is.an("array")
.and.is.deep.equal(["a", "b", "c", "d", "e"])
)
it("has kilo array of booleans", () => expect(entity)
.to.have.property("kilo")
.which.is.an("array")
.and.is.deep.equal([true, false, false, true, true])
)
it("has lima undefined", () => expect(entity)
.to.have.property("lima")
.which.is.undefined
)
it("has mike equal to Foo", () => expect(entity)
.to.have.property("mike")
.which.is.a("string")
.and.is.equal("Bar")
)
it("has november equal to 0", () => expect(entity)
.to.have.property("november")
.which.is.a("number")
.and.is.equal(0)
)
it("has oscar a SimpleObject", () => expect(entity)
.to.have.property("oscar")
.which.is.instanceOf(SimpleObject)
.and.is.deep.equal({ a: 8, b: 9 })
)
it("has papa a SimpleObject", () => expect(entity)
.to.have.property("papa")
.which.is.instanceOf(SimpleObject)
.and.is.deep.equal({ a: 12, b: 13 })
)
})
})

View File

@@ -0,0 +1,64 @@
import IEntity from "../../js/entity/IEntity"
import UnionType from "../../js/entity/UnionType"
import SimpleObject from "./SimpleObject"
export default class ComplexEntity extends IEntity {
static attributes = {
alpha: 32,
bravo: {
type: Number,
value: 78,
},
charlie: {
type: String,
value: "Charlie",
},
delta: {
type: String,
value: null,
},
echo: "echo",
foxtrot: {
value: false,
},
golf: {
type: Array,
},
hotel: {
type: Array,
value: null,
},
india: {
type: [Number]
},
juliett: {
type: [String],
value: ["a", "b", "c", "d", "e"],
},
kilo: {
type: [Boolean],
value: () => [true, false, false, true, true],
},
lima: {
type: String,
value: "Foo",
showDefault: false,
},
mike: {
type: new UnionType(Number, String, Array),
value: "Bar",
},
november: {
type: new UnionType(Number, String, Array),
},
oscar: {
type: SimpleObject
},
papa: () => new SimpleObject(12, 13),
}
static {
this.cleanupAttributes(this.attributes)
}
}

View File

@@ -0,0 +1,19 @@
import IEntity from "../../js/entity/IEntity"
export default class SimpleEntity extends IEntity {
static attributes = {
someNumber: 567,
someString: "alpha",
someString2: "beta",
someBoolean: true,
someBoolean2: false,
someObjectString: new String("gamma"),
someArray: [400, 500, 600, 700, 800],
someSet: new Set([10, 20, 30, 40, 50, 60, 70]),
}
static {
this.cleanupAttributes(this.attributes)
}
}

View File

@@ -0,0 +1,7 @@
export default class SimpleObject {
constructor(a = 8, b = 9) {
this.a = a
this.b = b
}
}

View File

@@ -1,5 +0,0 @@
{
"name": "Using fixtures to represent data",
"email": "hello@cypress.io",
"body": "Fixtures are a great way to mock data for responses to routes"
}

1090
dist/ueblueprint.js vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -1,17 +1,13 @@
import CalculatedType from "./entity/CalculatedType"
import TypeInitialization from "./entity/TypeInitialization"
import UnionType from "./entity/UnionType"
import SubAttributesDeclaration from "./entity/SubObject"
/**
* @typedef {import("./element/IElement").default} IElement
* @typedef {import("./entity/IEntity").AnyValue} AnyValue
* @typedef {import("./entity/IEntity").AnyValueConstructor<*>} AnyValueConstructor
* @typedef {import("./entity/IEntity").AttributeInformation} TypeInformation
* @typedef {import("./entity/IEntity").default} IEntity
* @typedef {import("./entity/IEntity").EntityConstructor} EntityConstructor
* @typedef {import("./entity/LinearColorEntity").default} LinearColorEntity
* @typedef {import("./entity/TypeInitialization").AnyValue} AnyValue
*/
/**
* @template T
* @typedef {import("./entity/TypeInitialization").AnyValueConstructor<T>} AnyValueConstructor
*/
export default class Utility {
@@ -98,22 +94,15 @@ export default class Utility {
/**
* @param {IEntity} entity
* @param {String[]} keys
* @param {any} propertyDefinition
* @returns {Boolean}
*/
static isSerialized(
entity,
keys,
propertyDefinition = Utility.objectGet(/** @type {EntityConstructor} */(entity.constructor).attributes, keys)
attribute = Utility.objectGet(/** @type {EntityConstructor} */(entity.constructor).attributes, keys)
) {
if (propertyDefinition instanceof CalculatedType) {
return Utility.isSerialized(entity, keys, propertyDefinition.calculate(entity))
}
if (propertyDefinition instanceof TypeInitialization) {
if (propertyDefinition.serialized) {
return true
}
return Utility.isSerialized(entity, keys, propertyDefinition.type)
if (attribute.constructor === Object) {
return /** @type {TypeInformation} */(attribute).serialized
}
return false
}
@@ -124,7 +113,10 @@ export default class Utility {
return undefined
}
if (!(keys instanceof Array)) {
throw new TypeError("Expected keys to be an array.")
throw new TypeError("UEBlueprint: Expected keys to be an array")
}
if (target instanceof SubAttributesDeclaration) {
target = target.attributes
}
if (keys.length == 0 || !(keys[0] in target) || target[keys[0]] === undefined) {
return defaultValue
@@ -158,9 +150,13 @@ export default class Utility {
return false
}
/**
* @param {AnyValue} a
* @param {AnyValue} b
*/
static equals(a, b) {
a = TypeInitialization.sanitize(a)
b = TypeInitialization.sanitize(b)
a = Utility.sanitize(a)
b = Utility.sanitize(b)
if (a === b) {
return true
}
@@ -169,25 +165,45 @@ export default class Utility {
}
}
/**
* @param {AnyValue | AnyValueConstructor<IEntity>} value
* @returns {AnyValueConstructor<IEntity> | AnyValueConstructor<IEntity>[]}
/**
* @param {null | AnyValue | TypeInformation} value
* @returns {AnyValueConstructor}
*/
static getType(value) {
if (value === null) {
return null
}
if (value instanceof TypeInitialization) {
return Utility.getType(value.type)
if (value.constructor === Object && value.type instanceof Function) {
// @ts-expect-error
return value.type
}
if (value instanceof UnionType) {
return value.types
return /** @type {AnyValueConstructor} */(value?.constructor)
}
/**
* @param {AnyValue} value
* @param {AnyValueConstructor} type
*/
static isValueOfType(value, type) {
return value === null || (value instanceof type || value.constructor === type)
}
/** @param {AnyValue} value */
static sanitize(value, targetType = /** @type {AnyValueConstructor} */(value?.constructor)) {
if (targetType instanceof Array) {
let type = targetType.find(t => Utility.isValueOfType(value, t))
if (!type) {
type = targetType[0]
}
targetType = type
}
if (value instanceof Function) {
// value is already a constructor
return value
if (targetType && !Utility.isValueOfType(value, targetType)) {
value = new targetType(value)
}
return /** @type {AnyValueConstructor<IEntity>} */(value?.constructor)
if (value instanceof Boolean || value instanceof Number || value instanceof String) {
value = value.valueOf() // Get the relative primitive value
}
return value
}
/**
@@ -347,4 +363,9 @@ export default class Utility {
}
requestAnimationFrame(doAnimation)
}
/** @param {String} value */
static warn(value) {
console.warn("UEBlueprint: " + value)
}
}

View File

@@ -15,10 +15,6 @@ import { LitElement } from "lit"
*/
export default class IElement extends LitElement {
/** @type {PropertyDeclarations} */
static properties = {
}
#nextUpdatedCallbacks = []
/** @type {Blueprint} */

View File

@@ -21,8 +21,8 @@ import Vector2DPinTemplate from "../template/pin/Vector2DPinTemplate"
import VectorPinTemplate from "../template/pin/VectorPinTemplate"
/**
* @typedef {import("../entity/IEntity").AnyValue} AnyValue
* @typedef {import("../entity/PinReferenceEntity").default} PinReferenceEntity
* @typedef {import("../entity/TypeInitialization").AnyValue} AnyValue
* @typedef {import("./LinkElement").LinkElementConstructor} LinkElementConstructor
* @typedef {import("./NodeElement").default} NodeElement
* @typedef {import("lit").CSSResult} CSSResult

View File

@@ -6,10 +6,13 @@ export default class ByteEntity extends IntegerEntity {
value: 0,
}
static {
this.cleanupAttributes(this.attributes)
}
/** @param {Object | Number | String} values */
constructor(values = 0) {
super(values)
/** @type {Number} */
const value = Math.round(this.value)
this.value = value >= 0 && value < 1 << 8 ? value : 0
}

View File

@@ -1,14 +1,20 @@
import IEntity from "./IEntity"
import ObjectReferenceEntity from "./ObjectReferenceEntity"
import TypeInitialization from "./TypeInitialization"
export default class FunctionReferenceEntity extends IEntity {
static attributes = {
MemberParent: new TypeInitialization(ObjectReferenceEntity, false),
MemberParent: {
type: ObjectReferenceEntity,
showDefault: false
},
MemberName: "",
}
static {
this.cleanupAttributes(this.attributes)
}
constructor(values) {
super(values)
/** @type {ObjectReferenceEntity} */ this.MemberParent

View File

@@ -3,7 +3,11 @@ import IEntity from "./IEntity"
export default class GuidEntity extends IEntity {
static attributes = {
value: String,
value: "",
}
static {
this.cleanupAttributes(this.attributes)
}
static generateGuid(random = true) {

View File

@@ -1,19 +1,43 @@
import CalculatedType from "./CalculatedType"
import Observable from "../Observable"
import SerializerFactory from "../serialization/SerializerFactory"
import TypeInitialization from "./TypeInitialization"
import Utility from "../Utility"
import SubAttributesDeclaration from "./SubObject"
import UnionType from "./UnionType"
import Utility from "../Utility"
/** @typedef {typeof IEntity} EntityConstructor */
/**
* @template {IEntity} T
* @typedef {new (Object) => T} IEntityConstructor
* @typedef {(entity: IEntity) => AnyValue} ValueSupplier
* @typedef {(entity: IEntity) => AnyValueConstructor<AnyValue>} TypeSupplier
* @typedef {IEntity | String | Number | Boolean} AnySimpleValue
* @typedef {AnySimpleValue | AnySimpleValue[]} AnyValue
* @typedef {{
* [key: String]: AttributeInformation | AnyValue | SubAttributesDeclaration
* }} AttributeDeclarations
* @typedef {typeof IEntity} EntityConstructor
* @typedef {{
* type?: AnyValueConstructor<AnyValue> | AnyValueConstructor<AnyValue>[] | UnionType | TypeSupplier,
* value?: AnyValue | ValueSupplier,
* showDefault?: Boolean,
* nullable?: Boolean,
* ignored?: Boolean,
* serialized?: Boolean,
* }} AttributeInformation
*/
/**
* @template {AnyValue} T
* @typedef {(new () => T) | EntityConstructor | StringConstructor | NumberConstructor | BooleanConstructor | ArrayConstructor} AnyValueConstructor
*/
export default class IEntity extends Observable {
/** @type {AttributeDeclarations} */
static attributes = {}
static defaultAttribute = {
showDefault: true,
nullable: false,
ignored: false,
serialized: false,
}
constructor(values = {}, suppressWarns = false) {
super()
@@ -24,82 +48,94 @@ export default class IEntity extends Observable {
* @param {String} prefix
*/
const defineAllAttributes = (target, attributes, values = {}, prefix = "") => {
const valuesPropertyNames = Object.getOwnPropertyNames(values)
for (let attribute of Utility.mergeArrays(Object.getOwnPropertyNames(attributes), valuesPropertyNames)) {
let value = Utility.objectGet(values, [attribute])
let defaultValue = attributes[attribute]
let defaultType = Utility.getType(defaultValue)
if (defaultValue instanceof CalculatedType) {
defaultValue = defaultValue.calculate(this)
defaultType = Utility.getType(defaultValue)
}
if (defaultValue != null && defaultValue === defaultType) {
defaultValue = new defaultType()
const valuesNames = Object.getOwnPropertyNames(values)
for (let attributeName of Utility.mergeArrays(Object.getOwnPropertyNames(attributes), valuesNames)) {
let value = Utility.objectGet(values, [attributeName])
/** @type {AttributeInformation} */
let attribute = attributes[attributeName]
if (attribute instanceof SubAttributesDeclaration) {
target[attributeName] = {}
defineAllAttributes(
target[attributeName],
attribute.attributes,
values[attributeName],
attributeName + "."
)
continue
}
if (!suppressWarns) {
if (!(attribute in attributes)) {
console.warn(
`Attribute ${prefix}${attribute} in the serialized data is not defined in ${this.constructor.name}.attributes`
if (!(attributeName in attributes)) {
Utility.warn(
`Attribute ${prefix}${attributeName} in the serialized data is not defined in `
+ `${this.constructor.name}.attributes`
)
} else if (
valuesPropertyNames.length > 0
&& !(attribute in values)
&& defaultValue !== undefined
&& !(defaultValue instanceof TypeInitialization && (!defaultValue.showDefault || defaultValue.ignored))
valuesNames.length > 0
&& !(attributeName in values)
&& !(!attribute.showDefault || attribute.ignored)
) {
console.warn(
`${this.constructor.name} will add attribute ${prefix}${attribute} not defined in the serialized data`
Utility.warn(
`${this.constructor.name} will add attribute ${prefix}${attributeName} not defined in the `
+ "serialized data"
)
}
}
// Not instanceof because all objects are instenceof Object, exact match needed
// @ts-expect-error
if (defaultType === Object) {
target[attribute] = {}
defineAllAttributes(target[attribute], attributes[attribute], values[attribute], attribute + ".")
continue
let defaultValue = attribute.value
let defaultType = attribute.type
if (attribute.serialized && defaultType instanceof Function) {
// If the attribute is serialized, the type must contain a function providing the type
defaultType = /** @type {TypeSupplier} */(defaultType)(this)
}
if (defaultType instanceof Array) {
defaultType = Array
}
if (defaultValue instanceof Function) {
defaultValue = defaultValue(this)
}
if (defaultType instanceof UnionType) {
if (defaultValue != undefined) {
defaultType = defaultType.types.find(
type => defaultValue instanceof type || defaultValue.constructor == type
) ?? defaultType.getFirstType()
} else {
defaultType = defaultType.getFirstType()
}
}
if (defaultType === undefined) {
defaultType = Utility.getType(defaultValue)
}
if (value !== undefined) {
// Remember value can still be null
if (
value?.constructor === String
&& defaultValue instanceof TypeInitialization
&& defaultValue.serialized
&& defaultValue.type !== String
) {
// @ts-expect-error
value = SerializerFactory.getSerializer(defaultValue.type).deserialize(value)
if (value?.constructor === String && attribute.serialized && defaultType !== String) {
value = SerializerFactory
.getSerializer(/** @type {AnyValueConstructor<*>} */(defaultType))
.deserialize(/** @type {String} */(value))
}
target[attribute] = TypeInitialization.sanitize(value, Utility.getType(defaultValue))
target[attributeName] = Utility.sanitize(value, /** @type {AnyValueConstructor<*>} */(defaultType))
continue // We have a value, need nothing more
}
if (defaultValue instanceof TypeInitialization) {
if (!defaultValue.showDefault) {
target[attribute] = undefined // Declare undefined to preserve the order of attributes
continue
}
if (defaultValue.serialized) {
defaultValue = ""
} else {
defaultType = defaultValue.type
defaultValue = defaultValue.value
if (defaultValue instanceof Function) {
defaultValue = defaultValue()
}
if (defaultValue === undefined) {
defaultValue = Utility.sanitize(new /** @type {AnyValueConstructor<*>} */(defaultType)())
}
if (!attribute.showDefault) {
target[attributeName] = undefined // Declare undefined to preserve the order of attributes
continue
}
if (attribute.serialized) {
if (defaultType !== String && defaultValue.constructor === String) {
defaultValue = SerializerFactory
.getSerializer(/** @type {AnyValueConstructor<*>} */(defaultType))
.deserialize(defaultValue)
}
}
if (defaultValue instanceof UnionType) {
defaultType = defaultValue.getFirstType()
defaultValue = TypeInitialization.sanitize(null, defaultType)
}
if (defaultValue instanceof Array) {
defaultValue = []
}
target[attribute] = TypeInitialization.sanitize(defaultValue, defaultType)
target[attributeName] = Utility.sanitize(
/** @type {AnyValue} */(defaultValue),
/** @type {AnyValueConstructor<AnyValue>} */(defaultType)
)
}
}
const attributes = /** @type {typeof IEntity} */(this.constructor).attributes
@@ -112,6 +148,45 @@ export default class IEntity extends Observable {
defineAllAttributes(this, attributes, values)
}
/** @param {AttributeDeclarations} attributes */
static cleanupAttributes(attributes, prefix = "") {
for (const attributeName in attributes) {
if (attributes[attributeName] instanceof SubAttributesDeclaration) {
this.cleanupAttributes(
/** @type {SubAttributesDeclaration} */(attributes[attributeName]).attributes,
prefix + "." + attributeName
)
continue
}
if (attributes[attributeName].constructor !== Object) {
attributes[attributeName] = {
value: attributes[attributeName],
}
}
const attribute = /** @type {AttributeInformation} */(attributes[attributeName])
if (attribute.type === undefined && !(attribute.value instanceof Function)) {
attribute.type = Utility.getType(attribute.value)
}
attributes[attributeName] = {
...IEntity.defaultAttribute,
...attribute,
}
if (attribute.value === undefined && attribute.type === undefined) {
throw new Error(
`UEBlueprint: Expected either "type" or "value" property in ${this.name} attribute ${prefix}`
+ attributeName
)
}
if (attribute.value === null) {
attributes[attributeName].nullable = true
}
}
}
static isValueOfType(value, type) {
return value != null && (value instanceof type || value.constructor === type)
}
unexpectedKeys() {
// @ts-expect-error
return Object.getOwnPropertyNames(this).length - Object.getOwnPropertyNames(this.constructor.attributes).length

View File

@@ -3,7 +3,11 @@ import IEntity from "./IEntity"
export default class IdentifierEntity extends IEntity {
static attributes = {
value: String,
value: "",
}
static {
this.cleanupAttributes(this.attributes)
}
static attributeConverter = {

View File

@@ -6,10 +6,13 @@ export default class IntegerEntity extends IEntity {
value: 0,
}
/** @param {Object | Number | String} values */
constructor(values = 0) {
super(values)
/** @type {Number} */
static {
this.cleanupAttributes(this.attributes)
}
/** @param {Object | Number | String} value */
constructor(value = 0) {
super(value)
this.value = Math.round(this.value)
}

View File

@@ -4,7 +4,11 @@ export default class InvariantTextEntity extends IEntity {
static lookbehind = "INVTEXT"
static attributes = {
value: String,
value: "",
}
static {
this.cleanupAttributes(this.attributes)
}
constructor(values) {

View File

@@ -9,7 +9,13 @@ export default class KeyBindingEntity extends IEntity {
bCtrl: false,
bAlt: false,
bCmd: false,
Key: IdentifierEntity,
Key: {
type: IdentifierEntity
},
}
static {
this.cleanupAttributes(this.attributes)
}
constructor(values = {}) {

View File

@@ -1,18 +1,43 @@
import IEntity from "./IEntity"
import RealUnitEntity from "./UnitRealEntity"
import TypeInitialization from "./TypeInitialization"
import Utility from "../Utility"
export default class LinearColorEntity extends IEntity {
static attributes = {
R: RealUnitEntity,
G: RealUnitEntity,
B: RealUnitEntity,
A: new TypeInitialization(RealUnitEntity, true, () => new RealUnitEntity(1), false, false),
H: new TypeInitialization(RealUnitEntity, true, undefined, false, true),
S: new TypeInitialization(RealUnitEntity, true, undefined, false, true),
V: new TypeInitialization(RealUnitEntity, true, undefined, false, true),
R: {
type: RealUnitEntity,
},
G: {
type: RealUnitEntity,
},
B: {
type: RealUnitEntity,
},
A: {
type: RealUnitEntity,
value: () => new RealUnitEntity(1),
showDefault: true,
},
H: {
type: RealUnitEntity,
showDefault: true,
ignored: true,
},
S: {
type: RealUnitEntity,
showDefault: true,
ignored: true,
},
V: {
type: RealUnitEntity,
showDefault: true,
ignored: true,
},
}
static {
this.cleanupAttributes(this.attributes)
}
/** @param {Number} x */

View File

@@ -5,9 +5,13 @@ export default class LocalizedTextEntity extends IEntity {
static lookbehind = "NSLOCTEXT"
static attributes = {
namespace: String,
key: String,
value: String,
namespace: "",
key: "",
value: "",
}
static {
this.cleanupAttributes(this.attributes)
}
constructor(values) {

View File

@@ -5,9 +5,19 @@ import ObjectReferenceEntity from "./ObjectReferenceEntity"
export default class MacroGraphReferenceEntity extends IEntity {
static attributes = {
MacroGraph: ObjectReferenceEntity,
GraphBlueprint: ObjectReferenceEntity,
GraphGuid: GuidEntity,
MacroGraph: {
type: ObjectReferenceEntity,
},
GraphBlueprint: {
type: ObjectReferenceEntity,
},
GraphGuid: {
type: GuidEntity,
},
}
static {
this.cleanupAttributes(this.attributes)
}
constructor(values) {

View File

@@ -9,45 +9,130 @@ import MacroGraphReferenceEntity from "./MacroGraphReferenceEntity"
import ObjectReferenceEntity from "./ObjectReferenceEntity"
import PinEntity from "./PinEntity"
import SymbolEntity from "./SymbolEntity"
import TypeInitialization from "./TypeInitialization"
import Utility from "../Utility"
import VariableReferenceEntity from "./VariableReferenceEntity"
export default class ObjectEntity extends IEntity {
static attributes = {
Class: ObjectReferenceEntity,
Class: {
type: ObjectReferenceEntity,
},
Name: "",
bIsPureFunc: new TypeInitialization(Boolean, false, false),
VariableReference: new TypeInitialization(VariableReferenceEntity, false, null),
SelfContextInfo: new TypeInitialization(SymbolEntity, false, null),
FunctionReference: new TypeInitialization(FunctionReferenceEntity, false, null,),
EventReference: new TypeInitialization(FunctionReferenceEntity, false, null,),
TargetType: new TypeInitialization(ObjectReferenceEntity, false, null),
MacroGraphReference: new TypeInitialization(MacroGraphReferenceEntity, false, null),
Enum: new TypeInitialization(ObjectReferenceEntity, false),
CommentColor: new TypeInitialization(LinearColorEntity, false),
bCommentBubbleVisible_InDetailsPanel: new TypeInitialization(Boolean, false),
bColorCommentBubble: new TypeInitialization(Boolean, false, false),
MoveMode: new TypeInitialization(SymbolEntity, false),
NodePosX: IntegerEntity,
NodePosY: IntegerEntity,
NodeWidth: new TypeInitialization(IntegerEntity, false),
NodeHeight: new TypeInitialization(IntegerEntity, false),
bCommentBubblePinned: new TypeInitialization(Boolean, false),
bCommentBubbleVisible: new TypeInitialization(Boolean, false),
NodeComment: new TypeInitialization(String, false),
AdvancedPinDisplay: new TypeInitialization(IdentifierEntity, false, null),
EnabledState: new TypeInitialization(IdentifierEntity, false, null),
NodeGuid: GuidEntity,
ErrorType: new TypeInitialization(IntegerEntity, false),
ErrorMsg: new TypeInitialization(String, false, ""),
CustomProperties: [PinEntity],
bIsPureFunc: {
value: false,
showDefault: false,
},
VariableReference: {
type: VariableReferenceEntity,
value: null,
showDefault: false,
},
SelfContextInfo: {
type: SymbolEntity,
value: null,
showDefault: false,
},
FunctionReference: {
type: FunctionReferenceEntity,
value: null,
showDefault: false,
},
EventReference: {
type: FunctionReferenceEntity,
value: null,
showDefault: false,
},
TargetType: {
type: ObjectReferenceEntity,
value: null,
showDefault: false,
},
MacroGraphReference: {
type: MacroGraphReferenceEntity,
value: null,
showDefault: false,
},
Enum: {
type: ObjectReferenceEntity,
showDefault: false,
},
CommentColor: {
type: LinearColorEntity,
showDefault: false,
},
bCommentBubbleVisible_InDetailsPanel: {
type: Boolean,
showDefault: false,
},
bColorCommentBubble: {
type: Boolean,
value: false,
showDefault: false,
},
MoveMode: {
type: SymbolEntity,
showDefault: false,
},
NodePosX: {
type: IntegerEntity,
},
NodePosY: {
type: IntegerEntity,
},
NodeWidth: {
type: IntegerEntity,
showDefault: false,
},
NodeHeight: {
type: IntegerEntity,
showDefault: false,
},
bCommentBubblePinned: {
type: Boolean,
showDefault: false,
},
bCommentBubbleVisible: {
type: Boolean,
showDefault: false,
},
NodeComment: {
type: String,
showDefault: false,
},
AdvancedPinDisplay: {
type: IdentifierEntity,
value: null,
showDefault: false,
},
EnabledState: {
type: IdentifierEntity,
value: null,
showDefault: false,
},
NodeGuid: {
type: GuidEntity,
},
ErrorType: {
type: IntegerEntity,
showDefault: false,
},
ErrorMsg: {
type: String,
value: "",
showDefault: false,
},
CustomProperties: {
type: [PinEntity]
},
}
static nameRegex = /^(\w+?)(?:_(\d+))?$/
static sequencerScriptingNameRegex = /\/Script\/SequencerScripting\.MovieSceneScripting(.+)Channel/
static {
this.cleanupAttributes(this.attributes)
}
constructor(values, suppressWarns = false) {
super(values, suppressWarns)
/** @type {ObjectReferenceEntity} */ this.Class

View File

@@ -3,12 +3,16 @@ import IEntity from "./IEntity"
export default class ObjectReferenceEntity extends IEntity {
static attributes = {
type: String,
path: String,
type: "",
path: "",
}
static {
this.cleanupAttributes(this.attributes)
}
constructor(values = {}) {
if (values.constructor !== Object) {
if (values.constructor === String) {
values = {
path: values
}

View File

@@ -3,7 +3,11 @@ import IEntity from "./IEntity"
export default class PathSymbolEntity extends IEntity {
static attributes = {
value: String,
value: "",
}
static {
this.cleanupAttributes(this.attributes)
}
constructor(values) {

View File

@@ -1,4 +1,3 @@
import CalculatedType from "./CalculatedType"
import GuidEntity from "./GuidEntity"
import IEntity from "./IEntity"
import IntegerEntity from "./IntegerEntity"
@@ -10,13 +9,13 @@ import RotatorEntity from "./RotatorEntity"
import SimpleSerializationRotatorEntity from "./SimpleSerializationRotatorEntity"
import SimpleSerializationVector2DEntity from "./SimpleSerializationVector2DEntity"
import SimpleSerializationVectorEntity from "./SimpleSerializationVectorEntity"
import TypeInitialization from "./TypeInitialization"
import SubAttributesDeclaration from "./SubObject"
import UnionType from "./UnionType"
import Utility from "../Utility"
import Vector2DEntity from "./Vector2DEntity"
import VectorEntity from "./VectorEntity"
/** @typedef {import("./TypeInitialization").AnyValue} AnyValue */
/** @typedef {import("./IEntity").AnyValue} AnyValue */
/** @template {AnyValue} T */
export default class PinEntity extends IEntity {
@@ -40,38 +39,67 @@ export default class PinEntity extends IEntity {
}
static lookbehind = "Pin"
static attributes = {
PinId: GuidEntity,
PinId: {
type: GuidEntity,
},
PinName: "",
PinFriendlyName: new TypeInitialization(new UnionType(LocalizedTextEntity, String), false, null),
PinToolTip: new TypeInitialization(String, false, ""),
Direction: new TypeInitialization(String, false, ""),
PinType: {
PinFriendlyName: {
type: new UnionType(LocalizedTextEntity, String),
showDefault: false,
},
PinToolTip: {
type: String,
showDefault: false,
},
Direction: {
type: String,
showDefault: false,
},
PinType: new SubAttributesDeclaration({
PinCategory: "",
PinSubCategory: "",
PinSubCategoryObject: ObjectReferenceEntity,
PinSubCategoryMemberReference: null,
PinValueType: null,
ContainerType: ObjectReferenceEntity,
PinSubCategoryObject: {
type: ObjectReferenceEntity,
},
PinSubCategoryMemberReference: {
type: ObjectReferenceEntity,
value: null,
},
PinValueType: {
type: String,
value: null,
},
ContainerType: {
type: ObjectReferenceEntity,
},
bIsReference: false,
bIsConst: false,
bIsWeakPointer: false,
bIsUObjectWrapper: false,
bSerializeAsSinglePrecisionFloat: false,
}),
LinkedTo: {
type: [PinReferenceEntity],
showDefault: false,
},
DefaultValue: {
/** @param {PinEntity} pinEntity */
type: pinEntity => pinEntity.getEntityType(true) ?? String,
serialized: true,
showDefault: false,
},
AutogeneratedDefaultValue: {
type: String,
showDefault: false,
},
DefaultObject: {
type: ObjectReferenceEntity,
showDefault: false,
value: null,
},
PersistentGuid: {
type: GuidEntity,
},
LinkedTo: new TypeInitialization([PinReferenceEntity], false),
DefaultValue:
new CalculatedType(
/** @param {PinEntity} pinEntity */
pinEntity => new TypeInitialization(
pinEntity.getEntityType(true) ?? String,
false,
undefined,
true
)
),
AutogeneratedDefaultValue: new TypeInitialization(String, false),
DefaultObject: new TypeInitialization(ObjectReferenceEntity, false, null),
PersistentGuid: GuidEntity,
bHidden: false,
bNotConnectable: false,
bDefaultValueIsReadOnly: false,
@@ -80,6 +108,10 @@ export default class PinEntity extends IEntity {
bOrphanedPin: false,
}
static {
this.cleanupAttributes(this.attributes)
}
constructor(values = {}, suppressWarns = false) {
super(values, suppressWarns)
/** @type {GuidEntity} */ this.PinId

View File

@@ -5,8 +5,16 @@ import PathSymbolEntity from "./PathSymbolEntity"
export default class PinReferenceEntity extends IEntity {
static attributes = {
objectName: PathSymbolEntity,
pinGuid: GuidEntity,
objectName: {
type: PathSymbolEntity,
},
pinGuid: {
type: GuidEntity,
},
}
static {
this.cleanupAttributes(this.attributes)
}
constructor(values) {

View File

@@ -3,9 +3,19 @@ import IEntity from "./IEntity"
export default class RotatorEntity extends IEntity {
static attributes = {
R: Number,
P: Number,
Y: Number,
R: {
value: 0,
},
P: {
value: 0,
},
Y: {
value: 0,
},
}
static {
this.cleanupAttributes(this.attributes)
}
constructor(values) {
@@ -14,4 +24,16 @@ export default class RotatorEntity extends IEntity {
/** @type {Number} */ this.P
/** @type {Number} */ this.Y
}
getRoll() {
return this.R
}
getPitch() {
return this.P
}
getYaw() {
return this.Y
}
}

9
js/entity/SubObject.js Normal file
View File

@@ -0,0 +1,9 @@
/** @typedef {import("./IEntity").AttributeDeclarations} AttributeDeclarations */
export default class SubAttributesDeclaration {
/** @param {AttributeDeclarations} attributes */
constructor(attributes) {
this.attributes = attributes
}
}

View File

@@ -3,7 +3,11 @@ import IEntity from "./IEntity"
export default class SymbolEntity extends IEntity {
static attributes = {
value: String
value: "",
}
static {
this.cleanupAttributes(this.attributes)
}
constructor(values) {

View File

@@ -1,106 +0,0 @@
import UnionType from "./UnionType"
/**
* @typedef {IEntity | String | Number | Boolean | Array} AnyValue
* @typedef {import("./IEntity").default} IEntity
*/
/**
* @template {AnyValue} T
* @typedef {import("./IEntity").IEntityConstructor<T>} IEntityConstructor
*/
/**
* @template {AnyValue} T
* @typedef {IEntityConstructor<T> | StringConstructor | NumberConstructor | BooleanConstructor | ArrayConstructor | UnionType} AnyValueConstructor
*/
/** @template {AnyValue} T */
export default class TypeInitialization {
/** @type {AnyValueConstructor<T>|AnyValueConstructor<T>[]} */
#type
get type() {
return this.#type
}
set type(v) {
this.#type = v
}
#showDefault = true
get showDefault() {
return this.#showDefault
}
set showDefault(v) {
this.#showDefault = v
}
/** @type {T | T[] | String | (() => T) | (() => T[])} */
#value
get value() {
return this.#value
}
set value(v) {
this.#value = v
}
/** @type {Boolean} */
#serialized
get serialized() {
return this.#serialized
}
set serialized(v) {
this.#serialized = v
}
#ignored
get ignored() {
return this.#ignored
}
set ignored(v) {
this.#ignored = v
}
static isValueOfType(value, type) {
return value != null && (value instanceof type || value.constructor === type)
}
static sanitize(value, targetType) {
if (targetType === undefined) {
targetType = value?.constructor
}
if (targetType instanceof Array) {
let type = targetType.find(t => TypeInitialization.isValueOfType(value, t))
if (!type) {
type = targetType[0]
}
targetType = type
}
if (targetType && !TypeInitialization.isValueOfType(value, targetType)) {
value = new targetType(value)
}
if (value instanceof Boolean || value instanceof Number || value instanceof String) {
value = value.valueOf() // Get the relative primitive value
}
return value
}
/**
* @param {AnyValueConstructor<T>|AnyValueConstructor<T>[]} type
* @param {Boolean} showDefault
* @param {T | T[] | String | (() => T) | (() => T[])} value
* @param {Boolean} serialized
*/
constructor(type, showDefault = true, value = undefined, serialized = false, ignored = false) {
if (value === undefined) {
if (type instanceof Array) {
value = []
} else {
value = () => TypeInitialization.sanitize(new type())
}
}
this.#type = type
this.#showDefault = showDefault
this.#value = value
this.#serialized = serialized
this.#ignored = ignored
}
}

View File

@@ -1,7 +1,4 @@
/**
* @template T
* @typedef {import("./TypeInitialization").AnyValueConstructor<T>} AnyValueConstructor
*/
/** @typedef {import("./IEntity").AnyValueConstructor<*>} AnyValueConstructor */
export default class UnionType {
@@ -10,7 +7,7 @@ export default class UnionType {
return this.#types
}
/** @param {...AnyValueConstructor<any>} types */
/** @param {...AnyValueConstructor} types */
constructor(...types) {
this.#types = types
}

View File

@@ -7,6 +7,10 @@ export default class RealUnitEntity extends IEntity {
value: 0,
}
static {
this.cleanupAttributes(this.attributes)
}
/** @param {Object | Number | String} values */
constructor(values = 0) {
super(values)

View File

@@ -1,10 +1,14 @@
import IEntity from "./IEntity"
import TypeInitialization from "./TypeInitialization"
export default class UnknownKeysEntity extends IEntity {
static attributes = {
lookbehind: new TypeInitialization(String, false, "", false, true)
lookbehind:
{
value: "",
showDefault: false,
ignore: true,
},
}
constructor(values) {

View File

@@ -1,14 +1,21 @@
import IEntity from "./IEntity"
import GuidEntity from "./GuidEntity"
import TypeInitialization from "./TypeInitialization"
export default class VariableReferenceEntity extends IEntity {
static attributes = {
MemberScope: new TypeInitialization(String, false),
MemberName: String,
MemberGuid: GuidEntity,
bSelfContext: new TypeInitialization(Boolean, false, false)
MemberScope: {
value: "",
showDefault: false,
},
MemberName: "",
MemberGuid: {
type: GuidEntity,
},
bSelfContext: {
value: false,
showDefault: false,
},
}
constructor(values) {

View File

@@ -3,8 +3,8 @@ import IEntity from "./IEntity"
export default class Vector2DEntity extends IEntity {
static attributes = {
X: Number,
Y: Number,
X: 0,
Y: 0,
}
constructor(values) {

View File

@@ -3,9 +3,9 @@ import IEntity from "./IEntity"
export default class VectorEntity extends IEntity {
static attributes = {
X: Number,
Y: Number,
Z: Number,
X: 0,
Y: 0,
Z: 0,
}
constructor(values) {

View File

@@ -2,11 +2,8 @@ import GeneralSerializer from "./GeneralSerializer"
/**
* @typedef {import("../entity/IEntity").default} IEntity
* @typedef {import("../entity/TypeInitialization").AnyValue} AnyValue
*/
/**
* @template {AnyValue} T
* @typedef {import("../entity/TypeInitialization").AnyValueConstructor<T>} AnyValueConstructor
* @typedef {import("../entity/IEntity").AnyValue} AnyValue
* @typedef {import("../entity/IEntity").AnyValueConstructor<*>} AnyValueConstructor
*/
/**
@@ -19,7 +16,7 @@ export default class CustomSerializer extends GeneralSerializer {
/**
* @param {(v: T, insideString: Boolean) => String} objectWriter
* @param {AnyValueConstructor<T>} entityType
* @param {AnyValueConstructor} entityType
*/
constructor(objectWriter, entityType) {
super(undefined, entityType)

View File

@@ -3,11 +3,8 @@ import ISerializer from "./ISerializer"
/**
* @typedef {import("../entity/IEntity").default} IEntity
* @typedef {import("../entity/TypeInitialization").AnyValue} AnyValue
*/
/**
* @template {AnyValue} T
* @typedef {import("../entity/TypeInitialization").AnyValueConstructor<T>} AnyValueConstructor
* @typedef {import("../entity/IEntity").AnyValue} AnyValue
* @typedef {import("../entity/IEntity").AnyValueConstructor<*>} AnyValueConstructor
*/
/**
@@ -18,7 +15,7 @@ export default class GeneralSerializer extends ISerializer {
/**
* @param {(value: String, entity: T) => String} wrap
* @param {AnyValueConstructor<T>} entityType
* @param {AnyValueConstructor} entityType
*/
constructor(wrap, entityType, attributePrefix, attributeSeparator, trailingSeparator, attributeValueConjunctionSign, attributeKeyPrinter) {
wrap = wrap ?? (v => `(${v})`)
@@ -35,7 +32,6 @@ export default class GeneralSerializer extends ISerializer {
let grammar = Grammar.getGrammarForType(ISerializer.grammar, this.entityType)
const parseResult = grammar.parse(value)
if (!parseResult.status) {
// @ts-expect-error
throw new Error(`Error when trying to parse the entity ${this.entityType.prototype.constructor.name}.`)
}
return parseResult.value

View File

@@ -1,4 +1,5 @@
// @ts-nocheck
import ByteEntity from "../entity/ByteEntity"
import FunctionReferenceEntity from "../entity/FunctionReferenceEntity"
import GuidEntity from "../entity/GuidEntity"
import IdentifierEntity from "../entity/IdentifierEntity"
@@ -17,17 +18,17 @@ import PinReferenceEntity from "../entity/PinReferenceEntity"
import RealUnitEntity from "../entity/UnitRealEntity"
import RotatorEntity from "../entity/RotatorEntity"
import SimpleSerializationRotatorEntity from "../entity/SimpleSerializationRotatorEntity"
import SimpleSerializationVector2DEntity from "../entity/SimpleSerializationVector2DEntity"
import SimpleSerializationVectorEntity from "../entity/SimpleSerializationVectorEntity"
import SymbolEntity from "../entity/SymbolEntity"
import TypeInitialization from "../entity/TypeInitialization"
import UnionType from "../entity/UnionType"
import UnknownKeysEntity from "../entity/UnknownKeysEntity"
import Utility from "../Utility"
import VariableReferenceEntity from "../entity/VariableReferenceEntity"
import Vector2DEntity from "../entity/Vector2DEntity"
import VectorEntity from "../entity/VectorEntity"
import SimpleSerializationVector2DEntity from "../entity/SimpleSerializationVector2DEntity"
import ByteEntity from "../entity/ByteEntity"
/** @typedef {import ("../entity/IEntity").AttributeInformation} AttributeInformation */
let P = Parsimmon
@@ -36,30 +37,35 @@ export default class Grammar {
/* --- Factory --- */
/** @param {Grammar} r */
static getGrammarForType(r, attributeType, defaultGrammar = r.AttributeAnyValue) {
if (attributeType instanceof TypeInitialization) {
let result = Grammar.getGrammarForType(r, attributeType.type, defaultGrammar)
if (attributeType.serialized && !(attributeType.type instanceof String)) {
static getGrammarForType(r, attribute, defaultGrammar = r.AttributeAnyValue) {
if (attribute.constructor === Object) {
attribute = /** @type {AttributeInformation} */(attribute)
let type = attribute.type
let result
if (type instanceof Array) {
result = Grammar.getGrammarForType(r, type[0])
.trim(P.optWhitespace)
.sepBy(P.string(","))
.skip(P.regex(/,?\s*/))
.wrap(P.string("("), P.string(")"))
} else if (type instanceof UnionType) {
result = type.types
.map(v => Grammar.getGrammarForType(r, Utility.getType(v)))
.reduce((accum, cur) => !cur || accum === r.AttributeAnyValue
? r.AttributeAnyValue
: accum.or(cur))
} else {
result = Grammar.getGrammarForType(r, type, defaultGrammar)
}
if (attribute.serialized && !(type instanceof String)) {
result = result.wrap(P.string('"'), P.string('"'))
}
if (attribute.nullable) {
result = result.or(r.Null)
}
return result
}
switch (Utility.getType(attributeType)) {
case Array:
return P.seqMap(
P.string("("),
attributeType
.map(v => Grammar.getGrammarForType(r, Utility.getType(v)))
.reduce((accum, cur) => !cur || accum === r.AttributeAnyValue
? r.AttributeAnyValue
: accum.or(cur)
)
.trim(P.optWhitespace)
.sepBy(P.string(","))
.skip(P.regex(/,?\s*/)),
P.string(")"),
(_0, grammar, _2) => grammar
)
switch (attribute) {
case Boolean:
return r.Boolean
case ByteEntity:
@@ -102,12 +108,6 @@ export default class Grammar {
return r.String
case SymbolEntity:
return r.Symbol
case UnionType:
return attributeType.types
.map(v => Grammar.getGrammarForType(r, Utility.getType(v)))
.reduce((accum, cur) => !cur || accum === r.AttributeAnyValue
? r.AttributeAnyValue
: accum.or(cur))
case VariableReferenceEntity:
return r.VariableReference
case Vector2DEntity:
@@ -145,7 +145,10 @@ export default class Grammar {
// Once the attribute name is known, look into entityType.attributes to get its type
const attributeKey = attributeName.split(".")
const attribute = Utility.objectGet(entityType.attributes, attributeKey)
let attributeValueGrammar = Grammar.getGrammarForType(r, attribute, r.AttributeAnyValue)
let attributeValueGrammar =
attribute.constructor === Object && /** @type {AttributeInformation} */(attribute).serialized
? r.String
: Grammar.getGrammarForType(r, attribute, r.AttributeAnyValue)
// Returns a setter function for the attribute
return attributeValueGrammar.map(attributeValue =>
entity => Utility.objectSet(entity, attributeKey, attributeValue, true)

View File

@@ -1,16 +1,13 @@
import Grammar from "./Grammar"
import Parsimmon from "parsimmon"
import SerializerFactory from "./SerializerFactory"
import TypeInitialization from "../entity/TypeInitialization"
import Utility from "../Utility"
import IEntity from "../entity/IEntity"
/**
* @typedef {import("../entity/IEntity").EntityConstructor} EntityConstructor
* @typedef {import("../entity/TypeInitialization").AnyValue} AnyValue
*/
/**
* @template {AnyValue} T
* @typedef {import("../entity/TypeInitialization").AnyValueConstructor<T>} AnyValueConstructor
* @typedef {import("../entity/IEntity").AnyValue} AnyValue
* @typedef {import("../entity/IEntity").AnyValueConstructor<*>} AnyValueConstructor
*/
/** @template {AnyValue} T */
@@ -18,7 +15,7 @@ export default class ISerializer {
static grammar = Parsimmon.createLanguage(new Grammar())
/** @param {AnyValueConstructor<T>} entityType */
/** @param {AnyValueConstructor} entityType */
constructor(
entityType,
attributePrefix = "",
@@ -76,7 +73,11 @@ export default class ISerializer {
if (!serializer) {
throw new Error(`Unknown value type "${type.name}", a serializer must be registered in the SerializerFactory class, check initializeSerializerFactory.js`)
}
return serializer.write(entity, value, insideString)
return serializer.write(
value instanceof IEntity ? value : entity,
value,
insideString
)
}
/**
@@ -90,13 +91,12 @@ export default class ISerializer {
let fullKey = key.concat("")
const last = fullKey.length - 1
const attributes = /** @type {EntityConstructor} */(object.constructor).attributes
const keys =
attributes ?
Utility.mergeArrays(
Object.getOwnPropertyNames(attributes),
Object.getOwnPropertyNames(object)
)
: Object.getOwnPropertyNames(object)
const keys = attributes
? Utility.mergeArrays(
Object.getOwnPropertyNames(attributes),
Object.getOwnPropertyNames(object)
)
: Object.getOwnPropertyNames(object)
for (const property of keys) {
fullKey[last] = property
const value = object[property]
@@ -125,10 +125,9 @@ export default class ISerializer {
}
showProperty(entity, object, attributeKey, attributeValue) {
// @ts-expect-error
const attributes = /** @type {EntityConstructor} */(this.entityType).attributes
const attribute = Utility.objectGet(attributes, attributeKey)
if (attribute instanceof TypeInitialization) {
if (attribute.constructor === Object) {
if (attribute.ignored) {
return false
}

View File

@@ -1,10 +1,11 @@
/**
* @typedef {import("../entity/IEntity").default} IEntity
* @typedef {import("../entity/TypeInitialization").AnyValue} AnyValue
* @typedef {import("../entity/IEntity").AnyValue} AnyValue
*/
/**
* @template T
* @typedef {import("../entity/TypeInitialization").AnyValueConstructor<T>} AnyValueConstructor
* @template {AnyValue} T
* @typedef {import("../entity/IEntity").AnyValueConstructor<T>} AnyValueConstructor
*/
/**
* @template {AnyValue} T
@@ -16,6 +17,11 @@ export default class SerializerFactory {
/** @type {Map<AnyValueConstructor<AnyValue>, ISerializer<AnyValue>>} */
static #serializers = new Map()
/**
* @template {AnyValue} T
* @param {AnyValueConstructor<T>} entity
* @param {ISerializer<T>} object
*/
static registerSerializer(entity, object) {
SerializerFactory.#serializers.set(entity, object)
}

View File

@@ -1,18 +1,18 @@
import Utility from "../Utility"
import GeneralSerializer from "./GeneralSerializer"
/** @typedef {import("../entity/TypeInitialization").AnyValue} AnyValue */
/**
* @template {AnyValue} T
* @typedef {import("../entity/TypeInitialization").AnyValueConstructor<T>} AnyValueConstructor
/**
* @typedef {import("../entity/IEntity").AnyValue} AnyValue
* @typedef {import("../entity/IEntity").AnyValueConstructor<*>} AnyValueConstructor
*/
/**
* @template {AnyValue} T
* @extends {GeneralSerializer<T>}
*/
export default class ToStringSerializer extends GeneralSerializer {
/** @param {AnyValueConstructor<T>} entityType */
/** @param {AnyValueConstructor} entityType */
constructor(entityType) {
super(undefined, entityType)
}

View File

@@ -19,6 +19,7 @@ import RealUnitEntity from "../entity/UnitRealEntity"
import RotatorEntity from "../entity/RotatorEntity"
import SerializerFactory from "./SerializerFactory"
import SimpleSerializationRotatorEntity from "../entity/SimpleSerializationRotatorEntity"
import SimpleSerializationVector2DEntity from "../entity/SimpleSerializationVector2DEntity"
import SimpleSerializationVectorEntity from "../entity/SimpleSerializationVectorEntity"
import SymbolEntity from "../entity/SymbolEntity"
import ToStringSerializer from "./ToStringSerializer"
@@ -27,7 +28,11 @@ import Utility from "../Utility"
import VariableReferenceEntity from "../entity/VariableReferenceEntity"
import Vector2DEntity from "../entity/Vector2DEntity"
import VectorEntity from "../entity/VectorEntity"
import SimpleSerializationVector2DEntity from "../entity/SimpleSerializationVector2DEntity"
/**
* @typedef {import("../entity/IEntity").AnySimpleValue} AnySimpleValue
* @typedef {import("../entity/IEntity").AnyValue} AnyValue
*/
export default function initializeSerializerFactory() {
@@ -44,11 +49,10 @@ export default function initializeSerializerFactory() {
SerializerFactory.registerSerializer(
Array,
new CustomSerializer(
/** @param {Array} array */
/** @param {AnySimpleValue[]} array */
(array, insideString) =>
`(${array
.map(v =>
// @ts-expect-error
SerializerFactory.getSerializer(Utility.getType(v)).serialize(v, insideString) + ","
)
.join("")