Enforcing type safety at import.
This commit is contained in:
parent
8e75f13e9a
commit
5f8c756dc7
10 changed files with 327 additions and 55 deletions
|
@ -36,7 +36,7 @@ class HistoryCommonAPI extends Module {
|
||||||
redo() { this.history.redo() }
|
redo() { this.history.redo() }
|
||||||
clear() { this.history.clear() }
|
clear() { this.history.clear() }
|
||||||
addToHistory(action) { this.history.addToHistory(action) }
|
addToHistory(action) { this.history.addToHistory(action) }
|
||||||
unserialize(data) { this.history.unserialize(data) }
|
unserialize(...data) { this.history.unserialize(...data) }
|
||||||
serialize() { return this.history.serialize() }
|
serialize() { return this.history.serialize() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,7 +124,7 @@ export class Action {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a string with the HTML-formated description of the action.
|
* Returns a string with the HTML-formatted description of the action.
|
||||||
*
|
*
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -129,7 +129,8 @@ class IOAPI extends Module {
|
||||||
if(Object.keys(Modules.Objects.types).indexOf(objType) > -1) {
|
if(Object.keys(Modules.Objects.types).indexOf(objType) > -1) {
|
||||||
Modules.Objects.currentObjects[objType] = []
|
Modules.Objects.currentObjects[objType] = []
|
||||||
for(let objData of data['objects'][objType]) {
|
for(let objData of data['objects'][objType]) {
|
||||||
let obj = new Modules.Objects.types[objType](...objData)
|
/** @type {DrawableObject} */
|
||||||
|
let obj = Modules.Objects.types[objType].import(...objData)
|
||||||
Modules.Objects.currentObjects[objType].push(obj)
|
Modules.Objects.currentObjects[objType].push(obj)
|
||||||
Modules.Objects.currentObjectsByName[obj.name] = obj
|
Modules.Objects.currentObjectsByName[obj.name] = obj
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,8 @@ qsTranslate = qsTranslate || function(category, string) { throw new Error('qsTra
|
||||||
qsTr = qsTr || function(string) { throw new Error('qsTr not implemented.'); }
|
qsTr = qsTr || function(string) { throw new Error('qsTr not implemented.'); }
|
||||||
/** @type {function(string, string): string} */
|
/** @type {function(string, string): string} */
|
||||||
QT_TRANSLATE_NOOP = QT_TRANSLATE_NOOP || function(string, string) { throw new Error('QT_TRANSLATE_NOOP not implemented.'); }
|
QT_TRANSLATE_NOOP = QT_TRANSLATE_NOOP || function(string, string) { throw new Error('QT_TRANSLATE_NOOP not implemented.'); }
|
||||||
|
/** @type {function(string): string} */
|
||||||
|
QT_TR_NOOP = QT_TR_NOOP || function(string) { throw new Error('QT_TR_NOOP not implemented.'); }
|
||||||
/** @type {function(string|boolean|int): string} */
|
/** @type {function(string|boolean|int): string} */
|
||||||
String.prototype.arg = String.prototype.arg || function(parameter) { throw new Error('arg not implemented.'); }
|
String.prototype.arg = String.prototype.arg || function(parameter) { throw new Error('arg not implemented.'); }
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@ import { getRandomColor, textsub } from "../utils.mjs"
|
||||||
import Objects from "../objects.mjs"
|
import Objects from "../objects.mjs"
|
||||||
import Latex from "../math/latex.mjs"
|
import Latex from "../math/latex.mjs"
|
||||||
import {Module} from "../modules.mjs"
|
import {Module} from "../modules.mjs"
|
||||||
|
import {ensureTypeSafety, serializesByPropertyType} from "../parameters.mjs"
|
||||||
|
|
||||||
// This file contains the default data to be imported from all other objects
|
// This file contains the default data to be imported from all other objects
|
||||||
|
|
||||||
|
@ -34,7 +35,7 @@ class ObjectsCommonAPI extends Module {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new name for an object, based on the \c allowedLetters.
|
* Creates a new name for an object, based on the allowedLetters.
|
||||||
* If variables with each of the allowedLetters is created, a subscript
|
* If variables with each of the allowedLetters is created, a subscript
|
||||||
* number is added to the name.
|
* number is added to the name.
|
||||||
* @param {string} allowedLetters
|
* @param {string} allowedLetters
|
||||||
|
@ -56,7 +57,7 @@ class ObjectsCommonAPI extends Module {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers the object \c obj in the object list.
|
* Registers the object obj in the object list.
|
||||||
* @param {DrawableObject} obj - Object to be registered.
|
* @param {DrawableObject} obj - Object to be registered.
|
||||||
*/
|
*/
|
||||||
registerObject(obj) {
|
registerObject(obj) {
|
||||||
|
@ -131,6 +132,22 @@ export class DrawableObject {
|
||||||
* @return {boolean}
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
static executable() {return false}
|
static executable() {return false}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Imports the object from its serialized form.
|
||||||
|
* @return {DrawableObject}
|
||||||
|
*/
|
||||||
|
static import(name, visible, color, labelContent, ...args) {
|
||||||
|
let importedArgs = [name.toString(), visible === true, color.toString(), labelContent]
|
||||||
|
console.log('---')
|
||||||
|
console.log(this, name, args)
|
||||||
|
for(let [name, propType] of Object.entries(this.properties()))
|
||||||
|
if(!name.startsWith('comment')) {
|
||||||
|
console.log(name, propType, importedArgs.length-4, args[importedArgs.length-4])
|
||||||
|
importedArgs.push(ensureTypeSafety(propType, args[importedArgs.length-4]))
|
||||||
|
}
|
||||||
|
return new this(...importedArgs)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base constructor for the object.
|
* Base constructor for the object.
|
||||||
|
@ -138,7 +155,7 @@ export class DrawableObject {
|
||||||
* @param {boolean} visible - true if the object is visible, false otherwise.
|
* @param {boolean} visible - true if the object is visible, false otherwise.
|
||||||
* @param {color|string} color - Color of the object (can be string or QColor)
|
* @param {color|string} color - Color of the object (can be string or QColor)
|
||||||
* @param {Enum} labelContent - One of 'null', 'name' or 'name + value' describing the content of the label.
|
* @param {Enum} labelContent - One of 'null', 'name' or 'name + value' describing the content of the label.
|
||||||
* @constructor()
|
* @constructor
|
||||||
*/
|
*/
|
||||||
constructor(name, visible = true, color = null, labelContent = 'name + value') {
|
constructor(name, visible = true, color = null, labelContent = 'name + value') {
|
||||||
if(color == null) color = getRandomColor()
|
if(color == null) color = getRandomColor()
|
||||||
|
@ -150,15 +167,18 @@ export class DrawableObject {
|
||||||
this.requiredBy = []
|
this.requiredBy = []
|
||||||
this.requires = []
|
this.requires = []
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Serilizes the object in an array that can be JSON serialized.
|
* Serializes the object in an array that can be JSON serialized.
|
||||||
* These parameters will be re-entered in the constructor when restored.
|
* These parameters will be re-entered in the constructor when restored.
|
||||||
* @return {array}
|
* @return {array}
|
||||||
*/
|
*/
|
||||||
export() {
|
export() {
|
||||||
// Should return what will be inputed as arguments when a file is loaded (serializable form)
|
let exportList = [this.name, this.visible, this.color.toString(), this.labelContent]
|
||||||
return [this.name, this.visible, this.color.toString(), this.labelContent]
|
for(let [name, propType] of Object.entries(this.constructor.properties()))
|
||||||
|
if(!name.startsWith('comment'))
|
||||||
|
exportList.push(serializesByPropertyType(propType, this[name]))
|
||||||
|
return exportList
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -182,7 +202,7 @@ export class DrawableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Readable string content of the label depending on the value of the \c latexContent.
|
* Readable string content of the label depending on the value of the latexContent.
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
getLabel() {
|
getLabel() {
|
||||||
|
@ -198,7 +218,7 @@ export class DrawableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Latex markup string content of the label depending on the value of the \c latexContent.
|
* Latex markup string content of the label depending on the value of the latexContent.
|
||||||
* Every non latin character should be passed as latex symbols and formulas
|
* Every non latin character should be passed as latex symbols and formulas
|
||||||
* should be in latex form.
|
* should be in latex form.
|
||||||
* See ../latex.mjs for helper methods.
|
* See ../latex.mjs for helper methods.
|
||||||
|
@ -275,16 +295,15 @@ export class DrawableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract method. Draw the object onto the \c canvas with the.
|
* Abstract method. Draw the object onto the canvas with the.
|
||||||
* @param {CanvasAPI} canvas
|
* @param {CanvasAPI} canvas
|
||||||
* @param {CanvasRenderingContext2D} ctx
|
|
||||||
*/
|
*/
|
||||||
draw(canvas) {}
|
draw(canvas) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Applicates a \c drawFunction with two position arguments depending on
|
* Applicates a drawFunction with two position arguments depending on
|
||||||
* both the \c posX and \c posY of where the label should be displayed,
|
* both the posX and posY of where the label should be displayed,
|
||||||
* and the \c labelPosition which declares the label should be displayed
|
* and the labelPosition which declares the label should be displayed
|
||||||
* relatively to that position.
|
* relatively to that position.
|
||||||
*
|
*
|
||||||
* @param {string|Enum} labelPosition - Position of the label relative to the marked position
|
* @param {string|Enum} labelPosition - Position of the label relative to the marked position
|
||||||
|
@ -333,17 +352,16 @@ export class DrawableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Automatically draw text (by default the label of the object on the \c canvas with
|
* Automatically draw text (by default the label of the object on the canvas
|
||||||
* the 2D context \c ctx depending on user settings.
|
* depending on user settings.
|
||||||
* This method takes into account both the \c posX and \c posY of where the label
|
* This method takes into account both the posX and posY of where the label
|
||||||
* should be displayed, including the \c labelPosition relative to it.
|
* should be displayed, including the labelPosition relative to it.
|
||||||
* The text is get both through the \c getLatexFunction and \c getTextFunction
|
* The text is get both through the getLatexFunction and getTextFunction
|
||||||
* depending on whether to use latex.
|
* depending on whether to use latex.
|
||||||
* Then, it's displayed using the \c drawFunctionLatex (x,y,imageData) and
|
* Then, it's displayed using the drawFunctionLatex (x,y,imageData) and
|
||||||
* \c drawFunctionText (x,y,text) depending on whether to use latex.
|
* drawFunctionText (x,y,text) depending on whether to use latex.
|
||||||
*
|
*
|
||||||
* @param {CanvasAPI} canvas
|
* @param {CanvasAPI} canvas
|
||||||
* @param {CanvasRenderingContext2D} ctx
|
|
||||||
* @param {string|Enum} labelPosition - Position of the label relative to the marked position
|
* @param {string|Enum} labelPosition - Position of the label relative to the marked position
|
||||||
* @param {number} posX - Component of the marked position on the x-axis
|
* @param {number} posX - Component of the marked position on the x-axis
|
||||||
* @param {number} posY - Component of the marked position on the y-axis
|
* @param {number} posY - Component of the marked position on the y-axis
|
||||||
|
|
|
@ -35,8 +35,8 @@ export default class Function extends ExecutableObject {
|
||||||
'comment',
|
'comment',
|
||||||
'Ex: R+* (ℝ⁺*), N (ℕ), Z-* (ℤ⁻*), ]0;1[, {3;4;5}'
|
'Ex: R+* (ℝ⁺*), N (ℕ), Z-* (ℤ⁻*), ]0;1[, {3;4;5}'
|
||||||
),
|
),
|
||||||
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position,
|
|
||||||
[QT_TRANSLATE_NOOP('prop','displayMode')]: P.Enum.FunctionDisplayType,
|
[QT_TRANSLATE_NOOP('prop','displayMode')]: P.Enum.FunctionDisplayType,
|
||||||
|
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position,
|
||||||
[QT_TRANSLATE_NOOP('prop','labelX')]: 'number',
|
[QT_TRANSLATE_NOOP('prop','labelX')]: 'number',
|
||||||
'comment2': QT_TRANSLATE_NOOP(
|
'comment2': QT_TRANSLATE_NOOP(
|
||||||
'comment',
|
'comment',
|
||||||
|
@ -81,13 +81,13 @@ export default class Function extends ExecutableObject {
|
||||||
return `\\begin{array}{l}${Latex.variable(this.name)}(x) = ${this.expression.latexMarkup}\\\\ D_{${this.name}} = ${this.definitionDomain.latexMarkup}\\end{array}`
|
return `\\begin{array}{l}${Latex.variable(this.name)}(x) = ${this.expression.latexMarkup}\\\\ D_{${this.name}} = ${this.definitionDomain.latexMarkup}\\end{array}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export() {
|
export() {
|
||||||
return [this.name, this.visible, this.color.toString(), this.labelContent,
|
return [this.name, this.visible, this.color.toString(), this.labelContent,
|
||||||
this.expression.toEditableString(), this.definitionDomain.toString(), this.destinationDomain.toString(),
|
this.expression.toEditableString(), this.definitionDomain.toString(), this.destinationDomain.toString(),
|
||||||
this.displayMode, this.labelPosition, this.labelX, this.drawPoints, this.drawDashedLines]
|
this.displayMode, this.labelPosition, this.labelX, this.drawPoints, this.drawDashedLines]
|
||||||
}
|
}
|
||||||
|
|
||||||
execute(x = 1) {
|
execute(x = 1) {
|
||||||
if(this.definitionDomain.includes(x))
|
if(this.definitionDomain.includes(x))
|
||||||
return this.expression.execute(x)
|
return this.expression.execute(x)
|
||||||
|
|
|
@ -26,21 +26,25 @@ export default class RepartitionFunction extends ExecutableObject {
|
||||||
static displayType(){return qsTr('Repartition')}
|
static displayType(){return qsTr('Repartition')}
|
||||||
static displayTypeMultiple(){return qsTr('Repartition functions')}
|
static displayTypeMultiple(){return qsTr('Repartition functions')}
|
||||||
static properties() {return {
|
static properties() {return {
|
||||||
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position,
|
|
||||||
[QT_TRANSLATE_NOOP('prop','labelX')]: 'number',
|
|
||||||
'comment1': QT_TRANSLATE_NOOP(
|
'comment1': QT_TRANSLATE_NOOP(
|
||||||
'comment',
|
'comment',
|
||||||
'Note: Specify the probability for each value.'
|
'Note: Specify the probability for each value.'
|
||||||
),
|
),
|
||||||
[QT_TRANSLATE_NOOP('prop','probabilities')]: new P.Dictionary('string', 'float', /^-?[\d.,]+$/, /^-?[\d.,]+$/, 'P({name_} = ', ') = '),
|
[QT_TRANSLATE_NOOP('prop','probabilities')]: new P.Dictionary('string', 'double', /^-?[\d.,]+$/, /^-?[\d.,]+$/, 'P({name_} = ', ') = '),
|
||||||
|
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position,
|
||||||
|
[QT_TRANSLATE_NOOP('prop','labelX')]: 'number',
|
||||||
}}
|
}}
|
||||||
|
static import(name, visible, color, labelContent, ...args) {
|
||||||
|
if(args.length === 5)
|
||||||
|
// Two legacy values no longer included.
|
||||||
|
args.shift(); args.shift()
|
||||||
|
return super.import(name, visible, color, labelContent, ...args)
|
||||||
|
}
|
||||||
|
|
||||||
constructor(name = null, visible = true, color = null, labelContent = 'name + value',
|
constructor(name = null, visible = true, color = null, labelContent = 'name + value',
|
||||||
beginIncluded = true, drawLineEnds = true, probabilities = {'0': '0'}, labelPosition = 'above', labelX = 1) {
|
probabilities = {'0': '0'}, labelPosition = 'above', labelX = 1) {
|
||||||
if(name == null) name = Common.getNewName('XYZUVW', "F_")
|
if(name == null) name = Common.getNewName('XYZUVW', "F_")
|
||||||
super(name, visible, color, labelContent)
|
super(name, visible, color, labelContent)
|
||||||
this.beginIncluded = beginIncluded
|
|
||||||
this.drawLineEnds = drawLineEnds
|
|
||||||
this.probabilities = probabilities
|
this.probabilities = probabilities
|
||||||
this.labelPosition = labelPosition
|
this.labelPosition = labelPosition
|
||||||
this.labelX = labelX
|
this.labelX = labelX
|
||||||
|
@ -49,7 +53,7 @@ export default class RepartitionFunction extends ExecutableObject {
|
||||||
|
|
||||||
export() {
|
export() {
|
||||||
return [this.name, this.visible, this.color.toString(), this.labelContent,
|
return [this.name, this.visible, this.color.toString(), this.labelContent,
|
||||||
this.beginIncluded, this.drawLineEnds, this.probabilities, this.labelPosition, this.labelX]
|
this.probabilities, this.labelPosition, this.labelX]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,6 @@ export default class Sequence extends ExecutableObject {
|
||||||
|
|
||||||
update() {
|
update() {
|
||||||
console.log('Updating sequence', this.sequence)
|
console.log('Updating sequence', this.sequence)
|
||||||
console.trace()
|
|
||||||
super.update()
|
super.update()
|
||||||
if(
|
if(
|
||||||
this.sequence == null || this.baseValues !== this.sequence.baseValues ||
|
this.sequence == null || this.baseValues !== this.sequence.baseValues ||
|
||||||
|
|
|
@ -32,7 +32,7 @@ export default class Text extends DrawableObject {
|
||||||
[QT_TRANSLATE_NOOP('prop','y')]: new P.Expression(),
|
[QT_TRANSLATE_NOOP('prop','y')]: new P.Expression(),
|
||||||
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Positioning,
|
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Positioning,
|
||||||
[QT_TRANSLATE_NOOP('prop','text')]: 'string',
|
[QT_TRANSLATE_NOOP('prop','text')]: 'string',
|
||||||
'comment1': QT_TRANSLATE_NOOP(
|
'comment1': QT_TRANSLATE_NOOP(
|
||||||
'comment',
|
'comment',
|
||||||
'If you have latex enabled, you can use use latex markup in between $$ to create equations.'
|
'If you have latex enabled, you can use use latex markup in between $$ to create equations.'
|
||||||
),
|
),
|
||||||
|
|
|
@ -29,8 +29,8 @@ export default class XCursor extends DrawableObject {
|
||||||
static displayType(){return qsTr('X Cursor')}
|
static displayType(){return qsTr('X Cursor')}
|
||||||
static displayTypeMultiple(){return qsTr('X Cursors')}
|
static displayTypeMultiple(){return qsTr('X Cursors')}
|
||||||
static properties() {return {
|
static properties() {return {
|
||||||
[QT_TRANSLATE_NOOP('prop','x')]: 'Expression',
|
[QT_TRANSLATE_NOOP('prop','x')]: new P.Expression(),
|
||||||
[QT_TRANSLATE_NOOP('prop','targetElement')]: new P.ObjectType('ExecutableObject'),
|
[QT_TRANSLATE_NOOP('prop','targetElement')]: new P.ObjectType('ExecutableObject', true),
|
||||||
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position,
|
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position,
|
||||||
[QT_TRANSLATE_NOOP('prop','approximate')]: 'boolean',
|
[QT_TRANSLATE_NOOP('prop','approximate')]: 'boolean',
|
||||||
[QT_TRANSLATE_NOOP('prop','rounding')]: 'number',
|
[QT_TRANSLATE_NOOP('prop','rounding')]: 'number',
|
||||||
|
|
|
@ -1,72 +1,204 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2021-2024 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
* (at your option) any later version.
|
* (at your option) any later version.
|
||||||
*
|
*
|
||||||
* This program is distributed in the hope that it will be useful,
|
* This program is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
import {parseDomain, Expression as Expr, Domain} from "./mathlib.mjs"
|
||||||
|
|
||||||
export class Expression {
|
const NONE = class Empty {}
|
||||||
|
|
||||||
|
let stringValuesValidators = {
|
||||||
|
'int': [parseInt, (x) => !isNaN(x)],
|
||||||
|
'double': [parseFloat, (x) => !isNaN(x)],
|
||||||
|
'string': [(x) => x, () => true]
|
||||||
|
}
|
||||||
|
let stringValidatorTypes = Object.keys(stringValuesValidators)
|
||||||
|
|
||||||
|
|
||||||
|
class PropertyType {
|
||||||
|
/**
|
||||||
|
* Validates if a value corresponds to the current property type, and if so, returns it.
|
||||||
|
* @param value
|
||||||
|
* @returns {null|object}
|
||||||
|
*/
|
||||||
|
parse(value) {
|
||||||
|
throw new TypeError(`validate function of ${typeof this} has not been implemented.`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exports the value of this property type.
|
||||||
|
* @param value
|
||||||
|
* @returns {string|number|bool|object}
|
||||||
|
*/
|
||||||
|
export(value) {
|
||||||
|
throw new TypeError(`export function of ${typeof this} has not been implemented.`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class Expression extends PropertyType {
|
||||||
constructor(...variables) {
|
constructor(...variables) {
|
||||||
|
super()
|
||||||
this.type = 'Expression'
|
this.type = 'Expression'
|
||||||
this.variables = variables
|
this.variables = variables
|
||||||
}
|
}
|
||||||
|
|
||||||
toString() {
|
toString() {
|
||||||
return this.variables.length == 0 ? 'Number' : `Expression(${this.variables.join(', ')})`
|
return this.variables.length === 0 ? 'Number' : `Expression(${this.variables.join(', ')})`
|
||||||
|
}
|
||||||
|
|
||||||
|
parse(value) {
|
||||||
|
let result = NONE
|
||||||
|
if(typeof value == 'string')
|
||||||
|
try {
|
||||||
|
result = new Expr(value)
|
||||||
|
} catch(e) {
|
||||||
|
// Silently error and return null
|
||||||
|
console.trace()
|
||||||
|
console.log(`Error parsing expression ${value}:`)
|
||||||
|
console.error(e)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
export(value) {
|
||||||
|
if(value instanceof Expr)
|
||||||
|
return value.toEditableString()
|
||||||
|
else
|
||||||
|
throw new TypeError(`Exportation error: ${value} is not an expression.`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Enum {
|
|
||||||
|
export class Enum extends PropertyType {
|
||||||
constructor(...values) {
|
constructor(...values) {
|
||||||
|
super()
|
||||||
this.type = 'Enum'
|
this.type = 'Enum'
|
||||||
this.values = values
|
this.values = values
|
||||||
|
this.legacyValues = {}
|
||||||
this.translatedValues = values.map(x => qsTr(x))
|
this.translatedValues = values.map(x => qsTr(x))
|
||||||
}
|
}
|
||||||
|
|
||||||
toString() {
|
toString() {
|
||||||
return this.type
|
return `${this.type}(${this.values.join(', ')})`
|
||||||
|
}
|
||||||
|
|
||||||
|
parse(value) {
|
||||||
|
let result = NONE
|
||||||
|
if(this.values.includes(value))
|
||||||
|
result = value
|
||||||
|
else if(this.legacyValues[value])
|
||||||
|
result = this.legacyValues[value]
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
export(value) {
|
||||||
|
if(this.values.includes(value))
|
||||||
|
return value
|
||||||
|
else if(this.legacyValues[value])
|
||||||
|
return this.legacyValues[value]
|
||||||
|
else
|
||||||
|
throw new TypeError(`Exportation error: ${value} not one of ${this.values.join(', ')}.`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ObjectType {
|
|
||||||
constructor(objType) {
|
export class ObjectType extends PropertyType {
|
||||||
|
constructor(objType, allowNull=false) {
|
||||||
|
super()
|
||||||
this.type = 'ObjectType'
|
this.type = 'ObjectType'
|
||||||
this.objType = objType
|
this.objType = objType
|
||||||
|
this.allowNull = allowNull
|
||||||
}
|
}
|
||||||
|
|
||||||
toString() {
|
toString() {
|
||||||
return this.objType
|
return this.objType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
parse(name) {
|
||||||
|
let result = NONE
|
||||||
|
if(typeof name == 'string' && name in Modules.Objects.currentObjectsByName) {
|
||||||
|
let obj = Modules.Objects.currentObjectsByName[name]
|
||||||
|
if (obj.type === this.objType || (this.objType === 'ExecutableObject' && obj.execute)) {
|
||||||
|
result = obj
|
||||||
|
} else {
|
||||||
|
// Silently error and return null
|
||||||
|
console.trace()
|
||||||
|
console.error(new TypeError(`Object ${name} is of not of type ${this.objType}:`))
|
||||||
|
}
|
||||||
|
} else if(this.allowNull && (name == null || name === "null"))
|
||||||
|
result = null
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
export(value) {
|
||||||
|
if(value.type === this.objType || (this.objType === 'ExecutableObject' && value.execute))
|
||||||
|
return value
|
||||||
|
else
|
||||||
|
throw new TypeError(`Exportation error: ${value} not a ${this.objType}.`)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class List {
|
|
||||||
|
export class List extends PropertyType {
|
||||||
constructor(type, format = /^.+$/, label = '', forbidAdding = false) {
|
constructor(type, format = /^.+$/, label = '', forbidAdding = false) {
|
||||||
|
super()
|
||||||
// type can be string, int and double.
|
// type can be string, int and double.
|
||||||
this.type = 'List'
|
this.type = 'List'
|
||||||
this.valueType = type
|
this.valueType = type
|
||||||
|
if(!stringValidatorTypes.includes(this.valueType))
|
||||||
|
throw new TypeError(`${this.valueType} must be one of ${stringValidatorTypes.join(', ')}.`)
|
||||||
this.format = format
|
this.format = format
|
||||||
this.label = label
|
this.label = label
|
||||||
this.forbidAdding = forbidAdding
|
this.forbidAdding = forbidAdding
|
||||||
}
|
}
|
||||||
|
|
||||||
toString() {
|
toString() {
|
||||||
return this.objType
|
return `${this.type}(${this.valueType}:${this.format})`
|
||||||
|
}
|
||||||
|
|
||||||
|
parse(value) {
|
||||||
|
let result = NONE
|
||||||
|
if(typeof value == 'object' && value.__proto__ === Array) {
|
||||||
|
let list = []
|
||||||
|
for(let v of value) {
|
||||||
|
if (this.format.test(v)) {
|
||||||
|
v = stringValuesValidators[this.valueType][0](v)
|
||||||
|
if(stringValuesValidators[this.valueType][1](v))
|
||||||
|
list.append(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(list.length === value.length)
|
||||||
|
result = value
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
export(value) {
|
||||||
|
if(typeof value == 'object' && value.__proto__ === Array)
|
||||||
|
return value
|
||||||
|
else
|
||||||
|
throw new TypeError(`Exportation error: ${value} not a list.`)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Dictionary {
|
|
||||||
|
export class Dictionary extends PropertyType {
|
||||||
constructor(type, keyType = 'string', format = /^.+$/, keyFormat = /^.+$/, preKeyLabel = '', postKeyLabel = ': ', forbidAdding = false) {
|
constructor(type, keyType = 'string', format = /^.+$/, keyFormat = /^.+$/, preKeyLabel = '', postKeyLabel = ': ', forbidAdding = false) {
|
||||||
|
super()
|
||||||
// type & keyType can be string, int and double.
|
// type & keyType can be string, int and double.
|
||||||
this.type = 'Dict'
|
this.type = 'Dict'
|
||||||
this.valueType = type
|
this.valueType = type
|
||||||
|
@ -77,9 +209,35 @@ export class Dictionary {
|
||||||
this.postKeyLabel = postKeyLabel
|
this.postKeyLabel = postKeyLabel
|
||||||
this.forbidAdding = forbidAdding
|
this.forbidAdding = forbidAdding
|
||||||
}
|
}
|
||||||
|
|
||||||
toString() {
|
toString() {
|
||||||
return 'Dictionary'
|
return `${this.type}(${this.keyType}:${this.keyFormat}: ${this.valueType}:${this.format})`
|
||||||
|
}
|
||||||
|
|
||||||
|
parse(value) {
|
||||||
|
let result = NONE
|
||||||
|
if(typeof value == 'object' && value.__proto__ !== Array) {
|
||||||
|
let dict = []
|
||||||
|
for(let [k, v] of Object.entries(value)) {
|
||||||
|
if (this.format.test(v) && this.keyFormat.test(k)) {
|
||||||
|
k = stringValuesValidators[this.keyType][0](k)
|
||||||
|
v = stringValuesValidators[this.valueType][0](v)
|
||||||
|
if(stringValuesValidators[this.keyType][1](k))
|
||||||
|
if(stringValuesValidators[this.valueType][1](v))
|
||||||
|
dict[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(Object.keys(dict).length === Object.keys(value).length)
|
||||||
|
result = value
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
export(value) {
|
||||||
|
if(typeof value == 'object' && value.__proto__ !== Array)
|
||||||
|
return value
|
||||||
|
else
|
||||||
|
throw new TypeError(`Exportation error: ${value} not a dictionary.`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,6 +253,14 @@ Enum.Position = new Enum(
|
||||||
QT_TR_NOOP('below-left'),
|
QT_TR_NOOP('below-left'),
|
||||||
QT_TR_NOOP('below-right')
|
QT_TR_NOOP('below-right')
|
||||||
)
|
)
|
||||||
|
Enum.Position.legacyValues = {
|
||||||
|
'top': 'above',
|
||||||
|
'bottom': 'below',
|
||||||
|
'top-left': 'above-left',
|
||||||
|
'top-right': 'above-right',
|
||||||
|
'bottom-left': 'below-left',
|
||||||
|
'bottom-right': 'below-right',
|
||||||
|
}
|
||||||
|
|
||||||
Enum.Positioning = new Enum(
|
Enum.Positioning = new Enum(
|
||||||
QT_TR_NOOP('center'),
|
QT_TR_NOOP('center'),
|
||||||
|
@ -125,4 +291,86 @@ Enum.XCursorValuePosition = new Enum(
|
||||||
QT_TR_NOOP('Hidden')
|
QT_TR_NOOP('Hidden')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures whether a provided value is of the corresponding type.
|
||||||
|
* @param {string|PropertyType} propertyType
|
||||||
|
* @param {string|number|boolean|array|object}value
|
||||||
|
* @returns {Object}
|
||||||
|
*/
|
||||||
|
export function ensureTypeSafety(propertyType, value) {
|
||||||
|
let result
|
||||||
|
let error = false
|
||||||
|
if(typeof propertyType == 'string')
|
||||||
|
switch(propertyType) {
|
||||||
|
case 'string':
|
||||||
|
result = value
|
||||||
|
error = typeof value !== 'string'
|
||||||
|
break
|
||||||
|
case 'number':
|
||||||
|
result = parseFloat(value)
|
||||||
|
error = isNaN(result)
|
||||||
|
break
|
||||||
|
case 'boolean':
|
||||||
|
result = value
|
||||||
|
error = value !== true && value !== false
|
||||||
|
break
|
||||||
|
case 'Domain':
|
||||||
|
try {
|
||||||
|
error = typeof value !== 'string'
|
||||||
|
if(!error)
|
||||||
|
result = parseDomain(value)
|
||||||
|
} catch(e) {
|
||||||
|
// Parse domain sometimes returns an empty set when it cannot parse a domain.
|
||||||
|
// It's okay though, it shouldn't be user parsed value, so returning an empty set
|
||||||
|
// is okay for a corrupted file, rather than erroring.
|
||||||
|
console.trace()
|
||||||
|
console.log(`Error parsing domain ${value}:`)
|
||||||
|
console.error(e)
|
||||||
|
error = true
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
else if(propertyType instanceof PropertyType) {
|
||||||
|
result = propertyType.parse(value)
|
||||||
|
error = result === NONE
|
||||||
|
} else
|
||||||
|
throw new TypeError(`Importation error: Unknown property type ${propertyType}.`)
|
||||||
|
if(error) {
|
||||||
|
console.trace()
|
||||||
|
throw new TypeError(`Importation error: Couldn't parse ${JSON.stringify(value)} as ${propertyType}.`)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serializes a property by its type to export into JSON.
|
||||||
|
* @param {string|PropertyType} propertyType
|
||||||
|
* @param value
|
||||||
|
* @returns {Object}
|
||||||
|
*/
|
||||||
|
export function serializesByPropertyType(propertyType, value) {
|
||||||
|
let result
|
||||||
|
if(typeof propertyType == 'string')
|
||||||
|
switch(propertyType) {
|
||||||
|
case 'string':
|
||||||
|
result = value.toString()
|
||||||
|
break
|
||||||
|
case 'number':
|
||||||
|
result = parseFloat(value)
|
||||||
|
break
|
||||||
|
case 'boolean':
|
||||||
|
result = value === true
|
||||||
|
break
|
||||||
|
case 'Domain':
|
||||||
|
if(value instanceof Domain)
|
||||||
|
result = value.toString()
|
||||||
|
else
|
||||||
|
throw new TypeError(`Exportation error: ${value} is not a domain.`)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
else if(propertyType instanceof PropertyType) {
|
||||||
|
result = propertyType.export(value)
|
||||||
|
} else
|
||||||
|
throw new TypeError(`Exportation error: Unknown property type ${propertyType}.`)
|
||||||
|
return result
|
||||||
|
}
|
Loading…
Reference in a new issue