diff --git a/src.compiler/BuilderHelpers.ts b/src.compiler/BuilderHelpers.ts index ead0b0e9f..2aebd0c6a 100644 --- a/src.compiler/BuilderHelpers.ts +++ b/src.compiler/BuilderHelpers.ts @@ -1,10 +1,57 @@ import * as ts from 'typescript'; +export function setMethodBody(m: ts.MethodDeclaration, body: ts.FunctionBody): ts.MethodDeclaration { + return ts.factory.updateMethodDeclaration( + m, + m.decorators, + m.modifiers, + m.asteriskToken, + m.name, + m.questionToken, + m.typeParameters, + m.parameters, + m.type, + body + ); +} + +export function createNodeFromSource<T extends ts.Node>(source: string, kind: ts.SyntaxKind): T { + const sourceFile = ts.createSourceFile( + 'temp.ts', + source.trim(), + ts.ScriptTarget.Latest, + /*setParentNodes */ true, + ts.ScriptKind.TS + ); + const node = findNode(sourceFile, kind); + if (!node) { + throw new Error( + `Could not parse TS source to ${ts.SyntaxKind[kind]}, node count was ${sourceFile.getChildCount()}` + ); + } + return markNodeSynthesized(node) as T; +} + +function findNode(node: ts.Node, kind: ts.SyntaxKind): ts.Node | null { + if (node.kind === kind) { + return node; + } + + for (const c of node.getChildren()) { + const f = findNode(c, kind); + if (f) { + return f; + } + } + + return null; +} + export function addNewLines(stmts: ts.Statement[]) { return stmts.map(stmt => ts.addSyntheticTrailingComment(stmt, ts.SyntaxKind.SingleLineCommentTrivia, '', true)); } export function getTypeWithNullableInfo(checker: ts.TypeChecker, node: ts.TypeNode | undefined) { - if(!node) { + if (!node) { return { isNullable: false, type: {} as ts.Type @@ -20,7 +67,12 @@ export function getTypeWithNullableInfo(checker: ts.TypeChecker, node: ts.TypeNo } else if (ts.isLiteralTypeNode(t) && t.literal.kind === ts.SyntaxKind.NullKeyword) { isNullable = true; } else if (type !== null) { - throw new Error('Multi union types on JSON settings not supported: ' + node.getSourceFile().fileName + ':' + node.getText()); + throw new Error( + 'Multi union types on JSON settings not supported: ' + + node.getSourceFile().fileName + + ':' + + node.getText() + ); } else { type = checker.getTypeAtLocation(t); } @@ -52,7 +104,6 @@ export function unwrapArrayItemType(type: ts.Type, typeChecker: ts.TypeChecker): return null; } - export function isPrimitiveType(type: ts.Type | null) { if (!type) { return false; @@ -85,7 +136,7 @@ export function isNumberType(type: ts.Type | null) { if (hasFlag(type, ts.TypeFlags.Number)) { return true; } - + return false; } @@ -116,4 +167,15 @@ export function hasFlag(type: ts.Type, flag: ts.TypeFlags): boolean { export function isMap(type: ts.Type | null): boolean { return !!(type && type.symbol?.name === 'Map'); -} \ No newline at end of file +} + +function markNodeSynthesized(node: ts.Node): ts.Node { + for(const c of node.getChildren()) { + markNodeSynthesized(c); + } + ts.setTextRange(node, { + pos: -1, + end: -1 + }); + return node; +} diff --git a/src.compiler/csharp/CSharpAst.ts b/src.compiler/csharp/CSharpAst.ts index a738226a8..8a356f203 100644 --- a/src.compiler/csharp/CSharpAst.ts +++ b/src.compiler/csharp/CSharpAst.ts @@ -21,6 +21,7 @@ export enum SyntaxKind { PrimitiveTypeNode, EnumMember, ArrayTypeNode, + MapTypeNode, Block, EmptyStatement, @@ -138,6 +139,7 @@ export interface NamedTypeDeclaration extends NamedElement, DocumentedElement, N typeParameters?: TypeParameterDeclaration[]; visibility: Visibility; partial: boolean; + hasVirtualMembersOrSubClasses: boolean; } export interface ClassDeclaration extends NamedTypeDeclaration { @@ -236,7 +238,7 @@ export interface UnresolvedTypeNode extends TypeNode { typeArguments?: UnresolvedTypeNode[]; } -export type TypeReferenceType = NamedTypeDeclaration | TypeParameterDeclaration | PrimitiveTypeNode | string; +export type TypeReferenceType = NamedTypeDeclaration | TypeParameterDeclaration | TypeNode | string; export interface TypeReference extends TypeNode { reference: TypeReferenceType; typeArguments?: TypeNode[]; @@ -246,6 +248,13 @@ export interface ArrayTypeNode extends TypeNode { elementType: TypeNode; } +export interface MapTypeNode extends TypeNode { + keyType: TypeNode; + keyIsValueType: boolean; + valueType: TypeNode; + valueIsValueType:boolean; +} + export interface FunctionTypeNode extends TypeNode { parameterTypes: TypeNode[]; returnType: TypeNode; @@ -492,6 +501,7 @@ export interface CatchClause extends Node { } // Node Tests +export function isNode(node: any): node is Node { return typeof(node) === 'object' && 'nodeType' in node; } export function isSourceFile(node: Node): node is SourceFile { return node.nodeType === SyntaxKind.SourceFile; } export function isUsingDeclaration(node: Node): node is UsingDeclaration { return node.nodeType === SyntaxKind.UsingDeclaration; } export function isNamespaceDeclaration(node: Node): node is NamespaceDeclaration { return node.nodeType === SyntaxKind.NamespaceDeclaration; } @@ -511,6 +521,7 @@ export function isFunctionTypeNode(node: Node): node is FunctionTypeNode { retur export function isPrimitiveTypeNode(node: Node): node is PrimitiveTypeNode { return node.nodeType === SyntaxKind.PrimitiveTypeNode; } export function isEnumMember(node: Node): node is EnumMember { return node.nodeType === SyntaxKind.EnumMember; } export function isArrayTypeNode(node: Node): node is ArrayTypeNode { return node.nodeType === SyntaxKind.ArrayTypeNode; } +export function isMapTypeNode(node: Node): node is MapTypeNode { return node.nodeType === SyntaxKind.MapTypeNode; } export function isBlock(node: Node): node is Block { return node.nodeType === SyntaxKind.Block; } export function isEmptyStatement(node: Node): node is EmptyStatement { return node.nodeType === SyntaxKind.EmptyStatement; } diff --git a/src.compiler/csharp/CSharpAstPrinter.ts b/src.compiler/csharp/CSharpAstPrinter.ts index 37ec7f6c5..753ab8939 100644 --- a/src.compiler/csharp/CSharpAstPrinter.ts +++ b/src.compiler/csharp/CSharpAstPrinter.ts @@ -258,12 +258,10 @@ export default class CSharpAstPrinter extends AstPrinterBase { if (d.isAbstract) { this.write('abstract '); } - - if (d.isVirtual) { + else if (d.isVirtual) { this.write('virtual '); } - - if (d.isOverride) { + else if (d.isOverride) { this.write('override '); } @@ -351,12 +349,10 @@ export default class CSharpAstPrinter extends AstPrinterBase { if (d.isAbstract) { this.write('abstract '); } - - if (d.isVirtual) { + else if (d.isVirtual) { this.write('virtual '); } - - if (d.isOverride) { + else if (d.isOverride) { this.write('override '); } } @@ -483,7 +479,7 @@ export default class CSharpAstPrinter extends AstPrinterBase { this.write('System.Collections.IList'); } else { if (forNew) { - this.write('AlphaTab.Core.List<'); + this.write('AlphaTab.Collections.List<'); } else { this.write('System.Collections.Generic.IList<'); } @@ -492,6 +488,26 @@ export default class CSharpAstPrinter extends AstPrinterBase { } } + break; + case cs.SyntaxKind.MapTypeNode: + const mapType = type as cs.MapTypeNode; + if (!mapType.valueIsValueType) { + if (forNew) { + this.write('AlphaTab.Collections.Map<'); + } else { + this.write('AlphaTab.Collections.IMap<'); + } + } else { + if (forNew) { + this.write('AlphaTab.Collections.ValueTypeMap<'); + } else { + this.write('AlphaTab.Collections.IValueTypeMap<'); + } + } + this.writeType(mapType.keyType); + this.write(', '); + this.writeType(mapType.valueType); + this.write('>'); break; case cs.SyntaxKind.FunctionTypeNode: const functionType = type as cs.FunctionTypeNode; @@ -699,7 +715,7 @@ export default class CSharpAstPrinter extends AstPrinterBase { protected writeNonNullExpression(expr: cs.NonNullExpression) { this.writeExpression(expr.expression); - if(!cs.isNonNullExpression(expr)) { + if (!cs.isNonNullExpression(expr)) { this.write('!'); } } diff --git a/src.compiler/csharp/CSharpAstTransformer.ts b/src.compiler/csharp/CSharpAstTransformer.ts index a1c06e800..e96405a9d 100644 --- a/src.compiler/csharp/CSharpAstTransformer.ts +++ b/src.compiler/csharp/CSharpAstTransformer.ts @@ -296,10 +296,6 @@ export default class CSharpAstTransformer { additionalNestedNonExportsDeclarations?: ts.Declaration[], globalStatements?: ts.Statement[] ): ts.Node { - if (this.shouldSkip(node, false)) { - return node; - } - if (ts.isClassDeclaration(node)) { this.visitClassDeclaration( node, @@ -330,12 +326,17 @@ export default class CSharpAstTransformer { } const tags = ts.getJSDocTags(node).filter(t => t.tagName.text === 'target'); if (tags.length > 0) { - return !tags.find(t => t.comment === 'csharp'); + return !tags.find(t => t.comment === this.targetTag); } return false; } + public get targetTag(): string { + return 'csharp'; + } + + protected visitEnumDeclaration(node: ts.EnumDeclaration) { const csEnum: cs.EnumDeclaration = { visibility: cs.Visibility.Public, @@ -346,7 +347,8 @@ export default class CSharpAstTransformer { tsNode: node, partial: false, skipEmit: this.shouldSkip(node, false), - tsSymbol: this._context.getSymbolForDeclaration(node) + tsSymbol: this._context.getSymbolForDeclaration(node), + hasVirtualMembersOrSubClasses: false }; if (node.name) { @@ -398,7 +400,8 @@ export default class CSharpAstTransformer { tsNode: node, skipEmit: this.shouldSkip(node, false), partial: !!ts.getJSDocTags(node).find(t => t.tagName.text === 'partial'), - tsSymbol: this._context.getSymbolForDeclaration(node) + tsSymbol: this._context.getSymbolForDeclaration(node), + hasVirtualMembersOrSubClasses: false }; if (node.name) { @@ -424,7 +427,9 @@ export default class CSharpAstTransformer { }); } - node.members.forEach(m => this.visitInterfaceElement(csInterface, m)); + if(!csInterface.skipEmit){ + node.members.forEach(m => this.visitInterfaceElement(csInterface, m)); + } this._csharpFile.namespace.declarations.push(csInterface); this._context.registerSymbol(csInterface); @@ -499,7 +504,8 @@ export default class CSharpAstTransformer { parent: this._csharpFile.namespace, isAbstract: false, partial: false, - members: [] + members: [], + hasVirtualMembersOrSubClasses: false }; if (this._testClassAttribute.length > 0) { @@ -738,7 +744,8 @@ export default class CSharpAstTransformer { partial: !!ts.getJSDocTags(node).find(t => t.tagName.text === 'partial'), members: [], skipEmit: this.shouldSkip(node, false), - tsSymbol: this._context.getSymbolForDeclaration(node) + tsSymbol: this._context.getSymbolForDeclaration(node), + hasVirtualMembersOrSubClasses: false }; if (node.name) { @@ -773,32 +780,34 @@ export default class CSharpAstTransformer { }); } - node.members.forEach(m => this.visitClassElement(csClass, m)); + if (!csClass.skipEmit) { + node.members.forEach(m => this.visitClassElement(csClass, m)); - if (globalStatements && globalStatements.length > 0) { - const staticConstructor = { - parent: csClass, - isStatic: true, - name: 'cctor', - nodeType: cs.SyntaxKind.ConstructorDeclaration, - parameters: [], - visibility: cs.Visibility.None, - tsNode: node, - body: { - parent: null, - nodeType: cs.SyntaxKind.Block, - statements: [] - } as cs.Block - } as cs.ConstructorDeclaration; + if (globalStatements && globalStatements.length > 0) { + const staticConstructor = { + parent: csClass, + isStatic: true, + name: 'cctor', + nodeType: cs.SyntaxKind.ConstructorDeclaration, + parameters: [], + visibility: cs.Visibility.None, + tsNode: node, + body: { + parent: null, + nodeType: cs.SyntaxKind.Block, + statements: [] + } as cs.Block + } as cs.ConstructorDeclaration; - globalStatements.forEach(s => { - const st = this.visitStatement(staticConstructor.body!, s)!; - if (st) { - (staticConstructor.body as cs.Block).statements.push(st); - } - }); + globalStatements.forEach(s => { + const st = this.visitStatement(staticConstructor.body!, s)!; + if (st) { + (staticConstructor.body as cs.Block).statements.push(st); + } + }); - csClass.members.push(staticConstructor); + csClass.members.push(staticConstructor); + } } this._csharpFile.namespace.declarations.push(csClass); @@ -970,6 +979,10 @@ export default class CSharpAstTransformer { tsNode: classElement, body: classElement.body ? this.visitBlock(member, classElement.body) : null } as cs.PropertyAccessorDeclaration; + + if (this._context.markOverride(classElement)) { + member.isOverride = true; + } } else { const signature = this._context.typeChecker.getSignatureFromDeclaration(classElement); const returnType = this._context.typeChecker.getReturnTypeOfSignature(signature!); @@ -984,17 +997,13 @@ export default class CSharpAstTransformer { parent: parent, visibility: this.mapVisibility(classElement), type: this.createUnresolvedTypeNode(null, classElement.type ?? classElement, returnType), - skipEmit: this.shouldSkip(classElement, false) + skipEmit: this.shouldSkip(classElement, false), + tsNode: classElement, + tsSymbol: this._context.getSymbolForDeclaration(classElement), }; - if (newProperty.visibility === cs.Visibility.Public || newProperty.visibility === cs.Visibility.Protected) { - if (this._context.isOverride(classElement)) { - newProperty.isVirtual = false; - newProperty.isOverride = true; - } else { - newProperty.isVirtual = true; - newProperty.isOverride = false; - } + if (this._context.markOverride(classElement)) { + newProperty.isOverride = true; } if (classElement.modifiers) { @@ -1040,6 +1049,11 @@ export default class CSharpAstTransformer { tsNode: classElement, body: classElement.body ? this.visitBlock(member, classElement.body) : null } as cs.PropertyAccessorDeclaration; + + if (this._context.markOverride(classElement)) { + member.isOverride = true; + } + return member.setAccessor; } else { const signature = this._context.typeChecker.getSignatureFromDeclaration(classElement); @@ -1055,17 +1069,13 @@ export default class CSharpAstTransformer { parent: parent, visibility: this.mapVisibility(classElement), type: this.createUnresolvedTypeNode(null, classElement.type ?? classElement, returnType), - skipEmit: this.shouldSkip(classElement, false) + skipEmit: this.shouldSkip(classElement, false), + tsNode: classElement, + tsSymbol: this._context.getSymbolForDeclaration(classElement), }; - if (newProperty.visibility === cs.Visibility.Public || newProperty.visibility === cs.Visibility.Protected) { - if (this._context.isOverride(classElement)) { - newProperty.isVirtual = false; - newProperty.isOverride = true; - } else { - newProperty.isVirtual = true; - newProperty.isOverride = false; - } + if (this._context.markOverride(classElement)) { + newProperty.isOverride = true; } if (classElement.modifiers) { @@ -1119,23 +1129,18 @@ export default class CSharpAstTransformer { type: this.createUnresolvedTypeNode(null, classElement.type ?? classElement, type), visibility: visibility, tsNode: classElement, + tsSymbol: this._context.getSymbolForDeclaration(classElement), skipEmit: this.shouldSkip(classElement, false) }; - if (csProperty.visibility === cs.Visibility.Public || csProperty.visibility === cs.Visibility.Protected) { - if (this._context.isOverride(classElement)) { - csProperty.isVirtual = false; - csProperty.isOverride = true; - } else { - csProperty.isVirtual = true; - csProperty.isOverride = false; - } - } - if (classElement.name) { csProperty.documentation = this.visitDocumentation(classElement.name); } + if (this._context.markOverride(classElement)) { + csProperty.isOverride = true; + } + let isReadonly = false; if (classElement.modifiers) { classElement.modifiers.forEach(m => { @@ -1220,6 +1225,7 @@ export default class CSharpAstTransformer { returnType: this.createUnresolvedTypeNode(null, classElement.type ?? classElement, returnType), visibility: this.mapVisibility(classElement), tsNode: classElement, + tsSymbol: this._context.getSymbolForDeclaration(classElement), skipEmit: this.shouldSkip(classElement, false) }; @@ -1227,14 +1233,8 @@ export default class CSharpAstTransformer { csMethod.documentation = this.visitDocumentation(classElement.name); } - if (csMethod.visibility === cs.Visibility.Public || csMethod.visibility === cs.Visibility.Protected) { - if (this._context.isOverride(classElement)) { - csMethod.isVirtual = false; - csMethod.isOverride = true; - } else { - csMethod.isVirtual = true; - csMethod.isOverride = false; - } + if (this._context.markOverride(classElement)) { + csMethod.isOverride = true; } if (classElement.modifiers) { @@ -2822,28 +2822,7 @@ export default class CSharpAstTransformer { protected visitArrayLiteralExpression(parent: cs.Node, expression: ts.ArrayLiteralExpression) { if (this.isMapInitializer(expression)) { - const csExpr = { - parent: parent, - tsNode: expression, - nodeType: cs.SyntaxKind.InvocationExpression, - arguments: [], - expression: {} as cs.Expression - } as cs.InvocationExpression; - - csExpr.expression = this.makeMemberAccess( - csExpr, - this._context.makeTypeName('alphaTab.core.TypeHelper'), - this._context.toPascalCase('createMapEntry') - ); - - expression.elements.forEach(e => { - const ex = this.visitExpression(csExpr, e); - if (ex) { - csExpr.arguments.push(ex); - } - }); - - return csExpr; + return this.createMapEntry(parent, expression); } else if (this.isSetInitializer(expression)) { const csExpr = { parent: parent, @@ -2890,6 +2869,31 @@ export default class CSharpAstTransformer { return csExpr; } } + protected createMapEntry(parent: cs.Node, expression: ts.ArrayLiteralExpression): cs.Expression { + const csExpr = { + parent: parent, + tsNode: expression, + nodeType: cs.SyntaxKind.InvocationExpression, + arguments: [], + expression: {} as cs.Expression + } as cs.InvocationExpression; + + csExpr.expression = this.makeMemberAccess( + csExpr, + this._context.makeTypeName('alphaTab.core.TypeHelper'), + this._context.toPascalCase('createMapEntry') + ); + + expression.elements.forEach(e => { + const ex = this.visitExpression(csExpr, e); + if (ex) { + csExpr.arguments.push(ex); + } + }); + + return csExpr; + } + protected isMapInitializer(expression: ts.ArrayLiteralExpression) { const isCandidate = expression.elements.length === 2 && @@ -2976,6 +2980,12 @@ export default class CSharpAstTransformer { return 'TrimStart'; } break; + case 'Number': + switch (symbol.name) { + case 'toString': + return 'ToInvariantString'; + } + break; } return null; } @@ -3083,7 +3093,7 @@ export default class CSharpAstTransformer { } as cs.InvocationExpression; const memberAccess = (callExpr.expression = { expression: null!, - member: this._context.toPascalCase('toString'), + member: this._context.toPascalCase('toInvariantString'), parent: callExpr, tsNode: expr.tsNode, nodeType: cs.SyntaxKind.MemberAccessExpression @@ -3099,22 +3109,6 @@ export default class CSharpAstTransformer { expr.parent = par; memberAccess.expression = par; - const invariantCultureInfo = { - parent: callExpr, - nodeType: cs.SyntaxKind.MemberAccessExpression, - tsNode: expr.tsNode, - expression: null!, - member: this._context.toPascalCase('invariantCulture') - } as cs.MemberAccessExpression; - - invariantCultureInfo.expression = { - parent: invariantCultureInfo, - tsNode: expr.tsNode, - nodeType: cs.SyntaxKind.Identifier, - text: this._context.makeTypeName('system.globalization.CultureInfo') - } as cs.Identifier; - callExpr.arguments.push(invariantCultureInfo); - return callExpr; } @@ -3217,6 +3211,13 @@ export default class CSharpAstTransformer { nodeType: cs.SyntaxKind.InvocationExpression } as cs.InvocationExpression; + // number.ToString + const isNumberToString = + ts.isPropertyAccessExpression(expression.expression) && + this._context.typeChecker.getTypeAtLocation(expression.expression.expression).flags & ts.TypeFlags.Number && + (expression.expression.name as ts.Identifier).text === 'toString' && + expression.arguments.length === 0; + callExpression.expression = this.visitExpression(callExpression, expression.expression)!; if (!callExpression.expression) { return null; @@ -3236,31 +3237,6 @@ export default class CSharpAstTransformer { } }); - // number.ToString - const isNumberToString = - ts.isPropertyAccessExpression(expression.expression) && - this._context.typeChecker.getTypeAtLocation(expression.expression.expression).flags & ts.TypeFlags.Number && - (expression.expression.name as ts.Identifier).text === 'toString' && - expression.arguments.length === 0; - - if (isNumberToString) { - const invariantCultureInfo = { - parent: parent, - nodeType: cs.SyntaxKind.MemberAccessExpression, - tsNode: expression, - expression: null!, - member: this._context.toPascalCase('invariantCulture') - } as cs.MemberAccessExpression; - - invariantCultureInfo.expression = { - parent: invariantCultureInfo, - tsNode: expression.expression, - nodeType: cs.SyntaxKind.Identifier, - text: this._context.makeTypeName('system.globalization.CultureInfo') - } as cs.Identifier; - - callExpression.arguments.push(invariantCultureInfo); - } if (expression.typeArguments) { callExpression.typeArguments = []; @@ -3448,7 +3424,7 @@ export default class CSharpAstTransformer { (node.tsSymbol.flags & ts.SymbolFlags.Variable) === ts.SymbolFlags.Variable || (node.tsSymbol.flags & ts.SymbolFlags.EnumMember) === ts.SymbolFlags.EnumMember || (node.tsSymbol.flags & ts.SymbolFlags.FunctionScopedVariable) === - ts.SymbolFlags.FunctionScopedVariable || + ts.SymbolFlags.FunctionScopedVariable || (node.tsSymbol.flags & ts.SymbolFlags.BlockScopedVariable) === ts.SymbolFlags.BlockScopedVariable ) { let smartCastType = this._context.getSmartCastType(expression); diff --git a/src.compiler/csharp/CSharpEmitterContext.ts b/src.compiler/csharp/CSharpEmitterContext.ts index 05984d06a..81260748a 100644 --- a/src.compiler/csharp/CSharpEmitterContext.ts +++ b/src.compiler/csharp/CSharpEmitterContext.ts @@ -8,6 +8,7 @@ export default class CSharpEmitterContext { private _fileLookup: Map<ts.SourceFile, cs.SourceFile> = new Map(); private _symbolLookup: Map<SymbolKey, cs.NamedElement & cs.Node> = new Map(); private _exportedSymbols: Map<SymbolKey, boolean> = new Map(); + private _virtualSymbols: Map<SymbolKey, boolean> = new Map(); private _symbolConst: Map<SymbolKey, boolean> = new Map(); private _diagnostics: ts.Diagnostic[] = []; @@ -314,38 +315,7 @@ export default class CSharpEmitterContext { mapValueType = this.getTypeFromTsType(node, mapType.typeArguments[1]); } - let isValueType = false; - if (mapValueType) { - switch (mapValueType.nodeType) { - case cs.SyntaxKind.PrimitiveTypeNode: - switch ((mapValueType as cs.PrimitiveTypeNode).type) { - case cs.PrimitiveType.Bool: - case cs.PrimitiveType.Int: - case cs.PrimitiveType.Double: - isValueType = true; - break; - } - break; - case cs.SyntaxKind.TypeReference: - const ref = (mapValueType as cs.TypeReference).reference; - if (typeof ref !== 'string') { - switch (ref.nodeType) { - case cs.SyntaxKind.EnumDeclaration: - isValueType = true; - break; - } - } - break; - } - } - - return { - nodeType: cs.SyntaxKind.TypeReference, - parent: node.parent, - tsNode: node.tsNode, - reference: this.buildCoreNamespace(tsSymbol) + (isValueType ? 'ValueTypeMap' : 'Map'), - typeArguments: [mapKeyType, mapValueType] - } as cs.TypeReference; + return this.createMapType(tsSymbol, node, mapKeyType!, mapValueType!); case 'Array': const arrayType = tsType as ts.TypeReference; let arrayElementType: cs.TypeNode | null = null; @@ -364,13 +334,7 @@ export default class CSharpEmitterContext { } as cs.PrimitiveTypeNode; } - return { - nodeType: cs.SyntaxKind.ArrayTypeNode, - parent: node.parent, - tsNode: node.tsNode, - elementType: arrayElementType - } as cs.ArrayTypeNode; - + return this.createArrayListType(tsSymbol, node, arrayElementType); case ts.InternalSymbolName.Type: let csType: cs.TypeNode | null = null; @@ -389,6 +353,57 @@ export default class CSharpEmitterContext { } } + protected createArrayListType(tsSymbol: ts.Symbol, node: cs.Node, arrayElementType: cs.TypeNode): cs.TypeNode { + return { + nodeType: cs.SyntaxKind.ArrayTypeNode, + parent: node.parent, + tsNode: node.tsNode, + elementType: arrayElementType + } as cs.ArrayTypeNode; + } + + protected createMapType( + symbol: ts.Symbol, + node: cs.Node, + mapKeyType: cs.TypeNode, + mapValueType: cs.TypeNode + ): cs.TypeNode { + return { + nodeType: cs.SyntaxKind.MapTypeNode, + parent: node.parent, + tsNode: node.tsNode, + keyType: mapKeyType, + valueType: mapValueType, + valueIsValueType: this.isCsValueType(mapValueType), + keyIsValueType: this.isCsValueType(mapKeyType) + } as cs.MapTypeNode; + } + + protected isCsValueType(mapValueType: cs.TypeNode) { + if (mapValueType) { + switch (mapValueType.nodeType) { + case cs.SyntaxKind.PrimitiveTypeNode: + switch ((mapValueType as cs.PrimitiveTypeNode).type) { + case cs.PrimitiveType.Bool: + case cs.PrimitiveType.Int: + case cs.PrimitiveType.Double: + return true; + } + break; + case cs.SyntaxKind.TypeReference: + const ref = (mapValueType as cs.TypeReference).reference; + if (typeof ref !== 'string') { + switch (ref.nodeType) { + case cs.SyntaxKind.EnumDeclaration: + return true; + } + } + break; + } + } + return false; + } + private resolveFunctionTypeFromTsType(node: cs.Node, tsType: ts.Type): cs.TypeNode | null { // typescript compiler API somehow does not provide proper type symbols // for function types, we need to attempt resolving the types via the function type declaration @@ -754,8 +769,13 @@ export default class CSharpEmitterContext { return result; } - private buildCoreNamespace(aliasSymbol: ts.Symbol) { + protected buildCoreNamespace(aliasSymbol: ts.Symbol) { let suffix = ''; + + if(aliasSymbol.name === 'Map') { + return this.toPascalCase('alphaTab.collections') + suffix + '.'; + } + if (aliasSymbol.declarations) { for (const decl of aliasSymbol.declarations) { let fileName = path.basename(decl.getSourceFile().fileName).toLowerCase(); @@ -775,9 +795,13 @@ export default class CSharpEmitterContext { } } } + return this.toPascalCase('alphaTab.core') + suffix + '.'; } protected toCoreTypeName(s: string) { + if (s === 'Map') { + return 'IMap' + } return s; } @@ -844,8 +868,8 @@ export default class CSharpEmitterContext { const declaration = symbol.valueDeclaration ? symbol.valueDeclaration : symbol.declarations && symbol.declarations.length > 0 - ? symbol.declarations[0] - : undefined; + ? symbol.declarations[0] + : undefined; if (declaration) { return symbol.name + '_' + declaration.getSourceFile().fileName + '_' + declaration.pos; @@ -1052,7 +1076,7 @@ export default class CSharpEmitterContext { return null; } const declarations = symbol.declarations; - if(!declarations || declarations.length === 0){ + if (!declarations || declarations.length === 0) { return null; } @@ -1166,7 +1190,12 @@ export default class CSharpEmitterContext { return false; } - public isOverride(classElement: ts.ClassElement): boolean { + public markAsSubclassed(classElement: ts.Symbol) { + const key = this.getSymbolKey(classElement); + this._virtualSymbols.set(key, true); + } + + public markOverride(classElement: ts.ClassElement): boolean { let parent: ts.Node = classElement; while (parent.kind !== ts.SyntaxKind.ClassDeclaration) { if (parent.parent) { @@ -1187,45 +1216,42 @@ export default class CSharpEmitterContext { return false; } - if (this.isClassElementOverride(classType, classElement)) { - return true; + + const overridden = this.getOverriddenMembers(classType, classElement); + if(overridden.length > 0) { + const member = this.typeChecker.getSymbolAtLocation(classElement) ?? this.typeChecker.getSymbolAtLocation(classElement.name!); + this._virtualSymbols.set(this.getSymbolKey(member), true); + + for (const s of overridden) { + const symbolKey = this.getSymbolKey(s); + this._virtualSymbols.set(symbolKey, true); + } } + - return false; + return overridden.length > 0; } - protected isClassElementOverride(classType: ts.InterfaceType, classElement: ts.ClassElement) { - return this.hasAnyBaseTypeClassMember(classType, classElement.name!.getText()); + protected getOverriddenMembers(classType: ts.InterfaceType, classElement: ts.ClassElement): ts.Symbol[] { + const symbols: ts.Symbol[] = []; + this.collectOverriddenMembersByName(symbols, classType, classElement.name!.getText(), false, false); + return symbols; } - protected hasAnyBaseTypeClassMember(classType: ts.Type, memberName: string, allowInterfaces: boolean = false) { - const baseTypes = classType.getBaseTypes(); - if (!baseTypes) { - return false; + protected collectOverriddenMembersByName(symbols: ts.Symbol[], classType: ts.InterfaceType, memberName: string, includeOwnMembers: boolean = false, allowInterfaces: boolean = false) { + const member = classType.symbol?.members?.get(ts.escapeLeadingUnderscores(memberName)); + if (includeOwnMembers && member) { + symbols.push(member); } - for (const baseType of baseTypes) { - if ( - ((allowInterfaces && baseType.isClassOrInterface()) || baseType.isClass()) && - this.hasClassMember(baseType, memberName) - ) { - return true; + const baseTypes = classType.getBaseTypes(); + if (baseTypes) { + for (const baseType of baseTypes) { + if (((allowInterfaces && baseType.isClassOrInterface()) || baseType.isClass())) { + this.collectOverriddenMembersByName(symbols, baseType, memberName, true, allowInterfaces); + } } } - - return false; - } - - protected hasClassMember(baseType: ts.Type, name: string): boolean { - if ( - baseType.symbol && - baseType.symbol.members && - baseType.symbol.members.has(ts.escapeLeadingUnderscores(name)) - ) { - return true; - } - - return this.hasAnyBaseTypeClassMember(baseType, name); } public isValueTypeExpression(expression: ts.NonNullExpression) { @@ -1266,31 +1292,100 @@ export default class CSharpEmitterContext { } public rewriteVisibilities() { - const visited: Set<SymbolKey> = new Set(); + const visitedVisibility: Set<SymbolKey> = new Set(); + const visitedVirtual: Map<SymbolKey, boolean> = new Map(); for (const kvp of this._symbolLookup) { const symbolKey = this.getSymbolKey(kvp[1].tsSymbol!); switch (kvp[1].nodeType) { case cs.SyntaxKind.ClassDeclaration: case cs.SyntaxKind.EnumDeclaration: case cs.SyntaxKind.InterfaceDeclaration: - if (!visited.has(symbolKey)) { - const csType = kvp[1] as cs.NamedTypeDeclaration; + const csType = kvp[1] as cs.NamedTypeDeclaration; + if (!visitedVisibility.has(symbolKey)) { const shouldBePublic = !!ts .getJSDocTags(csType.tsNode!) .find(t => t.tagName.text === 'csharp_public'); if (csType.visibility === cs.Visibility.Public || shouldBePublic) { if (this._exportedSymbols.has(symbolKey) || shouldBePublic) { - this.makePublic(csType, visited); + this.makePublic(csType, visitedVisibility); } else { csType.visibility = cs.Visibility.Internal; } } } + + if (this.makeVirtual(csType, visitedVirtual)) { + csType.hasVirtualMembersOrSubClasses = true; + } + break; } } } + private makeVirtual(node: cs.Node, visited: Map<SymbolKey, boolean>): boolean { + const x = this.getSymbolKey(this.getSymbolForDeclaration(node.tsNode!)); + if (visited.has(x)) { + return visited.get(x)!; + } + + let hasVirtualMember = false; + + switch (node.nodeType) { + case cs.SyntaxKind.ClassDeclaration: + const csClass = node as cs.ClassDeclaration; + csClass.members.forEach(m => { + if(this.makeVirtual(m, visited)) { + hasVirtualMember = true; + } + }); + + let baseClass = csClass.baseClass; + while(baseClass != null) { + if(cs.isTypeReference(baseClass)) { + const ref = baseClass.reference; + if(cs.isNode(ref) && cs.isClassDeclaration(ref)) { + ref.hasVirtualMembersOrSubClasses = true; + baseClass = ref; + } else { + break; + } + } else { + break; + } + } + + break; + + case cs.SyntaxKind.MethodDeclaration: + const csMethod = node as cs.MethodDeclaration; + + const methodKey = this.getSymbolKey(csMethod.tsSymbol!); + if(!csMethod.isOverride && this._virtualSymbols.has(methodKey)) { + csMethod.isVirtual = true; + hasVirtualMember = true; + } + + break; + + case cs.SyntaxKind.PropertyDeclaration: + const csProperty = node as cs.PropertyDeclaration; + + const propKey = this.getSymbolKey(csProperty.tsSymbol!); + if(!csProperty.isOverride && this._virtualSymbols.has(propKey)) { + csProperty.isVirtual = true; + hasVirtualMember = true; + } + + break; + } + + visited.set(x, hasVirtualMember); + + return hasVirtualMember; + } + + private makePublic(node: cs.Node, visited: Set<SymbolKey>) { if (node.tsSymbol) { const x = this.getSymbolKey(node.tsSymbol); @@ -1372,6 +1467,11 @@ export default class CSharpEmitterContext { this.makePublic(csArrayType.elementType, visited); } break; + case cs.SyntaxKind.MapTypeNode: + const mapType = node as cs.MapTypeNode; + this.makePublic(mapType.keyType, visited); + this.makePublic(mapType.valueType, visited); + break; } } diff --git a/src.compiler/kotlin/KotlinAstPrinter.ts b/src.compiler/kotlin/KotlinAstPrinter.ts index bacecde56..c20c3186b 100644 --- a/src.compiler/kotlin/KotlinAstPrinter.ts +++ b/src.compiler/kotlin/KotlinAstPrinter.ts @@ -246,7 +246,7 @@ export default class KotlinAstPrinter extends AstPrinterBase { if (d.isAbstract) { this.write('abstract '); - } else { + } else if(d.hasVirtualMembersOrSubClasses) { this.write('open '); } @@ -343,12 +343,12 @@ export default class KotlinAstPrinter extends AstPrinterBase { defaultConstructor.parameters = constructorDeclaration.parameters; defaultConstructor.baseConstructorArguments = constructorDeclaration.parameters.map( p => - ({ - parent: defaultConstructor, - nodeType: cs.SyntaxKind.Identifier, - text: p.name, - tsNode: defaultConstructor.tsNode - } as cs.Identifier) + ({ + parent: defaultConstructor, + nodeType: cs.SyntaxKind.Identifier, + text: p.name, + tsNode: defaultConstructor.tsNode + } as cs.Identifier) ); this.writeMember(defaultConstructor); } else { @@ -617,22 +617,60 @@ export default class KotlinAstPrinter extends AstPrinterBase { this.writeType(arrayType.elementType); this.write('>'); } else { - const isDynamicArray = - cs.isPrimitiveTypeNode(arrayType.elementType) && - arrayType.elementType.type === cs.PrimitiveType.Dynamic; - if (isDynamicArray && !forNew) { - this.write('kotlin.collections.MutableList<*>'); + var elementTypeName = this.getContainerTypeName(arrayType.elementType); + if (elementTypeName) { + this.write('alphaTab.collections.'); + this.write(elementTypeName); + this.write('List'); } else { - if (forNew) { - this.write('alphaTab.core.LateInitList<'); + const isDynamicArray = + cs.isPrimitiveTypeNode(arrayType.elementType) && + arrayType.elementType.type === cs.PrimitiveType.Dynamic; + if (isDynamicArray && !forNew) { + this.write('alphaTab.collections.List<*>'); } else { - this.write('kotlin.collections.MutableList<'); + if (forNew) { + this.write('alphaTab.collections.List<'); + } else { + this.write('alphaTab.collections.List<'); + } + this.writeType(arrayType.elementType); + this.write('>'); } - this.writeType(arrayType.elementType); - this.write('>'); } } + break; + case cs.SyntaxKind.MapTypeNode: + const mapType = type as cs.MapTypeNode; + + var keyTypeName = this.getContainerTypeName(mapType.keyType); + var valueTypeName = this.getContainerTypeName(mapType.valueType); + + this.write('alphaTab.collections.'); + if (keyTypeName && valueTypeName) { + + this.write(keyTypeName); + this.write(valueTypeName); + this.write('Map'); + } else if (keyTypeName) { + this.write(keyTypeName); + this.write('ObjectMap<'); + this.writeType(mapType.valueType); + this.write('>'); + } else if (valueTypeName) { + this.write('Object'); + this.write(valueTypeName); + this.write('Map<'); + this.writeType(mapType.keyType); + this.write('>'); + } else { + this.write('Map<'); + this.writeType(mapType.keyType); + this.write(', '); + this.writeType(mapType.valueType); + this.write('>'); + } break; case cs.SyntaxKind.FunctionTypeNode: const functionType = type as cs.FunctionTypeNode; @@ -682,6 +720,25 @@ export default class KotlinAstPrinter extends AstPrinterBase { } } + private getContainerTypeName(type: cs.TypeNode): string | null { + switch (type.nodeType) { + case cs.SyntaxKind.PrimitiveTypeNode: + if (type.isNullable) { + return null; + } + switch ((type as cs.PrimitiveTypeNode).type) { + case cs.PrimitiveType.Bool: + return 'Boolean'; + case cs.PrimitiveType.Int: + return 'Int'; + case cs.PrimitiveType.Double: + return 'Double'; + } + break; + } + return null; + } + protected writeTypeOfExpression(expr: cs.TypeOfExpression) { if (expr.expression) { this.writeExpression(expr.expression); @@ -875,11 +932,26 @@ export default class KotlinAstPrinter extends AstPrinterBase { protected writeArrayCreationExpression(expr: cs.ArrayCreationExpression) { if (expr.type) { if (expr.values) { - this.write('arrayListOf'); - if (expr.type && cs.isArrayTypeNode(expr.type)) { - this.write('<'); - this.writeType(expr.type.elementType); - this.write('>'); + let elementType: cs.TypeNode | null = null; + if (cs.isArrayTypeNode(expr.type)) { + elementType = expr.type.elementType; + } + else if (cs.isTypeReference(expr.type) && typeof (expr.type.reference) !== 'string' && cs.isArrayTypeNode(expr.type.reference)) { + elementType = expr.type.reference.elementType; + } + + let type = elementType ? this.getContainerTypeName(elementType) : null; + this.write('alphaTab.collections.') + if (type) { + this.write(type); + this.write('List') + } else { + this.write('List'); + if (expr.type && cs.isArrayTypeNode(expr.type)) { + this.write('<'); + this.writeType(expr.type.elementType); + this.write('>'); + } } this.writeLine('('); this._indent++; @@ -898,7 +970,9 @@ export default class KotlinAstPrinter extends AstPrinterBase { this.write(')'); } } else if (expr.values && expr.values.length > 0) { - this.write('arrayListOf('); + // TODO: check for typed array creation + this.write('alphaTab.collections.') + this.write('List('); this.writeCommaSeparated(expr.values, v => { if (expr.values!.length > 10) { this.writeLine(); @@ -1147,7 +1221,7 @@ export default class KotlinAstPrinter extends AstPrinterBase { } protected writeForStatement(s: cs.ForStatement) { - if (!this.tryWriteForRange(s)) { + if (/*!this.tryWriteForRange(s)*/ true) { this.write('if(true) '); this.beginBlock(); @@ -1423,7 +1497,7 @@ export default class KotlinAstPrinter extends AstPrinterBase { this.writeLine(`import ${using.namespaceOrTypeName}.*`); } - protected writeSemicolon() { + protected override writeSemicolon() { this.writeLine(); } } diff --git a/src.compiler/kotlin/KotlinAstTransformer.ts b/src.compiler/kotlin/KotlinAstTransformer.ts index c7540600e..abbb66dfe 100644 --- a/src.compiler/kotlin/KotlinAstTransformer.ts +++ b/src.compiler/kotlin/KotlinAstTransformer.ts @@ -10,11 +10,15 @@ export default class KotlinAstTransformer extends CSharpAstTransformer { this._testMethodAttribute = 'org.junit.Test'; } - public get extension(): string { - return '.kt' + public override get extension(): string { + return '.kt'; } + public override get targetTag(): string { + return 'kotlin'; + } + private _paramReferences: Map<string, cs.Identifier[]>[] = []; private _paramsWithAssignment: Set<string>[] = []; @@ -22,7 +26,7 @@ export default class KotlinAstTransformer extends CSharpAstTransformer { return 'param' + name; } - protected getIdentifierName(identifier: cs.Identifier, expression: ts.Identifier): string { + protected override getIdentifierName(identifier: cs.Identifier, expression: ts.Identifier): string { const paramName = super.getIdentifierName(identifier, expression); if ( identifier.tsSymbol && @@ -45,7 +49,7 @@ export default class KotlinAstTransformer extends CSharpAstTransformer { return paramName; } - protected visitPrefixUnaryExpression(parent: cs.Node, expression: ts.PrefixUnaryExpression) { + protected override visitPrefixUnaryExpression(parent: cs.Node, expression: ts.PrefixUnaryExpression) { const pre = super.visitPrefixUnaryExpression(parent, expression); if (pre) { switch (pre.operator) { @@ -61,7 +65,7 @@ export default class KotlinAstTransformer extends CSharpAstTransformer { return pre; } - protected visitPostfixUnaryExpression(parent: cs.Node, expression: ts.PostfixUnaryExpression) { + protected override visitPostfixUnaryExpression(parent: cs.Node, expression: ts.PostfixUnaryExpression) { const post = super.visitPostfixUnaryExpression(parent, expression); if (post) { switch (post.operator) { @@ -77,7 +81,7 @@ export default class KotlinAstTransformer extends CSharpAstTransformer { return post; } - protected visitBinaryExpression(parent: cs.Node, expression: ts.BinaryExpression) { + protected override visitBinaryExpression(parent: cs.Node, expression: ts.BinaryExpression) { const bin = super.visitBinaryExpression(parent, expression); // detect parameter assignment if ( @@ -98,7 +102,8 @@ export default class KotlinAstTransformer extends CSharpAstTransformer { // a == this or this == a // within an equals method needs to have the operator === - if (bin && + if ( + bin && cs.isBinaryExpression(bin) && (expression.operatorToken.kind === ts.SyntaxKind.EqualsEqualsEqualsToken || expression.operatorToken.kind === ts.SyntaxKind.EqualsEqualsToken) && @@ -167,7 +172,7 @@ export default class KotlinAstTransformer extends CSharpAstTransformer { block.statements.unshift(...localParams); } - protected visitGetAccessor(parent: cs.ClassDeclaration, classElement: ts.GetAccessorDeclaration) { + protected override visitGetAccessor(parent: cs.ClassDeclaration, classElement: ts.GetAccessorDeclaration) { this._paramReferences.push(new Map<string, cs.Identifier[]>()); this._paramsWithAssignment.push(new Set<string>()); @@ -179,7 +184,7 @@ export default class KotlinAstTransformer extends CSharpAstTransformer { return el; } - protected visitSetAccessor(parent: cs.ClassDeclaration, classElement: ts.SetAccessorDeclaration) { + protected override visitSetAccessor(parent: cs.ClassDeclaration, classElement: ts.SetAccessorDeclaration) { this._paramReferences.push(new Map<string, cs.Identifier[]>()); this._paramsWithAssignment.push(new Set<string>()); @@ -194,7 +199,7 @@ export default class KotlinAstTransformer extends CSharpAstTransformer { return el; } - protected visitConstructorDeclaration(parent: cs.ClassDeclaration, classElement: ts.ConstructorDeclaration) { + protected override visitConstructorDeclaration(parent: cs.ClassDeclaration, classElement: ts.ConstructorDeclaration) { this._paramReferences.push(new Map<string, cs.Identifier[]>()); this._paramsWithAssignment.push(new Set<string>()); @@ -210,7 +215,7 @@ export default class KotlinAstTransformer extends CSharpAstTransformer { return constr; } - protected visitArrowExpression(parent: cs.Node, expression: ts.ArrowFunction) { + protected override visitArrowExpression(parent: cs.Node, expression: ts.ArrowFunction) { this._paramReferences.push(new Map<string, cs.Identifier[]>()); this._paramsWithAssignment.push(new Set<string>()); @@ -222,7 +227,7 @@ export default class KotlinAstTransformer extends CSharpAstTransformer { return func; } - protected visitFunctionExpression(parent: cs.Node, expression: ts.FunctionExpression) { + protected override visitFunctionExpression(parent: cs.Node, expression: ts.FunctionExpression) { this._paramReferences.push(new Map<string, cs.Identifier[]>()); this._paramsWithAssignment.push(new Set<string>()); @@ -234,7 +239,7 @@ export default class KotlinAstTransformer extends CSharpAstTransformer { return func; } - protected visitMethodDeclaration( + protected override visitMethodDeclaration( parent: cs.ClassDeclaration | cs.InterfaceDeclaration, classElement: ts.MethodDeclaration ) { @@ -253,52 +258,22 @@ export default class KotlinAstTransformer extends CSharpAstTransformer { return method; } - protected visitPropertyAccessExpression(parent: cs.Node, expression: ts.PropertyAccessExpression) { + protected override visitPropertyAccessExpression(parent: cs.Node, expression: ts.PropertyAccessExpression) { const base = super.visitPropertyAccessExpression(parent, expression); return base; } - protected getSymbolName(parentSymbol: ts.Symbol, symbol: ts.Symbol, expression: cs.Expression): string | null { + protected override getSymbolName(parentSymbol: ts.Symbol, symbol: ts.Symbol, expression: cs.Expression): string | null { switch (parentSymbol.name) { - case 'Array': - switch (symbol.name) { - case 'length': - // new Array<string>(other.length) - if (expression.parent && - cs.isNewExpression(expression.parent) && - (expression.parent.tsNode as ts.NewExpression).arguments?.length === 1 && - (expression.parent.type as cs.UnresolvedTypeNode).tsType?.symbol - ?.name === 'ArrayConstructor' - ) { - return 'size'; - } - - return 'size.toDouble()'; - case 'push': - return 'add'; - case 'indexOf': - return 'indexOfInDouble'; - case 'filter': - return 'filterBy'; - case 'reverse': - return 'rev'; - case 'fill': - return 'fillWith'; - case 'map': - return 'mapTo'; - } - break; case 'String': switch (symbol.name) { case 'length': if ( - expression.parent && ( - cs.isReturnStatement(expression.parent) || - cs.isVariableDeclaration(expression.parent) || - (cs.isBinaryExpression(expression.parent) && - expression.parent.operator === '=') - ) + expression.parent && + (cs.isReturnStatement(expression.parent) || + cs.isVariableDeclaration(expression.parent) || + (cs.isBinaryExpression(expression.parent) && expression.parent.operator === '=')) ) { return 'length.toDouble()'; } @@ -314,25 +289,21 @@ export default class KotlinAstTransformer extends CSharpAstTransformer { return 'lowercase'; case 'toUpperCase': return 'uppercase'; + case 'split': + return 'splitBy'; + } + break; + case 'Number': + switch (symbol.name) { + case 'toString': + return 'toInvariantString'; } break; } return null; } - private isWithinForInitializer(expression: ts.Node): Boolean { - if (!expression.parent) { - return false; - } - - if (ts.isForStatement(expression.parent) && expression.parent.initializer === expression) { - return true; - } - - return this.isWithinForInitializer(expression.parent!); - } - - protected visitNonNullExpression(parent: cs.Node, expression: ts.NonNullExpression) { + protected override visitNonNullExpression(parent: cs.Node, expression: ts.NonNullExpression) { const nonNullExpression = { expression: {} as cs.Expression, parent: parent, @@ -348,7 +319,7 @@ export default class KotlinAstTransformer extends CSharpAstTransformer { return nonNullExpression; } - protected visitAsExpression(parent: cs.Node, expression: ts.AsExpression): cs.Expression | null { + protected override visitAsExpression(parent: cs.Node, expression: ts.AsExpression): cs.Expression | null { if (this.isCastToEnum(expression)) { const methodAccess = { nodeType: cs.SyntaxKind.MemberAccessExpression, @@ -383,7 +354,7 @@ export default class KotlinAstTransformer extends CSharpAstTransformer { } as cs.MemberAccessExpression; let expr = this.visitExpression(methodAccess, expression.expression); - if(!expr) { + if (!expr) { return null; } methodAccess.expression = expr; @@ -414,4 +385,65 @@ export default class KotlinAstTransformer extends CSharpAstTransformer { let targetType = this._context.typeChecker.getTypeFromTypeNode(expression.type); return targetType.flags & ts.TypeFlags.Enum || targetType.flags & ts.TypeFlags.EnumLiteral; } + + protected override createMapEntry(parent: cs.Node, expression: ts.ArrayLiteralExpression): cs.Expression { + const csExpr = { + parent: parent, + tsNode: expression, + nodeType: cs.SyntaxKind.InvocationExpression, + arguments: [], + expression: {} as cs.Expression + } as cs.InvocationExpression; + + let mapEntryTypeName = 'MapEntry'; + if (expression.elements.length === 2) { + const keyType = this._context.getType(expression.elements[0]); + let keyTypeContainerName = this.getContainerTypeName(keyType); + + const valueType = this._context.getType(expression.elements[1]); + let valueTypeContainerName = this.getContainerTypeName(valueType); + + if (keyTypeContainerName || valueTypeContainerName) { + keyTypeContainerName = keyTypeContainerName || 'Object'; + valueTypeContainerName = valueTypeContainerName || 'Object'; + mapEntryTypeName = keyTypeContainerName + valueTypeContainerName + mapEntryTypeName; + } + } + + csExpr.expression = { + nodeType: cs.SyntaxKind.Identifier, + text: this._context.makeTypeName(`alphaTab.collections.${mapEntryTypeName}`), + parent: csExpr, + tsNode: expression + } as cs.Identifier; + + expression.elements.forEach(e => { + const ex = this.visitExpression(csExpr, e); + if (ex) { + csExpr.arguments.push(ex); + } + }); + + return csExpr; + } + + private getContainerTypeName(tsType: ts.Type): string | null { + if (this._context.isNullableType(tsType)) { + return null; + } + if ( + (tsType.flags & ts.TypeFlags.Enum) !== 0 || + (tsType.flags & ts.TypeFlags.EnumLike) !== 0 || + (tsType.flags & ts.TypeFlags.EnumLiteral) !== 0 + ) { + return null; + } + if ((tsType.flags & ts.TypeFlags.Number) !== 0 || (tsType.flags & ts.TypeFlags.NumberLiteral) !== 0) { + return 'Double'; + } + if ((tsType.flags & ts.TypeFlags.Boolean) !== 0 || (tsType.flags & ts.TypeFlags.BooleanLiteral) !== 0) { + return 'Boolean'; + } + return null; + } } diff --git a/src.compiler/kotlin/KotlinEmitterContext.ts b/src.compiler/kotlin/KotlinEmitterContext.ts index c30d09cf2..2356bc2aa 100644 --- a/src.compiler/kotlin/KotlinEmitterContext.ts +++ b/src.compiler/kotlin/KotlinEmitterContext.ts @@ -8,7 +8,7 @@ export default class KotlinEmitterContext extends CSharpEmitterContext { this.noPascalCase = true; } - protected getClassName(type: cs.NamedTypeDeclaration, expr?: cs.Node) { + protected override getClassName(type: cs.NamedTypeDeclaration, expr?: cs.Node) { let className = super.getClassName(type, expr); // partial member access if ( @@ -22,7 +22,7 @@ export default class KotlinEmitterContext extends CSharpEmitterContext { return className; } - protected toCoreTypeName(s: string) { + protected override toCoreTypeName(s: string) { if(s === 'String') { return 'CoreString'; } @@ -40,10 +40,15 @@ export default class KotlinEmitterContext extends CSharpEmitterContext { return !!ts.getJSDocTags(tsSymbol.valueDeclaration).find(t => t.tagName.text === 'partial'); } - protected isClassElementOverride(classType: ts.Type, classElement: ts.ClassElement) { - if (this.hasAnyBaseTypeClassMember(classType, classElement.name!.getText(), true)) { - return true; - } + + protected override getOverriddenMembers(classType: ts.InterfaceType, classElement: ts.ClassElement): ts.Symbol[] { + const symbols: ts.Symbol[] = []; + this.collectOverriddenMembersByName(symbols, classType, classElement.name!.getText(), false, true); + return symbols; + } + + protected override collectOverriddenMembersByName(symbols: ts.Symbol[], classType: ts.InterfaceType, memberName: string, includeOwnMembers: boolean = false, allowInterfaces: boolean = false) { + super.collectOverriddenMembersByName(symbols, classType, memberName, includeOwnMembers, allowInterfaces); if ( classType.symbol.valueDeclaration && @@ -57,9 +62,7 @@ export default class KotlinEmitterContext extends CSharpEmitterContext { if (implementsClause) { for (const typeSyntax of implementsClause.types) { const type = this.typeChecker.getTypeFromTypeNode(typeSyntax); - if (this.hasClassMember(type, classElement.name!.getText())) { - return true; - } + super.collectOverriddenMembersByName(symbols, type as ts.InterfaceType, memberName, true, allowInterfaces); } } } @@ -67,7 +70,7 @@ export default class KotlinEmitterContext extends CSharpEmitterContext { return false; } - public isValueTypeNotNullSmartCast(expression: ts.Expression): boolean | undefined { + public override isValueTypeNotNullSmartCast(expression: ts.Expression): boolean | undefined { return undefined; } } diff --git a/src.compiler/typescript/CloneEmitter.ts b/src.compiler/typescript/CloneEmitter.ts index 60084332b..01ea069fc 100644 --- a/src.compiler/typescript/CloneEmitter.ts +++ b/src.compiler/typescript/CloneEmitter.ts @@ -91,7 +91,9 @@ function generateClonePropertyStatements( [ts.factory.createVariableDeclaration('i')], ts.NodeFlags.Const ), - ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('original'), propertyName), + ts.factory.createNonNullExpression( + ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('original'), propertyName) + ), ts.factory.createBlock([ ts.factory.createExpressionStatement( collectionAddMethod diff --git a/src.compiler/typescript/Serializer.common.ts b/src.compiler/typescript/Serializer.common.ts new file mode 100644 index 000000000..61733e49f --- /dev/null +++ b/src.compiler/typescript/Serializer.common.ts @@ -0,0 +1,69 @@ +import * as ts from 'typescript'; +import * as path from 'path'; + +export interface JsonProperty { + partialNames: boolean; + property: ts.PropertyDeclaration; + jsonNames: string[]; + target?: string; +} + +export interface JsonSerializable { + isStrict: boolean; + hasToJsonExtension: boolean; + hasSetPropertyExtension: boolean; + properties: JsonProperty[]; +} + + +export function isImmutable(type: ts.Type | null): boolean { + if (!type || !type.symbol) { + return false; + } + + const declaration = type.symbol.valueDeclaration; + if (declaration) { + return !!ts.getJSDocTags(declaration).find(t => t.tagName.text === 'json_immutable'); + } + + return false; +} + +export function createStringUnknownMapNode(): ts.TypeNode { + return ts.factory.createTypeReferenceNode('Map', [ + ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword), + ts.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword) + ]); +} + +function removeExtension(fileName: string) { + return fileName.substring(0, fileName.lastIndexOf('.')); +} + +export function toImportPath(fileName: string) { + return '@' + removeExtension(fileName).split('\\').join('/'); +} + +export function findModule(type: ts.Type, options: ts.CompilerOptions) { + if (type.symbol && type.symbol.declarations) { + for (const decl of type.symbol.declarations) { + const file = decl.getSourceFile(); + if (file) { + const relative = path.relative(path.join(path.resolve(options.baseUrl!)), path.resolve(file.fileName)); + return toImportPath(relative); + } + } + + return './' + type.symbol.name; + } + + return ''; +} + +export function findSerializerModule(type: ts.Type, options: ts.CompilerOptions) { + let module = findModule(type, options); + const importPath = module.split('/'); + importPath.splice(1, 0, 'generated'); + importPath[importPath.length - 1] = type.symbol!.name + 'Serializer'; + return importPath.join('/'); +} diff --git a/src.compiler/typescript/Serializer.fromJson.ts b/src.compiler/typescript/Serializer.fromJson.ts new file mode 100644 index 000000000..f1b5e5299 --- /dev/null +++ b/src.compiler/typescript/Serializer.fromJson.ts @@ -0,0 +1,36 @@ +import * as ts from 'typescript'; +import { addNewLines, createNodeFromSource, setMethodBody } from '../BuilderHelpers'; +import { JsonSerializable } from './Serializer.common'; + +function generateFromJsonBody(serializable: JsonSerializable, importer: (name: string, module: string) => void) { + importer('JsonHelper', '@src/io/JsonHelper'); + return ts.factory.createBlock(addNewLines([ + createNodeFromSource<ts.IfStatement>(`if(!m) { + return; + }`, ts.SyntaxKind.IfStatement), + serializable.isStrict + ? createNodeFromSource<ts.ExpressionStatement>( + `JsonHelper.forEach(m, (v, k) => this.setProperty(obj, k, v));`, + ts.SyntaxKind.ExpressionStatement + ) + : createNodeFromSource<ts.ExpressionStatement>( + `JsonHelper.forEach(m, (v, k) => this.setProperty(obj, k.toLowerCase(), v));`, + ts.SyntaxKind.ExpressionStatement + ) + ])); +} + +export function createFromJsonMethod( + input: ts.ClassDeclaration, + serializable: JsonSerializable, + importer: (name: string, module: string) => void +) { + const methodDecl = createNodeFromSource<ts.MethodDeclaration>( + `public class Serializer { + public static fromJson(obj: ${input.name!.text}, m: unknown): void { + } + }`, + ts.SyntaxKind.MethodDeclaration + ); + return setMethodBody(methodDecl, generateFromJsonBody(serializable, importer)); +} diff --git a/src.compiler/typescript/Serializer.setProperty.ts b/src.compiler/typescript/Serializer.setProperty.ts new file mode 100644 index 000000000..007340da0 --- /dev/null +++ b/src.compiler/typescript/Serializer.setProperty.ts @@ -0,0 +1,570 @@ +import * as ts from 'typescript'; +import { addNewLines, createNodeFromSource, setMethodBody } from '../BuilderHelpers'; +import { isPrimitiveType } from '../BuilderHelpers'; +import { hasFlag } from '../BuilderHelpers'; +import { getTypeWithNullableInfo } from '../BuilderHelpers'; +import { isTypedArray } from '../BuilderHelpers'; +import { unwrapArrayItemType } from '../BuilderHelpers'; +import { isMap } from '../BuilderHelpers'; +import { isEnumType } from '../BuilderHelpers'; +import { isNumberType } from '../BuilderHelpers'; +import { wrapToNonNull } from '../BuilderHelpers'; +import { + createStringUnknownMapNode, + findModule, + findSerializerModule, + isImmutable, + JsonProperty, + JsonSerializable +} from './Serializer.common'; + +function isPrimitiveFromJson(type: ts.Type, typeChecker: ts.TypeChecker) { + if (!type) { + return false; + } + + const isArray = isTypedArray(type); + const arrayItemType = unwrapArrayItemType(type, typeChecker); + + if (hasFlag(type, ts.TypeFlags.Unknown)) { + return true; + } + if (hasFlag(type, ts.TypeFlags.Number)) { + return true; + } + if (hasFlag(type, ts.TypeFlags.String)) { + return true; + } + if (hasFlag(type, ts.TypeFlags.Boolean)) { + return true; + } + + if (arrayItemType) { + if (isArray && hasFlag(arrayItemType, ts.TypeFlags.Number)) { + return true; + } + if (isArray && hasFlag(arrayItemType, ts.TypeFlags.String)) { + return true; + } + if (isArray && hasFlag(arrayItemType, ts.TypeFlags.Boolean)) { + return true; + } + } else if (type.symbol) { + switch (type.symbol.name) { + case 'Uint8Array': + case 'Uint16Array': + case 'Uint32Array': + case 'Int8Array': + case 'Int16Array': + case 'Int32Array': + case 'Float32Array': + case 'Float64Array': + return true; + } + } + + return null; +} + +function cloneTypeNode(node: ts.TypeNode): ts.TypeNode { + if (ts.isUnionTypeNode(node)) { + return ts.factory.createUnionTypeNode(node.types.map(cloneTypeNode)); + } else if ( + node.kind === ts.SyntaxKind.StringKeyword || + node.kind === ts.SyntaxKind.NumberKeyword || + node.kind === ts.SyntaxKind.BooleanKeyword || + node.kind === ts.SyntaxKind.UnknownKeyword || + node.kind === ts.SyntaxKind.AnyKeyword || + node.kind === ts.SyntaxKind.VoidKeyword + ) { + return ts.factory.createKeywordTypeNode(node.kind); + } else if (ts.isLiteralTypeNode(node)) { + return ts.factory.createLiteralTypeNode(node.literal); + } else if (ts.isArrayTypeNode(node)) { + return ts.factory.createArrayTypeNode(cloneTypeNode(node.elementType)); + } + + throw new Error(`Unsupported TypeNode: '${ts.SyntaxKind[node.kind]}' extend type node cloning`); +} + +function generateSetPropertyBody( + program: ts.Program, + serializable: JsonSerializable, + importer: (name: string, module: string) => void +) { + const statements: ts.Statement[] = []; + const cases: ts.CaseOrDefaultClause[] = []; + + const typeChecker = program.getTypeChecker(); + for (const prop of serializable.properties) { + const jsonNames = prop.jsonNames.map(j => j.toLowerCase()); + const caseValues: string[] = jsonNames.filter(j => j !== ''); + const fieldName = (prop.property.name as ts.Identifier).text; + + const caseStatements: ts.Statement[] = []; + + const type = getTypeWithNullableInfo(typeChecker, prop.property.type); + + const assignField = function (expr: ts.Expression): ts.Statement { + return ts.factory.createExpressionStatement( + ts.factory.createAssignment( + ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('obj'), fieldName), + expr + ) + ); + }; + + if (isPrimitiveFromJson(type.type!, typeChecker)) { + caseStatements.push( + assignField( + ts.factory.createAsExpression( + type.isNullable + ? ts.factory.createIdentifier('v') + : ts.factory.createNonNullExpression(ts.factory.createIdentifier('v')), + cloneTypeNode(prop.property.type!) + ) + ) + ); + caseStatements.push(ts.factory.createReturnStatement(ts.factory.createTrue())); + } else if (isEnumType(type.type)) { + importer(type.type.symbol!.name, findModule(type.type, program.getCompilerOptions())); + importer('JsonHelper', '@src/io/JsonHelper'); + if (type.isNullable) { + caseStatements.push( + createNodeFromSource<ts.ExpressionStatement>( + `obj.${fieldName} = JsonHelper.parseEnum<${type.type.symbol.name}>(v, ${type.type.symbol.name});`, + ts.SyntaxKind.ExpressionStatement + ) + ); + } else { + caseStatements.push( + createNodeFromSource<ts.ExpressionStatement>( + `obj.${fieldName} = JsonHelper.parseEnum<${type.type.symbol.name}>(v, ${type.type.symbol.name})!;`, + ts.SyntaxKind.ExpressionStatement + ) + ); + } + caseStatements.push(ts.factory.createReturnStatement(ts.factory.createTrue())); + } else if (isTypedArray(type.type!)) { + const arrayItemType = unwrapArrayItemType(type.type!, typeChecker)!; + const collectionAddMethod = + (ts + .getJSDocTags(prop.property) + .filter(t => t.tagName.text === 'json_add') + .map(t => t.comment ?? '')[0] as string) ?? `${fieldName}.push`; + + // obj.fieldName = []; + // for(const i of value) { + // obj.addFieldName(Type.FromJson(i)); + // } + // or + // for(const __li of value) { + // obj.fieldName.push(Type.FromJson(__li)); + // } + + let itemSerializer = arrayItemType.symbol.name + 'Serializer'; + importer(itemSerializer, findSerializerModule(arrayItemType, program.getCompilerOptions())); + importer(arrayItemType.symbol.name, findModule(arrayItemType, program.getCompilerOptions())); + + const loopItems = [ + createNodeFromSource<ts.ExpressionStatement>( + `obj.${fieldName} = [];`, + ts.SyntaxKind.ExpressionStatement + ), + createNodeFromSource<ts.ForOfStatement>( + `for(const o of (v as (Map<string, unknown> | null)[])) { + const i = new ${arrayItemType.symbol.name}(); + ${itemSerializer}.fromJson(i, o); + obj.${collectionAddMethod}(i) + }`, + ts.SyntaxKind.ForOfStatement + ) + ]; + + if (type.isNullable) { + caseStatements.push( + ts.factory.createIfStatement(ts.factory.createIdentifier('v'), ts.factory.createBlock(loopItems)) + ); + } else { + caseStatements.push(...loopItems); + } + caseStatements.push(ts.factory.createReturnStatement(ts.factory.createTrue())); + } else if (isMap(type.type)) { + const mapType = type.type as ts.TypeReference; + if (!isPrimitiveType(mapType.typeArguments![0])) { + throw new Error('only Map<EnumType, *> maps are supported extend if needed!'); + } + + let mapKey: ts.Expression; + if (isEnumType(mapType.typeArguments![0])) { + importer( + mapType.typeArguments![0].symbol!.name, + findModule(mapType.typeArguments![0], program.getCompilerOptions()) + ); + importer('JsonHelper', '@src/io/JsonHelper'); + mapKey = createNodeFromSource<ts.NonNullExpression>( + `JsonHelper.parseEnum<${mapType.typeArguments![0].symbol!.name}>(k, ${mapType.typeArguments![0].symbol!.name + })!`, + ts.SyntaxKind.NonNullExpression + ); + } else if (isNumberType(mapType.typeArguments![0])) { + mapKey = createNodeFromSource<ts.CallExpression>(`parseInt(k)`, ts.SyntaxKind.CallExpression); + } else { + mapKey = ts.factory.createIdentifier('k'); + } + + let mapValue; + let itemSerializer: string = ''; + if (isPrimitiveFromJson(mapType.typeArguments![1], typeChecker)) { + mapValue = ts.factory.createAsExpression( + ts.factory.createIdentifier('v'), + ts.isTypeReferenceNode(prop.property.type!) && prop.property.type.typeArguments + ? cloneTypeNode(prop.property.type.typeArguments[1]) + : ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword) + ); + } else { + itemSerializer = mapType.typeArguments![1].symbol.name + 'Serializer'; + importer(itemSerializer, findSerializerModule(mapType.typeArguments![1], program.getCompilerOptions())); + importer( + mapType.typeArguments![1]!.symbol.name, + findModule(mapType.typeArguments![1], program.getCompilerOptions()) + ); + mapValue = ts.factory.createIdentifier('i'); + } + + const collectionAddMethod = ts + .getJSDocTags(prop.property) + .filter(t => t.tagName.text === 'json_add') + .map(t => t.comment ?? '')[0] as string || fieldName + '.set'; + + caseStatements.push( + assignField( + ts.factory.createNewExpression( + ts.factory.createIdentifier('Map'), + [ + typeChecker.typeToTypeNode(mapType.typeArguments![0], undefined, undefined)!, + typeChecker.typeToTypeNode(mapType.typeArguments![1], undefined, undefined)! + ], + [] + ) + ) + ); + + caseStatements.push( + ts.factory.createExpressionStatement( + ts.factory.createCallExpression( + ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('JsonHelper'), 'forEach'), + undefined, + [ + ts.factory.createIdentifier('v'), + ts.factory.createArrowFunction( + undefined, + undefined, + [ + ts.factory.createParameterDeclaration(undefined, undefined, undefined, 'v'), + ts.factory.createParameterDeclaration(undefined, undefined, undefined, 'k') + ], + undefined, + ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), + ts.factory.createBlock( + addNewLines( + [ + itemSerializer.length > 0 && + createNodeFromSource<ts.VariableStatement>( + `const i = new ${mapType.typeArguments![1].symbol.name}();`, + ts.SyntaxKind.VariableStatement + ), + itemSerializer.length > 0 && + createNodeFromSource<ts.ExpressionStatement>( + `${itemSerializer}.fromJson(i, v as Map<string, unknown>)`, + ts.SyntaxKind.ExpressionStatement + ), + ts.factory.createExpressionStatement( + ts.factory.createCallExpression( + collectionAddMethod + ? ts.factory.createPropertyAccessExpression( + ts.factory.createIdentifier('obj'), + collectionAddMethod + ) + : ts.factory.createPropertyAccessExpression( + ts.factory.createPropertyAccessExpression( + ts.factory.createIdentifier('obj'), + ts.factory.createIdentifier(fieldName) + ), + ts.factory.createIdentifier('set') + ), + undefined, + [mapKey, mapValue] + ) + ) + ].filter(s => !!s) as ts.Statement[] + ) + ) + ) + ] + ) + ) + ); + + caseStatements.push(ts.factory.createReturnStatement(ts.factory.createTrue())); + } else if (isImmutable(type.type)) { + let itemSerializer = type.type.symbol.name; + importer(itemSerializer, findModule(type.type, program.getCompilerOptions())); + + // obj.fieldName = TypeName.fromJson(value)! + // return true; + caseStatements.push( + createNodeFromSource<ts.ExpressionStatement>( + `obj.${fieldName} = ${itemSerializer}.fromJson(v)!;`, + ts.SyntaxKind.ExpressionStatement + ) + ); + caseStatements.push( + createNodeFromSource<ts.ReturnStatement>( + `return true;`, + ts.SyntaxKind.ReturnStatement + ) + ); + } else { + // for complex types it is a bit more tricky + // if the property matches exactly, we use fromJson + // if the property starts with the field name, we try to set a sub-property + const jsonNameArray = ts.factory.createArrayLiteralExpression( + jsonNames.map(n => ts.factory.createStringLiteral(n)) + ); + + let itemSerializer = type.type.symbol.name + 'Serializer'; + importer(itemSerializer, findSerializerModule(type.type, program.getCompilerOptions())); + if (type.isNullable) { + importer(type.type.symbol!.name, findModule(type.type, program.getCompilerOptions())); + } + + // TODO if no partial name support, simply generate cases + statements.push( + ts.factory.createIfStatement( + // if(["", "core"].indexOf(property) >= 0) + ts.factory.createBinaryExpression( + ts.factory.createCallExpression( + ts.factory.createPropertyAccessExpression(jsonNameArray, 'indexOf'), + [], + [ts.factory.createIdentifier('property')] + ), + ts.SyntaxKind.GreaterThanEqualsToken, + ts.factory.createNumericLiteral('0') + ), + ts.factory.createBlock( + !type.isNullable + ? [ + ts.factory.createExpressionStatement( + ts.factory.createCallExpression( + // TypeName.fromJson + ts.factory.createPropertyAccessExpression( + ts.factory.createIdentifier(itemSerializer), + 'fromJson' + ), + [], + [ + ts.factory.createPropertyAccessExpression( + ts.factory.createIdentifier('obj'), + fieldName + ), + ts.factory.createAsExpression( + ts.factory.createIdentifier('v'), + createStringUnknownMapNode() + ) + ] + ) + ), + ts.factory.createReturnStatement(ts.factory.createTrue()) + ] + : [ + ts.factory.createIfStatement( + ts.factory.createIdentifier('v'), + ts.factory.createBlock([ + assignField( + ts.factory.createNewExpression( + ts.factory.createIdentifier(type.type.symbol.name), + undefined, + [] + ) + ), + ts.factory.createExpressionStatement( + ts.factory.createCallExpression( + // TypeName.fromJson + ts.factory.createPropertyAccessExpression( + ts.factory.createIdentifier(itemSerializer), + 'fromJson' + ), + [], + [ + ts.factory.createPropertyAccessExpression( + ts.factory.createIdentifier('obj'), + fieldName + ), + ts.factory.createAsExpression( + ts.factory.createIdentifier('v'), + createStringUnknownMapNode() + ) + ] + ) + ) + ]), + ts.factory.createBlock([assignField(ts.factory.createNull())]) + ), + ts.factory.createReturnStatement(ts.factory.createTrue()) + ] + ), + !prop.partialNames + ? undefined + : ts.factory.createBlock([ + // for(const candidate of ["", "core"]) { + // if(candidate.indexOf(property) === 0) { + // if(!this.field) { this.field = new FieldType(); } + // if(this.field.setProperty(property.substring(candidate.length), value)) return true; + // } + // } + ts.factory.createForOfStatement( + undefined, + ts.factory.createVariableDeclarationList( + [ts.factory.createVariableDeclaration('c')], + ts.NodeFlags.Const + ), + jsonNameArray, + ts.factory.createBlock([ + ts.factory.createIfStatement( + ts.factory.createBinaryExpression( + ts.factory.createCallExpression( + ts.factory.createPropertyAccessExpression( + ts.factory.createIdentifier('property'), + 'indexOf' + ), + [], + [ts.factory.createIdentifier('c')] + ), + ts.SyntaxKind.EqualsEqualsEqualsToken, + ts.factory.createNumericLiteral('0') + ), + ts.factory.createBlock( + [ + type.isNullable && + ts.factory.createIfStatement( + ts.factory.createPrefixUnaryExpression( + ts.SyntaxKind.ExclamationToken, + ts.factory.createPropertyAccessExpression( + ts.factory.createIdentifier('obj'), + fieldName + ) + ), + ts.factory.createBlock([ + assignField( + ts.factory.createNewExpression( + ts.factory.createIdentifier( + type.type!.symbol!.name + ), + [], + [] + ) + ) + ]) + ), + ts.factory.createIfStatement( + ts.factory.createCallExpression( + ts.factory.createPropertyAccessExpression( + ts.factory.createIdentifier(itemSerializer), + 'setProperty' + ), + [], + [ + ts.factory.createPropertyAccessExpression( + ts.factory.createIdentifier('obj'), + fieldName + ), + ts.factory.createCallExpression( + ts.factory.createPropertyAccessExpression( + ts.factory.createIdentifier('property'), + 'substring' + ), + [], + [ + ts.factory.createPropertyAccessExpression( + ts.factory.createIdentifier('c'), + 'length' + ) + ] + ), + ts.factory.createIdentifier('v') + ] + ), + ts.factory.createBlock([ + ts.factory.createReturnStatement(ts.factory.createTrue()) + ]) + ) + ].filter(s => !!s) as ts.Statement[] + ) + ) + ]) + ) + ]) + ) + ); + } + + if (caseStatements.length > 0) { + for (let i = 0; i < caseValues.length; i++) { + let caseClause = ts.factory.createCaseClause( + ts.factory.createStringLiteral(caseValues[i]), + // last case gets the statements, others are fall through + i < caseValues.length - 1 ? [] : caseStatements + ); + if (prop.target && i === 0) { + caseClause = ts.addSyntheticLeadingComment( + caseClause, + ts.SyntaxKind.MultiLineCommentTrivia, + `@target ${prop.target}`, + true + ); + } + cases.push(caseClause); + } + } + } + + if (cases.length > 0) { + const switchExpr = ts.factory.createSwitchStatement( + ts.factory.createIdentifier('property'), + ts.factory.createCaseBlock(cases) + ); + statements.unshift(switchExpr); + } + + if(serializable.hasSetPropertyExtension) { + statements.push(ts.factory.createReturnStatement( + createNodeFromSource<ts.CallExpression>( + `obj.setProperty(property, v);`, + ts.SyntaxKind.CallExpression + ) + )); + } + else { + statements.push(ts.factory.createReturnStatement(ts.factory.createFalse())); + } + + + return ts.factory.createBlock(addNewLines(statements)); +} + +export function createSetPropertyMethod( + program: ts.Program, + input: ts.ClassDeclaration, + serializable: JsonSerializable, + importer: (name: string, module: string) => void +) { + const methodDecl = createNodeFromSource<ts.MethodDeclaration>( + `public class Serializer { + public static setProperty(obj: ${input.name!.text}, property: string, v: unknown): boolean { + } + }`, + ts.SyntaxKind.MethodDeclaration + ); + return setMethodBody(methodDecl, generateSetPropertyBody(program, serializable, importer)); +} diff --git a/src.compiler/typescript/Serializer.toJson.ts b/src.compiler/typescript/Serializer.toJson.ts new file mode 100644 index 000000000..40a4757e9 --- /dev/null +++ b/src.compiler/typescript/Serializer.toJson.ts @@ -0,0 +1,270 @@ +import * as ts from 'typescript'; +import { + addNewLines, + createNodeFromSource, + setMethodBody, + isPrimitiveType, + hasFlag, + getTypeWithNullableInfo, + isTypedArray, + unwrapArrayItemType, + isMap, + isEnumType +} from '../BuilderHelpers'; +import { findModule, findSerializerModule, isImmutable, JsonProperty, JsonSerializable } from './Serializer.common'; + +function isPrimitiveToJson(type: ts.Type, typeChecker: ts.TypeChecker) { + if (!type) { + return false; + } + + const isArray = isTypedArray(type); + const arrayItemType = unwrapArrayItemType(type, typeChecker); + + if (hasFlag(type, ts.TypeFlags.Unknown)) { + return true; + } + if (hasFlag(type, ts.TypeFlags.Number)) { + return true; + } + if (hasFlag(type, ts.TypeFlags.String)) { + return true; + } + if (hasFlag(type, ts.TypeFlags.Boolean)) { + return true; + } + + if (arrayItemType) { + if (isArray && hasFlag(arrayItemType, ts.TypeFlags.Number)) { + return true; + } + if (isArray && hasFlag(arrayItemType, ts.TypeFlags.String)) { + return true; + } + if (isArray && hasFlag(arrayItemType, ts.TypeFlags.Boolean)) { + return true; + } + } else if (type.symbol) { + switch (type.symbol.name) { + case 'Uint8Array': + case 'Uint16Array': + case 'Uint32Array': + case 'Int8Array': + case 'Int16Array': + case 'Int32Array': + case 'Float32Array': + case 'Float64Array': + return true; + } + } + + return false; +} + +function generateToJsonBody( + program: ts.Program, + serializable: JsonSerializable, + importer: (name: string, module: string) => void +) { + const statements: ts.Statement[] = []; + + statements.push( + createNodeFromSource<ts.IfStatement>( + ` + if(!obj) { + return null; + } + `, + ts.SyntaxKind.IfStatement + ) + ); + + statements.push( + createNodeFromSource<ts.VariableStatement>( + ` + const o = new Map<string, unknown>(); + `, + ts.SyntaxKind.VariableStatement + ) + ); + + for (let prop of serializable.properties) { + const fieldName = (prop.property.name as ts.Identifier).text; + const jsonName = prop.jsonNames.filter(n => n !== '')[0]; + + if (!jsonName) { + continue; + } + const typeChecker = program.getTypeChecker(); + const type = getTypeWithNullableInfo(typeChecker, prop.property.type!); + const isArray = isTypedArray(type.type!); + + let propertyStatements: ts.Statement[] = []; + + if (isPrimitiveToJson(type.type!, typeChecker)) { + propertyStatements.push( + createNodeFromSource<ts.ExpressionStatement>( + ` + o.set(${JSON.stringify(jsonName)}, obj.${fieldName}); + `, + ts.SyntaxKind.ExpressionStatement + ) + ); + } else if (isEnumType(type.type!)) { + if (type.isNullable) { + propertyStatements.push( + createNodeFromSource<ts.ExpressionStatement>( + ` + o.set(${JSON.stringify(jsonName)}, obj.${fieldName} as number|null); + `, + ts.SyntaxKind.ExpressionStatement + ) + ); + } else { + propertyStatements.push( + createNodeFromSource<ts.ExpressionStatement>( + ` + o.set(${JSON.stringify(jsonName)}, obj.${fieldName} as number); + `, + ts.SyntaxKind.ExpressionStatement + ) + ); + } + } else if (isArray) { + const arrayItemType = unwrapArrayItemType(type.type!, typeChecker)!; + let itemSerializer = arrayItemType.symbol.name + 'Serializer'; + importer(itemSerializer, findSerializerModule(arrayItemType, program.getCompilerOptions())); + if (type.isNullable) { + propertyStatements.push( + createNodeFromSource<ts.IfStatement>( + `if(obj.${fieldName} !== null) { + o.set(${JSON.stringify(jsonName)}, obj.${fieldName}?.map(i => ${itemSerializer}.toJson(i))); + }`, + ts.SyntaxKind.IfStatement + ) + ); + } else { + propertyStatements.push( + createNodeFromSource<ts.ExpressionStatement>( + ` + o.set(${JSON.stringify(jsonName)}, obj.${fieldName}.map(i => ${itemSerializer}.toJson(i))); + `, + ts.SyntaxKind.ExpressionStatement + ) + ); + } + + } else if (isMap(type.type)) { + const mapType = type.type as ts.TypeReference; + if (!isPrimitiveType(mapType.typeArguments![0])) { + throw new Error('only Map<Primitive, *> maps are supported extend if needed!'); + } + + let serializeBlock: ts.Block; + if (isPrimitiveToJson(mapType.typeArguments![1], typeChecker)) { + serializeBlock = createNodeFromSource<ts.Block>( + `{ + const m = new Map<string, unknown>(); + o.set(${JSON.stringify(jsonName)}, m); + for(const [k, v] of obj.${fieldName}!) { + m.set(k.toString(), v); + } + }`, ts.SyntaxKind.Block); + } else if (isEnumType(mapType.typeArguments![1])) { + serializeBlock = createNodeFromSource<ts.Block>( + `{ + const m = new Map<string, unknown>(); + o.set(${JSON.stringify(jsonName)}, m); + for(const [k, v] of obj.${fieldName}!) { + m.set(k.toString(), v as number); + } + }`, ts.SyntaxKind.Block); + } else { + const itemSerializer = mapType.typeArguments![1].symbol.name + 'Serializer'; + importer(itemSerializer, findSerializerModule(mapType.typeArguments![1], program.getCompilerOptions())); + + serializeBlock = createNodeFromSource<ts.Block>( + `{ + const m = new Map<string, unknown>(); + o.set(${JSON.stringify(jsonName)}, m); + for(const [k, v] of obj.${fieldName}!) { + m.set(k.toString(), ${itemSerializer}.toJson(v)); + } + }`, ts.SyntaxKind.Block); + } + + if (type.isNullable) { + propertyStatements.push(ts.factory.createIfStatement( + ts.factory.createBinaryExpression( + ts.factory.createPropertyAccessExpression( + ts.factory.createIdentifier('obj'), + fieldName + ), + ts.SyntaxKind.ExclamationEqualsEqualsToken, + ts.factory.createNull() + ), + serializeBlock) + ); + } else { + propertyStatements.push(serializeBlock); + } + } else if (isImmutable(type.type)) { + let itemSerializer = type.type.symbol.name; + importer(itemSerializer, findModule(type.type, program.getCompilerOptions())); + propertyStatements.push( + createNodeFromSource<ts.ExpressionStatement>( + ` + o.set(${JSON.stringify(jsonName)}, ${itemSerializer}.toJson(obj.${fieldName})); + `, + ts.SyntaxKind.ExpressionStatement + ) + ); + } else { + let itemSerializer = type.type.symbol.name + 'Serializer'; + importer(itemSerializer, findSerializerModule(type.type, program.getCompilerOptions())); + propertyStatements.push( + createNodeFromSource<ts.ExpressionStatement>( + ` + o.set(${JSON.stringify(jsonName)}, ${itemSerializer}.toJson(obj.${fieldName})); + `, + ts.SyntaxKind.ExpressionStatement + ) + ); + } + + if (prop.target) { + propertyStatements = propertyStatements.map(s => + ts.addSyntheticLeadingComment(s, ts.SyntaxKind.MultiLineCommentTrivia, `@target ${prop.target}`, true) + ); + } + + statements.push(...propertyStatements); + } + + if(serializable.hasToJsonExtension) { + statements.push( createNodeFromSource<ts.ExpressionStatement>( + `obj.toJson(o);`, + ts.SyntaxKind.ExpressionStatement + )); + } + + statements.push(ts.factory.createReturnStatement(ts.factory.createIdentifier('o'))); + + return ts.factory.createBlock(addNewLines(statements)); +} + +export function createToJsonMethod( + program: ts.Program, + input: ts.ClassDeclaration, + serializable: JsonSerializable, + importer: (name: string, module: string) => void +) { + const methodDecl = createNodeFromSource<ts.MethodDeclaration>( + `public class Serializer { + public static toJson(obj: ${input.name!.text} | null): Map<string, unknown> | null { + } + }`, + ts.SyntaxKind.MethodDeclaration + ); + return setMethodBody(methodDecl, generateToJsonBody(program, serializable, importer)); +} diff --git a/src.compiler/typescript/SerializerEmitter.ts b/src.compiler/typescript/SerializerEmitter.ts index 48d47c6ec..a3492d89d 100644 --- a/src.compiler/typescript/SerializerEmitter.ts +++ b/src.compiler/typescript/SerializerEmitter.ts @@ -6,1219 +6,11 @@ import * as path from 'path'; import * as ts from 'typescript'; import createEmitter from './EmitterBase'; -import { addNewLines } from '../BuilderHelpers'; -import { isPrimitiveType } from '../BuilderHelpers'; -import { hasFlag } from '../BuilderHelpers'; -import { getTypeWithNullableInfo } from '../BuilderHelpers'; -import { isTypedArray } from '../BuilderHelpers'; -import { unwrapArrayItemType } from '../BuilderHelpers'; -import { isMap } from '../BuilderHelpers'; -import { isEnumType } from '../BuilderHelpers'; -import { isNumberType } from '../BuilderHelpers'; -import { wrapToNonNull } from '../BuilderHelpers'; +import { JsonProperty, JsonSerializable, toImportPath } from './Serializer.common'; +import { createSetPropertyMethod } from './Serializer.setProperty'; +import { createFromJsonMethod } from './Serializer.fromJson'; +import { createToJsonMethod } from './Serializer.toJson'; -interface JsonProperty { - partialNames: boolean; - property: ts.PropertyDeclaration; - jsonNames: string[]; - target?: string; -} - -function isImmutable(type: ts.Type | null): boolean { - if (!type || !type.symbol) { - return false; - } - - const declaration = type.symbol.valueDeclaration; - if (declaration) { - return !!ts.getJSDocTags(declaration).find(t => t.tagName.text === 'json_immutable'); - } - - return false; -} - -function removeExtension(fileName: string) { - return fileName.substring(0, fileName.lastIndexOf('.')); -} - -function toImportPath(fileName: string) { - return '@' + removeExtension(fileName).split('\\').join('/'); -} - -function createStringUnknownMapNode(): ts.TypeNode { - return ts.factory.createTypeReferenceNode('Map', [ - ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword), - ts.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword) - ]); -} - -function findModule(type: ts.Type, options: ts.CompilerOptions) { - if (type.symbol && type.symbol.declarations) { - for (const decl of type.symbol.declarations) { - const file = decl.getSourceFile(); - if (file) { - const relative = path.relative(path.join(path.resolve(options.baseUrl!)), path.resolve(file.fileName)); - return toImportPath(relative); - } - } - - return './' + type.symbol.name; - } - - return ''; -} - -function findSerializerModule(type: ts.Type, options: ts.CompilerOptions) { - let module = findModule(type, options); - const importPath = module.split('/'); - importPath.splice(1, 0, 'generated'); - importPath[importPath.length - 1] = type.symbol!.name + 'Serializer'; - return importPath.join('/'); -} - -// -// fromJson -function generateFromJsonBody(importer: (name: string, module: string) => void) { - importer('JsonHelper', '@src/io/JsonHelper'); - return ts.factory.createBlock( - addNewLines([ - ts.factory.createIfStatement( - ts.factory.createPrefixUnaryExpression( - ts.SyntaxKind.ExclamationToken, - ts.factory.createIdentifier('m') - ), - ts.factory.createBlock([ts.factory.createReturnStatement()]) - ), - ts.factory.createExpressionStatement( - ts.factory.createCallExpression( - ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('JsonHelper'), 'forEach'), - undefined, - [ - ts.factory.createIdentifier('m'), - ts.factory.createArrowFunction( - undefined, - undefined, - [ - ts.factory.createParameterDeclaration(undefined, undefined, undefined, 'v'), - ts.factory.createParameterDeclaration(undefined, undefined, undefined, 'k') - ], - undefined, - ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), - ts.factory.createCallExpression( - ts.factory.createPropertyAccessExpression(ts.factory.createThis(), 'setProperty'), - undefined, - [ - ts.factory.createIdentifier('obj'), - ts.factory.createCallExpression( - ts.factory.createPropertyAccessExpression( - ts.factory.createIdentifier('k'), - 'toLowerCase' - ), - undefined, - [] - ), - ts.factory.createIdentifier('v') - ] - ) - ) - ] - ) - ) - ]) - ); -} - -function createFromJsonMethod(input: ts.ClassDeclaration, importer: (name: string, module: string) => void) { - return ts.factory.createMethodDeclaration( - undefined, - [ - ts.factory.createModifier(ts.SyntaxKind.PublicKeyword), - ts.factory.createModifier(ts.SyntaxKind.StaticKeyword) - ], - undefined, - 'fromJson', - undefined, - undefined, - [ - ts.factory.createParameterDeclaration( - undefined, - undefined, - undefined, - 'obj', - undefined, - ts.factory.createTypeReferenceNode(input.name!.text, undefined) - ), - ts.factory.createParameterDeclaration( - undefined, - undefined, - undefined, - 'm', - undefined, - ts.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword) - ) - ], - ts.factory.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword), - generateFromJsonBody(importer) - ); -} -// -// toJson -function isPrimitiveToJson(type: ts.Type, typeChecker: ts.TypeChecker) { - if (!type) { - return false; - } - - const isArray = isTypedArray(type); - const arrayItemType = unwrapArrayItemType(type, typeChecker); - - if (hasFlag(type, ts.TypeFlags.Unknown)) { - return true; - } - if (hasFlag(type, ts.TypeFlags.Number)) { - return true; - } - if (hasFlag(type, ts.TypeFlags.String)) { - return true; - } - if (hasFlag(type, ts.TypeFlags.Boolean)) { - return 'val'; - } - - if (arrayItemType) { - if (isArray && hasFlag(arrayItemType, ts.TypeFlags.Number)) { - return true; - } - if (isArray && hasFlag(arrayItemType, ts.TypeFlags.String)) { - return true; - } - if (isArray && hasFlag(arrayItemType, ts.TypeFlags.Boolean)) { - return true; - } - } else if (type.symbol) { - switch (type.symbol.name) { - case 'Uint8Array': - case 'Uint16Array': - case 'Uint32Array': - case 'Int8Array': - case 'Int16Array': - case 'Int32Array': - case 'Float32Array': - case 'Float64Array': - return true; - } - } - - return false; -} - -function generateToJsonBody( - program: ts.Program, - propertiesToSerialize: JsonProperty[], - importer: (name: string, module: string) => void -) { - const statements: ts.Statement[] = []; - - statements.push( - ts.factory.createIfStatement( - ts.factory.createPrefixUnaryExpression(ts.SyntaxKind.ExclamationToken, ts.factory.createIdentifier('obj')), - ts.factory.createBlock([ts.factory.createReturnStatement(ts.factory.createNull())]) - ) - ); - - statements.push( - ts.factory.createVariableStatement( - undefined, - ts.factory.createVariableDeclarationList( - [ - ts.factory.createVariableDeclaration( - 'o', - undefined, - undefined, - ts.factory.createNewExpression( - ts.factory.createIdentifier('Map'), - [ - ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword), - ts.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword) - ], - [] - ) - ) - ], - ts.NodeFlags.Const - ) - ) - ); - - for (let prop of propertiesToSerialize) { - const fieldName = (prop.property.name as ts.Identifier).text; - const jsonName = prop.jsonNames.filter(n => n !== '')[0]; - - if (!jsonName) { - continue; - } - const typeChecker = program.getTypeChecker(); - const type = getTypeWithNullableInfo(typeChecker, prop.property.type!); - const isArray = isTypedArray(type.type!); - - let propertyStatements: ts.Statement[] = []; - - if (isPrimitiveToJson(type.type!, typeChecker)) { - propertyStatements.push( - ts.factory.createExpressionStatement( - ts.factory.createCallExpression( - ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('o'), 'set'), - undefined, - [ - ts.factory.createStringLiteral(jsonName), - ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('obj'), fieldName) - ] - ) - ) - ); - } else if (isEnumType(type.type!)) { - propertyStatements.push( - ts.factory.createExpressionStatement( - ts.factory.createCallExpression( - ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('o'), 'set'), - undefined, - [ - ts.factory.createStringLiteral(jsonName), - ts.factory.createAsExpression( - ts.factory.createPropertyAccessExpression( - ts.factory.createIdentifier('obj'), - fieldName - ), - type.isNullable - ? ts.factory.createUnionTypeNode([ - ts.factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword), - ts.factory.createLiteralTypeNode(ts.factory.createNull()) - ]) - : ts.factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword) - ) - ] - ) - ) - ); - } else if (isArray) { - const arrayItemType = unwrapArrayItemType(type.type!, typeChecker)!; - let itemSerializer = arrayItemType.symbol.name + 'Serializer'; - importer(itemSerializer, findSerializerModule(arrayItemType, program.getCompilerOptions())); - - propertyStatements.push( - ts.factory.createExpressionStatement( - ts.factory.createCallExpression( - ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('o'), 'set'), - undefined, - [ - ts.factory.createStringLiteral(jsonName), - ts.factory.createCallExpression( - ts.factory.createPropertyAccessExpression( - ts.factory.createPropertyAccessExpression( - ts.factory.createIdentifier('obj'), - fieldName - ), - 'map' - ), - undefined, - [ - ts.factory.createArrowFunction( - undefined, - undefined, - [ts.factory.createParameterDeclaration(undefined, undefined, undefined, 'i')], - undefined, - ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), - ts.factory.createCallExpression( - ts.factory.createPropertyAccessExpression( - ts.factory.createIdentifier(itemSerializer), - 'toJson' - ), - undefined, - [ts.factory.createIdentifier('i')] - ) - ) - ] - ) - ] - ) - ) - ); - } else if (isMap(type.type)) { - const mapType = type.type as ts.TypeReference; - if (!isPrimitiveType(mapType.typeArguments![0])) { - throw new Error('only Map<Primitive, *> maps are supported extend if needed!'); - } - - let writeValue: ts.Expression; - if (isPrimitiveToJson(mapType.typeArguments![1], typeChecker)) { - writeValue = ts.factory.createIdentifier('v'); - } else if (isEnumType(mapType.typeArguments![1])) { - writeValue = ts.factory.createAsExpression( - ts.factory.createIdentifier('v'), - ts.factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword) - ); - } else { - const itemSerializer = mapType.typeArguments![1].symbol.name + 'Serializer'; - importer(itemSerializer, findSerializerModule(mapType.typeArguments![1], program.getCompilerOptions())); - - writeValue = ts.factory.createCallExpression( - ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(itemSerializer), 'toJson'), - undefined, - [ts.factory.createIdentifier('v')] - ); - } - - propertyStatements.push( - ts.factory.createBlock([ - ts.factory.createVariableStatement( - undefined, - ts.factory.createVariableDeclarationList( - [ - ts.factory.createVariableDeclaration( - 'm', - undefined, - undefined, - ts.factory.createNewExpression( - ts.factory.createIdentifier('Map'), - [ - ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword), - ts.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword) - ], - [] - ) - ) - ], - ts.NodeFlags.Const - ) - ), - ts.factory.createExpressionStatement( - ts.factory.createCallExpression( - ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('o'), 'set'), - undefined, - [ts.factory.createStringLiteral(jsonName), ts.factory.createIdentifier('m')] - ) - ), - - ts.factory.createForOfStatement( - undefined, - ts.factory.createVariableDeclarationList( - [ - ts.factory.createVariableDeclaration( - ts.factory.createArrayBindingPattern([ - ts.factory.createBindingElement(undefined, undefined, 'k'), - ts.factory.createBindingElement(undefined, undefined, 'v') - ]) - ) - ], - ts.NodeFlags.Const - ), - ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('obj'), fieldName), - ts.factory.createBlock([ - ts.factory.createExpressionStatement( - ts.factory.createCallExpression( - ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('m'), 'set'), - undefined, - [ - // todo: key to string - ts.factory.createCallExpression( - ts.factory.createPropertyAccessExpression( - ts.factory.createIdentifier('k'), - 'toString' - ), - undefined, - [] - ), - writeValue - ] - ) - ) - ]) - ) - ]) - ); - } else if (isImmutable(type.type)) { - let itemSerializer = type.type.symbol.name; - importer(itemSerializer, findModule(type.type, program.getCompilerOptions())); - propertyStatements.push( - ts.factory.createExpressionStatement( - ts.factory.createCallExpression( - ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('o'), 'set'), - undefined, - [ - ts.factory.createStringLiteral(jsonName), - ts.factory.createCallExpression( - ts.factory.createPropertyAccessExpression( - ts.factory.createIdentifier(itemSerializer), - 'toJson' - ), - [], - [ - ts.factory.createPropertyAccessExpression( - ts.factory.createIdentifier('obj'), - fieldName - ) - ] - ) - ] - ) - ) - ); - } else { - let itemSerializer = type.type.symbol.name + 'Serializer'; - importer(itemSerializer, findSerializerModule(type.type, program.getCompilerOptions())); - propertyStatements.push( - ts.factory.createExpressionStatement( - ts.factory.createCallExpression( - ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('o'), 'set'), - undefined, - [ - ts.factory.createStringLiteral(jsonName), - ts.factory.createCallExpression( - ts.factory.createPropertyAccessExpression( - ts.factory.createIdentifier(itemSerializer), - 'toJson' - ), - [], - [ - ts.factory.createPropertyAccessExpression( - ts.factory.createIdentifier('obj'), - fieldName - ) - ] - ) - ] - ) - ) - ); - } - - if (prop.target) { - propertyStatements = propertyStatements.map(s => - ts.addSyntheticLeadingComment(s, ts.SyntaxKind.MultiLineCommentTrivia, `@target ${prop.target}`, true) - ); - } - - statements.push(...propertyStatements); - } - - statements.push(ts.factory.createReturnStatement(ts.factory.createIdentifier('o'))); - - return ts.factory.createBlock(addNewLines(statements)); -} - -function createToJsonMethod( - program: ts.Program, - input: ts.ClassDeclaration, - propertiesToSerialize: JsonProperty[], - importer: (name: string, module: string) => void -) { - return ts.factory.createMethodDeclaration( - undefined, - [ - ts.factory.createModifier(ts.SyntaxKind.PublicKeyword), - ts.factory.createModifier(ts.SyntaxKind.StaticKeyword) - ], - undefined, - 'toJson', - undefined, - undefined, - [ - ts.factory.createParameterDeclaration( - undefined, - undefined, - undefined, - 'obj', - undefined, - ts.factory.createUnionTypeNode([ - ts.factory.createTypeReferenceNode(input.name!.text, undefined), - ts.factory.createLiteralTypeNode(ts.factory.createNull()) - ]) - ) - ], - ts.factory.createUnionTypeNode([ - createStringUnknownMapNode(), - ts.factory.createLiteralTypeNode(ts.factory.createNull()) - ]), - generateToJsonBody(program, propertiesToSerialize, importer) - ); -} - -// -// setProperty - -function isPrimitiveFromJson(type: ts.Type, typeChecker: ts.TypeChecker) { - if (!type) { - return false; - } - - const isArray = isTypedArray(type); - const arrayItemType = unwrapArrayItemType(type, typeChecker); - - if (hasFlag(type, ts.TypeFlags.Unknown)) { - return true; - } - if (hasFlag(type, ts.TypeFlags.Number)) { - return true; - } - if (hasFlag(type, ts.TypeFlags.String)) { - return true; - } - if (hasFlag(type, ts.TypeFlags.Boolean)) { - return true; - } - - if (arrayItemType) { - if (isArray && hasFlag(arrayItemType, ts.TypeFlags.Number)) { - return true; - } - if (isArray && hasFlag(arrayItemType, ts.TypeFlags.String)) { - return true; - } - if (isArray && hasFlag(arrayItemType, ts.TypeFlags.Boolean)) { - return true; - } - } else if (type.symbol) { - switch (type.symbol.name) { - case 'Uint8Array': - case 'Uint16Array': - case 'Uint32Array': - case 'Int8Array': - case 'Int16Array': - case 'Int32Array': - case 'Float32Array': - case 'Float64Array': - return true; - } - } - - return null; -} - -function createEnumMapping(type: ts.Type): ts.Expression { - return ts.factory.createCallExpression( - ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('JsonHelper'), 'parseEnum'), - [ts.factory.createTypeReferenceNode(type.symbol.name)], - [ts.factory.createIdentifier('v'), ts.factory.createIdentifier(type.symbol.name)] - ); -} - -function cloneTypeNode(node: ts.TypeNode): ts.TypeNode { - if(ts.isUnionTypeNode(node)) { - return ts.factory.createUnionTypeNode(node.types.map(cloneTypeNode)); - } else if(node.kind === ts.SyntaxKind.StringKeyword - || node.kind === ts.SyntaxKind.NumberKeyword - || node.kind === ts.SyntaxKind.BooleanKeyword - || node.kind === ts.SyntaxKind.UnknownKeyword - || node.kind === ts.SyntaxKind.AnyKeyword - || node.kind === ts.SyntaxKind.VoidKeyword) { - return ts.factory.createKeywordTypeNode(node.kind); - } else if(ts.isLiteralTypeNode(node)) { - return ts.factory.createLiteralTypeNode(node.literal); - } else if(ts.isArrayTypeNode(node)) { - return ts.factory.createArrayTypeNode(cloneTypeNode(node.elementType)); - } - - throw new Error(`Unsupported TypeNode: '${ts.SyntaxKind[node.kind]}' extend type node cloning`); -} - -function generateSetPropertyBody( - program: ts.Program, - propertiesToSerialize: JsonProperty[], - importer: (name: string, module: string) => void -) { - const statements: ts.Statement[] = []; - const cases: ts.CaseOrDefaultClause[] = []; - - const typeChecker = program.getTypeChecker(); - for (const prop of propertiesToSerialize) { - const jsonNames = prop.jsonNames.map(j => j.toLowerCase()); - const caseValues: string[] = jsonNames.filter(j => j !== ''); - const fieldName = (prop.property.name as ts.Identifier).text; - - const caseStatements: ts.Statement[] = []; - - const type = getTypeWithNullableInfo(typeChecker, prop.property.type); - - const assignField = function (expr: ts.Expression): ts.Statement { - return ts.factory.createExpressionStatement( - ts.factory.createAssignment( - ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('obj'), fieldName), - expr - ) - ); - }; - - if (isPrimitiveFromJson(type.type!, typeChecker)) { - caseStatements.push( - assignField( - ts.factory.createAsExpression( - type.isNullable - ? ts.factory.createIdentifier('v') - : ts.factory.createNonNullExpression(ts.factory.createIdentifier('v')), - cloneTypeNode(prop.property.type!) - ) - ) - ); - caseStatements.push(ts.factory.createReturnStatement(ts.factory.createTrue())); - } else if (isEnumType(type.type)) { - // obj.fieldName = enummapping - // return true; - importer(type.type.symbol!.name, findModule(type.type, program.getCompilerOptions())); - importer('JsonHelper', '@src/io/JsonHelper'); - const read = createEnumMapping(type.type); - if (type.isNullable) { - caseStatements.push(assignField(read)); - } else { - caseStatements.push(assignField(ts.factory.createNonNullExpression(read))); - } - caseStatements.push(ts.factory.createReturnStatement(ts.factory.createTrue())); - } else if (isTypedArray(type.type!)) { - const arrayItemType = unwrapArrayItemType(type.type!, typeChecker)!; - const collectionAddMethod = ts - .getJSDocTags(prop.property) - .filter(t => t.tagName.text === 'json_add') - .map(t => t.comment ?? '')[0] as string; - - // obj.fieldName = []; - // for(const i of value) { - // obj.addFieldName(Type.FromJson(i)); - // } - // or - // for(const __li of value) { - // obj.fieldName.push(Type.FromJson(__li)); - // } - - let itemSerializer = arrayItemType.symbol.name + 'Serializer'; - importer(itemSerializer, findSerializerModule(arrayItemType, program.getCompilerOptions())); - importer(arrayItemType.symbol.name, findModule(arrayItemType, program.getCompilerOptions())); - - const loopItems = [ - assignField(ts.factory.createArrayLiteralExpression(undefined)), - ts.factory.createForOfStatement( - undefined, - ts.factory.createVariableDeclarationList( - [ts.factory.createVariableDeclaration('o')], - ts.NodeFlags.Const - ), - ts.factory.createAsExpression( - ts.factory.createIdentifier('v'), - ts.factory.createArrayTypeNode( - ts.factory.createUnionTypeNode([ - createStringUnknownMapNode(), - ts.factory.createLiteralTypeNode(ts.factory.createNull()) - ]) - ) - ), - ts.factory.createBlock( - [ - ts.factory.createVariableStatement( - undefined, - ts.factory.createVariableDeclarationList( - [ - ts.factory.createVariableDeclaration( - 'i', - undefined, - undefined, - ts.factory.createNewExpression( - ts.factory.createIdentifier(arrayItemType.symbol.name), - undefined, - [] - ) - ) - ], - ts.NodeFlags.Const - ) - ), - ts.factory.createCallExpression( - ts.factory.createPropertyAccessExpression( - ts.factory.createIdentifier(itemSerializer), - 'fromJson' - ), - undefined, - [ts.factory.createIdentifier('i'), ts.factory.createIdentifier('o')] - ), - ts.factory.createExpressionStatement( - collectionAddMethod - ? // obj.addFieldName(i) - ts.factory.createCallExpression( - ts.factory.createPropertyAccessExpression( - ts.factory.createIdentifier('obj'), - collectionAddMethod - ), - undefined, - [ts.factory.createIdentifier('i')] - ) - : // obj.fieldName.push(i) - ts.factory.createCallExpression( - ts.factory.createPropertyAccessExpression( - ts.factory.createPropertyAccessExpression( - ts.factory.createIdentifier('obj'), - fieldName - ), - 'push' - ), - undefined, - [ts.factory.createIdentifier('i')] - ) - ) - ].filter(s => !!s) as ts.Statement[] - ) - ) - ]; - - if (type.isNullable) { - caseStatements.push( - ts.factory.createIfStatement(ts.factory.createIdentifier('v'), ts.factory.createBlock(loopItems)) - ); - } else { - caseStatements.push(...loopItems); - } - caseStatements.push(ts.factory.createReturnStatement(ts.factory.createTrue())); - } else if (isMap(type.type)) { - const mapType = type.type as ts.TypeReference; - if (!isPrimitiveType(mapType.typeArguments![0])) { - throw new Error('only Map<EnumType, *> maps are supported extend if needed!'); - } - - let mapKey; - if (isEnumType(mapType.typeArguments![0])) { - importer( - mapType.typeArguments![0].symbol!.name, - findModule(mapType.typeArguments![0], program.getCompilerOptions()) - ); - importer('JsonHelper', '@src/io/JsonHelper'); - mapKey = ts.factory.createNonNullExpression( - ts.factory.createCallExpression( - ts.factory.createPropertyAccessExpression( - ts.factory.createIdentifier('JsonHelper'), - 'parseEnum' - ), - [ts.factory.createTypeReferenceNode(mapType.typeArguments![0].symbol!.name)], - [ - ts.factory.createIdentifier('k'), - ts.factory.createIdentifier(mapType.typeArguments![0].symbol!.name) - ] - ) - ); - } else if (isNumberType(mapType.typeArguments![0])) { - mapKey = ts.factory.createCallExpression(ts.factory.createIdentifier('parseInt'), undefined, [ - ts.factory.createIdentifier('k') - ]); - } else { - mapKey = ts.factory.createIdentifier('k'); - } - - let mapValue; - let itemSerializer: string = ''; - if (isPrimitiveFromJson(mapType.typeArguments![1], typeChecker)) { - // const isNullable = mapType.typeArguments![1].flags & ts.TypeFlags.Union - // && !!(mapType.typeArguments![1] as ts.UnionType).types.find(t => t.flags & ts.TypeFlags.Null); - - mapValue = ts.factory.createAsExpression( - ts.factory.createIdentifier('v'), - ts.isTypeReferenceNode(prop.property.type!) && prop.property.type.typeArguments - ? cloneTypeNode(prop.property.type.typeArguments[1]) - : ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword) - ); - } else { - itemSerializer = mapType.typeArguments![1].symbol.name + 'Serializer'; - importer(itemSerializer, findSerializerModule(mapType.typeArguments![1], program.getCompilerOptions())); - importer( - mapType.typeArguments![1]!.symbol.name, - findModule(mapType.typeArguments![1], program.getCompilerOptions()) - ); - mapValue = ts.factory.createIdentifier('i'); - } - - const collectionAddMethod = ts - .getJSDocTags(prop.property) - .filter(t => t.tagName.text === 'json_add') - .map(t => t.comment ?? '')[0] as string; - - caseStatements.push( - assignField( - ts.factory.createNewExpression( - ts.factory.createIdentifier('Map'), - [ - typeChecker.typeToTypeNode(mapType.typeArguments![0], undefined, undefined)!, - typeChecker.typeToTypeNode(mapType.typeArguments![1], undefined, undefined)! - ], - [] - ) - ) - ); - - caseStatements.push( - ts.factory.createExpressionStatement( - ts.factory.createCallExpression( - ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('JsonHelper'), 'forEach'), - undefined, - [ - ts.factory.createIdentifier('v'), - ts.factory.createArrowFunction( - undefined, - undefined, - [ - ts.factory.createParameterDeclaration(undefined, undefined, undefined, 'v'), - ts.factory.createParameterDeclaration(undefined, undefined, undefined, 'k') - ], - undefined, - ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), - ts.factory.createBlock( - addNewLines( - [ - itemSerializer.length > 0 && - ts.factory.createVariableStatement( - undefined, - ts.factory.createVariableDeclarationList( - [ - ts.factory.createVariableDeclaration( - 'i', - undefined, - undefined, - ts.factory.createNewExpression( - ts.factory.createIdentifier( - mapType.typeArguments![1].symbol.name - ), - undefined, - [] - ) - ) - ], - ts.NodeFlags.Const - ) - ), - itemSerializer.length > 0 && - ts.factory.createExpressionStatement( - ts.factory.createCallExpression( - ts.factory.createPropertyAccessExpression( - ts.factory.createIdentifier(itemSerializer), - 'fromJson' - ), - undefined, - [ - ts.factory.createIdentifier('i'), - ts.factory.createAsExpression( - ts.factory.createIdentifier('v'), - createStringUnknownMapNode() - ) - ] - ) - ), - ts.factory.createExpressionStatement( - ts.factory.createCallExpression( - collectionAddMethod - ? ts.factory.createPropertyAccessExpression( - ts.factory.createIdentifier('obj'), - collectionAddMethod - ) - : ts.factory.createPropertyAccessExpression( - ts.factory.createPropertyAccessExpression( - ts.factory.createIdentifier('obj'), - ts.factory.createIdentifier(fieldName) - ), - ts.factory.createIdentifier('set') - ), - undefined, - [mapKey, mapValue] - ) - ) - ].filter(s => !!s) as ts.Statement[] - ) - ) - ) - ] - ) - ) - ); - - caseStatements.push(ts.factory.createReturnStatement(ts.factory.createTrue())); - } else if (isImmutable(type.type)) { - let itemSerializer = type.type.symbol.name; - importer(itemSerializer, findModule(type.type, program.getCompilerOptions())); - - // obj.fieldName = TypeName.fromJson(value)! - // return true; - caseStatements.push( - assignField( - wrapToNonNull( - type.isNullable, - ts.factory.createCallExpression( - // TypeName.fromJson - ts.factory.createPropertyAccessExpression( - ts.factory.createIdentifier(itemSerializer), - 'fromJson' - ), - [], - [ts.factory.createIdentifier('v')] - ), - ts.factory - ) - ) - ); - caseStatements.push(ts.factory.createReturnStatement(ts.factory.createTrue())); - } else { - // for complex types it is a bit more tricky - // if the property matches exactly, we use fromJson - // if the property starts with the field name, we try to set a sub-property - const jsonNameArray = ts.factory.createArrayLiteralExpression( - jsonNames.map(n => ts.factory.createStringLiteral(n)) - ); - - let itemSerializer = type.type.symbol.name + 'Serializer'; - importer(itemSerializer, findSerializerModule(type.type, program.getCompilerOptions())); - if (type.isNullable) { - importer(type.type.symbol!.name, findModule(type.type, program.getCompilerOptions())); - } - - // TODO if no partial name support, simply generate cases - statements.push( - ts.factory.createIfStatement( - // if(["", "core"].indexOf(property) >= 0) - ts.factory.createBinaryExpression( - ts.factory.createCallExpression( - ts.factory.createPropertyAccessExpression(jsonNameArray, 'indexOf'), - [], - [ts.factory.createIdentifier('property')] - ), - ts.SyntaxKind.GreaterThanEqualsToken, - ts.factory.createNumericLiteral('0') - ), - ts.factory.createBlock( - !type.isNullable - ? [ - ts.factory.createExpressionStatement( - ts.factory.createCallExpression( - // TypeName.fromJson - ts.factory.createPropertyAccessExpression( - ts.factory.createIdentifier(itemSerializer), - 'fromJson' - ), - [], - [ - ts.factory.createPropertyAccessExpression( - ts.factory.createIdentifier('obj'), - fieldName - ), - ts.factory.createAsExpression( - ts.factory.createIdentifier('v'), - createStringUnknownMapNode() - ) - ] - ) - ), - ts.factory.createReturnStatement(ts.factory.createTrue()) - ] - : [ - ts.factory.createIfStatement( - ts.factory.createIdentifier('v'), - ts.factory.createBlock([ - assignField( - ts.factory.createNewExpression( - ts.factory.createIdentifier(type.type.symbol.name), - undefined, - [] - ) - ), - ts.factory.createExpressionStatement( - ts.factory.createCallExpression( - // TypeName.fromJson - ts.factory.createPropertyAccessExpression( - ts.factory.createIdentifier(itemSerializer), - 'fromJson' - ), - [], - [ - ts.factory.createPropertyAccessExpression( - ts.factory.createIdentifier('obj'), - fieldName - ), - ts.factory.createAsExpression( - ts.factory.createIdentifier('v'), - createStringUnknownMapNode() - ) - ] - ) - ) - ]), - ts.factory.createBlock([assignField(ts.factory.createNull())]) - ), - ts.factory.createReturnStatement(ts.factory.createTrue()) - ] - ), - !prop.partialNames - ? undefined - : ts.factory.createBlock([ - // for(const candidate of ["", "core"]) { - // if(candidate.indexOf(property) === 0) { - // if(!this.field) { this.field = new FieldType(); } - // if(this.field.setProperty(property.substring(candidate.length), value)) return true; - // } - // } - ts.factory.createForOfStatement( - undefined, - ts.factory.createVariableDeclarationList( - [ts.factory.createVariableDeclaration('c')], - ts.NodeFlags.Const - ), - jsonNameArray, - ts.factory.createBlock([ - ts.factory.createIfStatement( - ts.factory.createBinaryExpression( - ts.factory.createCallExpression( - ts.factory.createPropertyAccessExpression( - ts.factory.createIdentifier('property'), - 'indexOf' - ), - [], - [ts.factory.createIdentifier('c')] - ), - ts.SyntaxKind.EqualsEqualsEqualsToken, - ts.factory.createNumericLiteral('0') - ), - ts.factory.createBlock( - [ - type.isNullable && - ts.factory.createIfStatement( - ts.factory.createPrefixUnaryExpression( - ts.SyntaxKind.ExclamationToken, - ts.factory.createPropertyAccessExpression( - ts.factory.createIdentifier('obj'), - fieldName - ) - ), - ts.factory.createBlock([ - assignField( - ts.factory.createNewExpression( - ts.factory.createIdentifier( - type.type!.symbol!.name - ), - [], - [] - ) - ) - ]) - ), - ts.factory.createIfStatement( - ts.factory.createCallExpression( - ts.factory.createPropertyAccessExpression( - ts.factory.createIdentifier(itemSerializer), - 'setProperty' - ), - [], - [ - ts.factory.createPropertyAccessExpression( - ts.factory.createIdentifier('obj'), - fieldName - ), - ts.factory.createCallExpression( - ts.factory.createPropertyAccessExpression( - ts.factory.createIdentifier('property'), - 'substring' - ), - [], - [ - ts.factory.createPropertyAccessExpression( - ts.factory.createIdentifier('c'), - 'length' - ) - ] - ), - ts.factory.createIdentifier('v') - ] - ), - ts.factory.createBlock([ - ts.factory.createReturnStatement(ts.factory.createTrue()) - ]) - ) - ].filter(s => !!s) as ts.Statement[] - ) - ) - ]) - ) - ]) - ) - ); - } - - if (caseStatements.length > 0) { - for (let i = 0; i < caseValues.length; i++) { - let caseClause = ts.factory.createCaseClause( - ts.factory.createStringLiteral(caseValues[i]), - // last case gets the statements, others are fall through - i < caseValues.length - 1 ? [] : caseStatements - ); - if (prop.target && i === 0) { - caseClause = ts.addSyntheticLeadingComment( - caseClause, - ts.SyntaxKind.MultiLineCommentTrivia, - `@target ${prop.target}`, - true - ); - } - cases.push(caseClause); - } - } - } - - if (cases.length > 0) { - const switchExpr = ts.factory.createSwitchStatement( - ts.factory.createIdentifier('property'), - ts.factory.createCaseBlock(cases) - ); - statements.unshift(switchExpr); - } - - statements.push(ts.factory.createReturnStatement(ts.factory.createFalse())); - - return ts.factory.createBlock(addNewLines(statements)); -} - -function createSetPropertyMethod( - program: ts.Program, - input: ts.ClassDeclaration, - propertiesToSerialize: JsonProperty[], - importer: (name: string, module: string) => void -) { - return ts.factory.createMethodDeclaration( - undefined, - [ - ts.factory.createModifier(ts.SyntaxKind.PublicKeyword), - ts.factory.createModifier(ts.SyntaxKind.StaticKeyword) - ], - undefined, - 'setProperty', - undefined, - undefined, - [ - ts.factory.createParameterDeclaration( - undefined, - undefined, - undefined, - 'obj', - undefined, - ts.factory.createTypeReferenceNode(input.name!.text, undefined) - ), - ts.factory.createParameterDeclaration( - undefined, - undefined, - undefined, - 'property', - undefined, - ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword) - ), - ts.factory.createParameterDeclaration( - undefined, - undefined, - undefined, - 'v', - undefined, - ts.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword) - ) - ], - ts.factory.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword), - generateSetPropertyBody(program, propertiesToSerialize, importer) - ); -} export default createEmitter('json', (program, input) => { console.log(`Writing Serializer for ${input.name!.text}`); @@ -1227,7 +19,13 @@ export default createEmitter('json', (program, input) => { path.resolve(input.getSourceFile().fileName) ); - let propertiesToSerialize: JsonProperty[] = []; + const serializable: JsonSerializable = { + properties: [], + isStrict: !!ts.getJSDocTags(input).find(t => t.tagName.text === 'json_strict'), + hasToJsonExtension: false, + hasSetPropertyExtension: false + }; + input.members.forEach(member => { if (ts.isPropertyDeclaration(member)) { const propertyDeclaration = member as ts.PropertyDeclaration; @@ -1236,14 +34,14 @@ export default createEmitter('json', (program, input) => { m => m.kind === ts.SyntaxKind.StaticKeyword || m.kind === ts.SyntaxKind.PrivateKeyword ) ) { - const jsonNames = [(member.name as ts.Identifier).text]; + const jsonNames = [(member.name as ts.Identifier).text.toLowerCase()]; if (ts.getJSDocTags(member).find(t => t.tagName.text === 'json_on_parent')) { jsonNames.push(''); } if (!ts.getJSDocTags(member).find(t => t.tagName.text === 'json_ignore')) { - propertiesToSerialize.push({ + serializable.properties.push({ property: propertyDeclaration, jsonNames: jsonNames, partialNames: !!ts.getJSDocTags(member).find(t => t.tagName.text === 'json_partial_names'), @@ -1252,6 +50,16 @@ export default createEmitter('json', (program, input) => { } } } + else if (ts.isMethodDeclaration(member)) { + switch ((member.name as ts.Identifier).text) { + case 'toJson': + serializable.hasToJsonExtension = true; + break; + case 'setProperty': + serializable.hasSetPropertyExtension = true; + break; + } + } }); const statements: ts.Statement[] = []; @@ -1286,9 +94,9 @@ export default createEmitter('json', (program, input) => { undefined, undefined, [ - createFromJsonMethod(input, importer), - createToJsonMethod(program, input, propertiesToSerialize, importer), - createSetPropertyMethod(program, input, propertiesToSerialize, importer) + createFromJsonMethod(input, serializable, importer), + createToJsonMethod(program, input, serializable, importer), + createSetPropertyMethod(program, input, serializable, importer) ] ) ); diff --git a/src.csharp/AlphaTab.Test/Model/ComparisonHelpers.cs b/src.csharp/AlphaTab.Test/Model/ComparisonHelpers.cs new file mode 100644 index 000000000..b27a4fd39 --- /dev/null +++ b/src.csharp/AlphaTab.Test/Model/ComparisonHelpers.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; +using AlphaTab.Collections; +using AlphaTab.Test; + +namespace AlphaTab.Model +{ + internal partial class ComparisonHelpers + { + private static bool CompareObjects(object? expected, object? actual, string path, + IList<string> ignoreKeys) + { + Globals.Fail( + $"cannot compare unknown object types expected[{actual?.GetType().FullName}] expected[${expected?.GetType().FullName}]"); + return false; + } + } +} diff --git a/src.csharp/AlphaTab.Test/VisualTests/VisualTestHelper.cs b/src.csharp/AlphaTab.Test/VisualTests/VisualTestHelper.cs index 03ee71c3a..2e97f59a1 100644 --- a/src.csharp/AlphaTab.Test/VisualTests/VisualTestHelper.cs +++ b/src.csharp/AlphaTab.Test/VisualTests/VisualTestHelper.cs @@ -61,7 +61,7 @@ public static async Task RunVisualTestScore(Score score, string referenceFileNam IList<double>? tracks = null, string? message = null, double tolerancePercent = 1, bool triggerResize = false) { settings ??= new Settings(); - tracks ??= new Core.List<double> {0}; + tracks ??= new AlphaTab.Collections.List<double> {0}; settings.Core.Engine = "skia"; settings.Core.EnableLazyLoading = false; @@ -89,7 +89,7 @@ public static async Task RunVisualTestScore(Score score, string referenceFileNam var referenceFileData = await TestPlatform.LoadFile(referenceFileName); - var result = new AlphaTab.Core.List<RenderFinishedEventArgs>(); + var result = new AlphaTab.Collections.List<RenderFinishedEventArgs>(); var totalWidth = 0.0; var totalHeight = 0.0; var isResizeRender = false; @@ -101,7 +101,7 @@ public static async Task RunVisualTestScore(Score score, string referenceFileNam }; renderer.PreRender.On(isResize => { - result = new AlphaTab.Core.List<RenderFinishedEventArgs>(); + result = new AlphaTab.Collections.List<RenderFinishedEventArgs>(); totalWidth = 0.0; totalHeight = 0.0; }); @@ -177,7 +177,7 @@ private static void LoadFonts() } private static void CompareVisualResult(double totalWidth, double totalHeight, - AlphaTab.Core.List<RenderFinishedEventArgs> result, string referenceFileName, + AlphaTab.Collections.List<RenderFinishedEventArgs> result, string referenceFileName, Uint8Array referenceFileData, string? message, double tolerancePercent = 1) { SKBitmap finalBitmap; diff --git a/src.csharp/AlphaTab/Core/List.cs b/src.csharp/AlphaTab/Collections/List.cs similarity index 91% rename from src.csharp/AlphaTab/Core/List.cs rename to src.csharp/AlphaTab/Collections/List.cs index 7c4c6ff21..d176b2990 100644 --- a/src.csharp/AlphaTab/Core/List.cs +++ b/src.csharp/AlphaTab/Collections/List.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace AlphaTab.Core +namespace AlphaTab.Collections { internal class List<T> : System.Collections.Generic.List<T> { diff --git a/src.csharp/AlphaTab/Collections/Map.cs b/src.csharp/AlphaTab/Collections/Map.cs new file mode 100644 index 000000000..7e8525881 --- /dev/null +++ b/src.csharp/AlphaTab/Collections/Map.cs @@ -0,0 +1,81 @@ +using System.Collections.Generic; +using System.Linq; + +namespace AlphaTab.Collections +{ + public interface IMap + { + double Size { get; } + void Clear(); + } + public interface IMap<TKey, TValue> : IMap, IEnumerable<MapEntry<TKey, TValue>> + where TValue : class? + { + IEnumerable<TKey> Keys(); + IEnumerable<TValue> Values(); + bool Has(TKey key); + TValue Get(TKey key); + void Set(TKey key, TValue value); + void Delete(TKey key); + } + + public class Map<TKey, TValue> : Dictionary<TKey, TValue>, IMap<TKey, TValue> + where TValue : class? + { + public double Size => Count; + IEnumerable<TKey> IMap<TKey, TValue>.Keys() + { + return base.Keys; + } + + IEnumerable<TValue> IMap<TKey, TValue>.Values() + { + return base.Values; + } + + public Map() + { + } + + public Map(IEnumerable<MapEntry<TKey, TValue>> entries) + { + foreach (var entry in entries) + { + this[entry.Key] = entry.Value; + } + } + public Map(IEnumerable<KeyValuePair<TKey, TValue>> entries) + { + foreach (var entry in entries) + { + this[entry.Key] = entry.Value; + } + } + + public bool Has(TKey key) + { + return ContainsKey(key); + } + + public TValue Get(TKey key) + { + return this[key]; + } + + public void Set(TKey key, TValue value) + { + this[key] = value; + } + + IEnumerator<MapEntry<TKey, TValue>> IEnumerable<MapEntry<TKey, TValue>>.GetEnumerator() + { + return ((IEnumerable<KeyValuePair<TKey, TValue>>) this).Select(kvp => + new MapEntry<TKey, TValue>(kvp)).GetEnumerator(); + } + + public void Delete(TKey key) + { + Remove(key); + } + } +} diff --git a/src.csharp/AlphaTab/Core/EcmaScript/MapEntry.cs b/src.csharp/AlphaTab/Collections/MapEntry.cs similarity index 58% rename from src.csharp/AlphaTab/Core/EcmaScript/MapEntry.cs rename to src.csharp/AlphaTab/Collections/MapEntry.cs index b8fdb0719..b20b5ac4f 100644 --- a/src.csharp/AlphaTab/Core/EcmaScript/MapEntry.cs +++ b/src.csharp/AlphaTab/Collections/MapEntry.cs @@ -1,6 +1,8 @@ -namespace AlphaTab.Core.EcmaScript +using System.Collections.Generic; + +namespace AlphaTab.Collections { - public class MapEntry<TKey, TValue> + public struct MapEntry<TKey, TValue> { public TKey Key { get; set; } public TValue Value { get; set; } @@ -11,6 +13,12 @@ public MapEntry(TKey key, TValue value) Value = value; } + public MapEntry(KeyValuePair<TKey, TValue> kvp) + { + Key = kvp.Key; + Value = kvp.Value; + } + public void Deconstruct(out TKey key, out TValue value) { key = Key; diff --git a/src.csharp/AlphaTab/Collections/ValueTypeMap.cs b/src.csharp/AlphaTab/Collections/ValueTypeMap.cs new file mode 100644 index 000000000..167ef74af --- /dev/null +++ b/src.csharp/AlphaTab/Collections/ValueTypeMap.cs @@ -0,0 +1,84 @@ +using System.Collections.Generic; +using System.Linq; + +namespace AlphaTab.Collections +{ + public interface IValueTypeMap<TKey, TValue> : IEnumerable<MapEntry<TKey, TValue>> + where TValue : struct + { + double Size { get; } + IEnumerable<TKey> Keys(); + IEnumerable<TValue> Values(); + bool Has(TKey key); + TValue? Get(TKey key); + void Set(TKey key, TValue value); + void Delete(TKey key); + void Clear(); + } + + public class ValueTypeMap<TKey, TValue> : Dictionary<TKey, TValue>, IValueTypeMap<TKey, TValue> + where TValue : struct + { + public double Size => Count; + IEnumerable<TKey> IValueTypeMap<TKey, TValue>.Keys() + { + return base.Keys; + } + + IEnumerable<TValue> IValueTypeMap<TKey, TValue>.Values() + { + return base.Values; + } + + public ValueTypeMap() + { + } + + public ValueTypeMap(IEnumerable<MapEntry<TKey, TValue>> entries) + { + foreach (var entry in entries) + { + this[entry.Key] = entry.Value; + } + } + + public ValueTypeMap(IEnumerable<KeyValuePair<TKey, TValue>> entries) + { + foreach (var entry in entries) + { + this[entry.Key] = entry.Value; + } + } + + public bool Has(TKey key) + { + return ContainsKey(key); + } + + public TValue? Get(TKey key) + { + if (TryGetValue(key, out var value)) + { + return value; + } + + return null; + } + + public void Set(TKey key, TValue value) + { + this[key] = value; + } + + public void Delete(TKey key) + { + Remove(key); + } + + IEnumerator<MapEntry<TKey, TValue>> IEnumerable<MapEntry<TKey, TValue>>.GetEnumerator() + { + return ((IEnumerable<KeyValuePair<TKey, TValue>>) this).Select(kvp => + new MapEntry<TKey, TValue>(kvp)).GetEnumerator(); + } + } +} diff --git a/src.csharp/AlphaTab/Core/EcmaScript/Map.cs b/src.csharp/AlphaTab/Core/EcmaScript/Map.cs deleted file mode 100644 index fe404c4ec..000000000 --- a/src.csharp/AlphaTab/Core/EcmaScript/Map.cs +++ /dev/null @@ -1,161 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; - -namespace AlphaTab.Core.EcmaScript -{ - public abstract class Map - { - } - - public class Map<TKey, TValue> : Map, IEnumerable<MapEntry<TKey, TValue>>, - IDictionary<TKey, TValue> - where TValue : class? - { - private readonly Dictionary<TKey, TValue> _data; - - public Map() - { - _data = new Dictionary<TKey, TValue>(); - } - - public Map(IEnumerable<MapEntry<TKey, TValue>> entries) - { - _data = entries.ToDictionary(e => e.Key, e => e.Value); - } - - public double Size => _data.Count; - - public TValue Get(TKey key) - { - if (_data.TryGetValue(key, out var value)) - { - return value; - } - -#pragma warning disable 8653 - return default; -#pragma warning restore 8653 - } - - public void Set(TKey key, TValue value) - { - _data[key] = value; - } - - public bool Has(TKey key) - { - return _data.ContainsKey(key); - } - - public void Delete(TKey key) - { - _data.Remove(key); - } - - void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item) - { - ((ICollection<KeyValuePair<TKey, TValue>>) _data).Add(item); - } - - public void Clear() - { - _data.Clear(); - } - - bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item) - { - return ((ICollection<KeyValuePair<TKey, TValue>>) _data).Contains(item); - } - - void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, - int arrayIndex) - { - ((ICollection<KeyValuePair<TKey, TValue>>) _data).CopyTo(array, arrayIndex); - } - - bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item) - { - return ((ICollection<KeyValuePair<TKey, TValue>>) _data).Remove(item); - } - - int ICollection<KeyValuePair<TKey, TValue>>.Count => - ((ICollection<KeyValuePair<TKey, TValue>>) _data).Count; - - bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly => - ((ICollection<KeyValuePair<TKey, TValue>>) _data).IsReadOnly; - - IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>. - GetEnumerator() - { - return ((IEnumerable<KeyValuePair<TKey, TValue>>) _data).GetEnumerator(); - } - - public IEnumerator<MapEntry<TKey, TValue>> GetEnumerator() - { - return _data.Select(d => new MapEntry<TKey, TValue>(d.Key, d.Value)).GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - public void ForEach(Action<TValue> callback) - { - foreach (var kvp in _data) - { - callback(kvp.Value); - } - } - - public void ForEach(Action<TValue, TKey> callback) - { - foreach (var kvp in _data) - { - callback(kvp.Value, kvp.Key); - } - } - - public IEnumerable<TKey> Keys() - { - return _data.Keys; - } - - void IDictionary<TKey, TValue>.Add(TKey key, TValue value) - { - _data.Add(key, value); - } - - bool IDictionary<TKey, TValue>.ContainsKey(TKey key) - { - return _data.ContainsKey(key); - } - - bool IDictionary<TKey, TValue>.Remove(TKey key) - { - return _data.Remove(key); - } - - bool IDictionary<TKey, TValue>.TryGetValue(TKey key, out TValue value) - { - return _data.TryGetValue(key, out value); - } - - TValue IDictionary<TKey, TValue>.this[TKey key] - { - get => _data[key]; - set => _data[key] = value; - } - - ICollection<TKey> IDictionary<TKey, TValue>.Keys => _data.Keys; - - ICollection<TValue> IDictionary<TKey, TValue>.Values => _data.Values; - - public IEnumerable<TValue> Values() - { - return _data.Values; - } - } -} diff --git a/src.csharp/AlphaTab/Core/EcmaScript/ValueTypeMap.cs b/src.csharp/AlphaTab/Core/EcmaScript/ValueTypeMap.cs deleted file mode 100644 index 1f7a8f0b3..000000000 --- a/src.csharp/AlphaTab/Core/EcmaScript/ValueTypeMap.cs +++ /dev/null @@ -1,73 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; - -namespace AlphaTab.Core.EcmaScript -{ - public class ValueTypeMap<TKey, TValue> : IEnumerable<MapEntry<TKey, TValue>> - where TValue : struct - { - private readonly Dictionary<TKey, TValue> _data; - - public ValueTypeMap() - { - _data = new Dictionary<TKey, TValue>(); - } - - public ValueTypeMap(IEnumerable<MapEntry<TKey, TValue>> entries) - { - _data = entries.ToDictionary(e => e.Key, e => e.Value); - } - - public double Size => _data.Count; - - public TValue? Get(TKey key) - { - if (_data.TryGetValue(key, out var value)) - { - return value; - } - - return null; - } - - public void Set(TKey key, TValue value) - { - _data[key] = value; - } - - public bool Has(TKey key) - { - return _data.ContainsKey(key); - } - - public void Delete(TKey key) - { - _data.Remove(key); - } - - public void Clear() - { - _data.Clear(); - } - - public IEnumerator<MapEntry<TKey, TValue>> GetEnumerator() - { - return _data.Select(d => new MapEntry<TKey, TValue>(d.Key, d.Value)).GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - public void ForEach(Action<TValue, TKey> callback) - { - foreach (var kvp in _data) - { - callback(kvp.Value, kvp.Key); - } - } - } -} diff --git a/src.csharp/AlphaTab/Core/TypeHelper.cs b/src.csharp/AlphaTab/Core/TypeHelper.cs index 9bcd973ae..7d5666617 100644 --- a/src.csharp/AlphaTab/Core/TypeHelper.cs +++ b/src.csharp/AlphaTab/Core/TypeHelper.cs @@ -33,7 +33,7 @@ public static IList<T> Splice<T>(this IList<T> data, double start, double delete public static IList<T> Slice<T>(this IList<T> data) { - return new AlphaTab.Core.List<T>(data); + return new AlphaTab.Collections.List<T>(data); } public static void Reverse<T>(this IList<T> data) @@ -251,22 +251,22 @@ public static IList<string> Split(this string s, string separator) return new List<string>(s.Split(new[] {separator}, StringSplitOptions.None)); } - public static MapEntry<double, TValue> CreateMapEntry<TValue>(int key, TValue value) + public static KeyValuePair<double, TValue> CreateMapEntry<TValue>(int key, TValue value) { - return new MapEntry<double, TValue>(key, value); + return new KeyValuePair<double, TValue>(key, value); } - public static MapEntry<TKey, double> CreateMapEntry<TKey>(TKey key, int value) + public static KeyValuePair<TKey, double> CreateMapEntry<TKey>(TKey key, int value) { - return new MapEntry<TKey, double>(key, value); + return new KeyValuePair<TKey, double>(key, value); } - public static MapEntry<TKey, TValue> CreateMapEntry<TKey, TValue>(TKey key, TValue value) + public static KeyValuePair<TKey, TValue> CreateMapEntry<TKey, TValue>(TKey key, TValue value) { - return new MapEntry<TKey, TValue>(key, value); + return new KeyValuePair<TKey, TValue>(key, value); } - public static string ToString(this double num, int radix) + public static string ToInvariantString(this double num, int radix) { if (radix == 16) { @@ -276,6 +276,21 @@ public static string ToString(this double num, int radix) return num.ToString(CultureInfo.InvariantCulture); } + public static string ToInvariantString(this double num) + { + return num.ToString(CultureInfo.InvariantCulture); + } + + public static string ToInvariantString(this int num) + { + return num.ToString(CultureInfo.InvariantCulture); + } + + public static string ToInvariantString(this Enum num) + { + return ((IConvertible)num).ToInt32(CultureInfo.InvariantCulture).ToString(CultureInfo.InvariantCulture); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static RegExp CreateRegex(string pattern, string flags) { @@ -345,5 +360,11 @@ public static string TypeOf(object? actual) return "object"; } } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static IEnumerable<T> SetInitializer<T>(params T[] items) + { + return items; + } } } diff --git a/src.csharp/AlphaTab/Environment.cs b/src.csharp/AlphaTab/Environment.cs index bf8454f65..9f3b5f9bf 100644 --- a/src.csharp/AlphaTab/Environment.cs +++ b/src.csharp/AlphaTab/Environment.cs @@ -1,8 +1,10 @@ using System; +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; -using AlphaTab.Core.EcmaScript; +using AlphaTab.Core; +using AlphaTab.Collections; using AlphaTab.Platform.CSharp; namespace AlphaTab @@ -12,7 +14,7 @@ partial class Environment public const bool SupportsTextDecoder = true; public static void PlatformInit() { - + } @@ -33,7 +35,7 @@ public static Action Throttle(Action action, double delay) }; } - private static void CreatePlatformSpecificRenderEngines(Map<string, RenderEngineFactory> renderEngines) + private static void CreatePlatformSpecificRenderEngines(IMap<string, RenderEngineFactory> renderEngines) { renderEngines.Set( "skia", diff --git a/src.csharp/AlphaTab/Io/TypeConversions.cs b/src.csharp/AlphaTab/Io/TypeConversions.cs new file mode 100644 index 000000000..59cd4e082 --- /dev/null +++ b/src.csharp/AlphaTab/Io/TypeConversions.cs @@ -0,0 +1,30 @@ +namespace AlphaTab.Io +{ + internal static class TypeConversions + { + public static double Int32ToUint16(double v) + { + return (ushort)(int)v; + } + + public static double Int32ToInt16(double v) + { + return (short)(int)v; + } + + public static double Int32ToUint32(double v) + { + return (uint)(int)v; + } + + public static double Uint16ToInt16(double v) + { + return (short)(ushort)(int)v; + } + + public static double Int16ToUint32(double v) + { + return (uint)(short)(int)v; + } + } +} diff --git a/src.csharp/AlphaTab/Platform/CSharp/ManagedThreadScoreRenderer.cs b/src.csharp/AlphaTab/Platform/CSharp/ManagedThreadScoreRenderer.cs index 2d22f178a..d89019bfb 100644 --- a/src.csharp/AlphaTab/Platform/CSharp/ManagedThreadScoreRenderer.cs +++ b/src.csharp/AlphaTab/Platform/CSharp/ManagedThreadScoreRenderer.cs @@ -155,7 +155,7 @@ public void ResizeRender() } } - public void RenderScore(Score score, IList<double> trackIndexes) + public void RenderScore(Score? score, IList<double>? trackIndexes) { if (CheckAccess()) { diff --git a/src.csharp/AlphaTab/Platform/Svg/FontSizes.cs b/src.csharp/AlphaTab/Platform/Svg/FontSizes.cs index 1963d453c..f14645d3f 100644 --- a/src.csharp/AlphaTab/Platform/Svg/FontSizes.cs +++ b/src.csharp/AlphaTab/Platform/Svg/FontSizes.cs @@ -1,4 +1,5 @@ -using AlphaTab.Core.EcmaScript; +using AlphaTab.Core; +using AlphaTab.Core.EcmaScript; namespace AlphaTab.Platform.Svg { diff --git a/src.csharp/AlphaTab/Core/Lazy.cs b/src.csharp/AlphaTab/Util/Lazy.cs similarity index 95% rename from src.csharp/AlphaTab/Core/Lazy.cs rename to src.csharp/AlphaTab/Util/Lazy.cs index c412dc86f..3a1bfff78 100644 --- a/src.csharp/AlphaTab/Core/Lazy.cs +++ b/src.csharp/AlphaTab/Util/Lazy.cs @@ -1,4 +1,4 @@ -namespace AlphaTab.Core +namespace AlphaTab.Util { public class Lazy<T> where T:class? { diff --git a/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/EnvironmentPartials.kt b/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/EnvironmentPartials.kt index 32ab5b5c9..5bbe0a094 100644 --- a/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/EnvironmentPartials.kt +++ b/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/EnvironmentPartials.kt @@ -6,14 +6,14 @@ import kotlin.contracts.ExperimentalContracts @ExperimentalContracts @ExperimentalUnsignedTypes -internal expect fun createPlatformSpecificRenderEngines(engines: alphaTab.core.ecmaScript.Map<String, RenderEngineFactory>) +internal expect fun createPlatformSpecificRenderEngines(engines: alphaTab.collections.Map<String, RenderEngineFactory>) @Suppress("UNUSED_PARAMETER") @kotlin.contracts.ExperimentalContracts @ExperimentalUnsignedTypes class EnvironmentPartials { companion object { - internal fun createPlatformSpecificRenderEngines(engines: alphaTab.core.ecmaScript.Map<String, RenderEngineFactory>) { + internal fun createPlatformSpecificRenderEngines(engines: alphaTab.collections.Map<String, RenderEngineFactory>) { alphaTab.createPlatformSpecificRenderEngines(engines) } diff --git a/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/collections/BooleanList.kt b/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/collections/BooleanList.kt new file mode 100644 index 000000000..c11d3efeb --- /dev/null +++ b/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/collections/BooleanList.kt @@ -0,0 +1,20 @@ +package alphaTab.collections + +public interface IBooleanIterable : Iterable<Boolean> { + public override fun iterator(): BooleanIterator +} + +internal class BooleanList(vararg elements: Boolean) : IBooleanIterable { + private val _data: BooleanArray = elements + + public val length: Double + get() = _data.size.toDouble() + + public operator fun get(index: Int): Boolean { + return _data[index] + } + + public override fun iterator(): BooleanIterator { + return _data.iterator() + } +} diff --git a/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/collections/DoubleBooleanMap.kt b/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/collections/DoubleBooleanMap.kt new file mode 100644 index 000000000..086cb3ffd --- /dev/null +++ b/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/collections/DoubleBooleanMap.kt @@ -0,0 +1,120 @@ +package alphaTab.collections + +internal open class DoubleBooleanMapEntry { + private var _key: Double = 0.0 + public var key: Double + get() = _key + internal set(value) { + _key = value + } + + private var _value: Boolean = false + public var value: Boolean + get() = _value + internal set(value) { + _value = value + } +} + +internal class DoubleBooleanMapEntryInternal : DoubleBooleanMapEntry(), IMapEntryInternal { + public override var hashCode: Int = 0 + public override var next: Int = 0 + + override fun reset() { + key = 0.0 + value = false + } +} + +internal class DoubleBooleanMap : MapBase<DoubleBooleanMapEntry, DoubleBooleanMapEntryInternal>() { + public fun has(key: Double): Boolean { + return findEntryInternal(key, + { entry, k -> entry.key == k }) >= 0 + } + + public fun get(key: Double): Boolean { + val i = findEntryInternal(key, + { entry, k -> entry.key == k }) + if (i >= 0) { + return entries[i].value + } + throw KeyNotFoundException() + } + + public fun set(key: Double, value: Boolean) { + insert(key, value) + } + + private fun insert(key: Double, value: Boolean) { + insertInternal(key, value, + { entry, k -> entry.key = k }, + { entry, v -> entry.value = v }, + { entry, k -> entry.key == k } + ) + } + + public fun delete(key: Double) { + deleteInternal(key.hashCode()) + } + + private var _values: ValueCollection? = null + public fun values(): IBooleanIterable { + _values = _values ?: ValueCollection(this) + return _values!! + } + + private var _keys: KeyCollection? = null + public fun keys(): IDoubleIterable { + _keys = _keys ?: KeyCollection(this) + return _keys!! + } + + override fun createEntries(size: Int): Array<DoubleBooleanMapEntryInternal> { + return Array(size) { + DoubleBooleanMapEntryInternal() + } + } + + override fun createEntries( + size: Int, + old: Array<DoubleBooleanMapEntryInternal> + ): Array<DoubleBooleanMapEntryInternal> { + return Array(size) { + if (it < old.size) old[it] else DoubleBooleanMapEntryInternal() + } + } + + private class ValueCollection(private val map: DoubleBooleanMap) : IBooleanIterable { + override fun iterator(): BooleanIterator { + return ValueIterator(map.iterator()) + } + + private class ValueIterator(private val iterator: Iterator<DoubleBooleanMapEntry>) : + BooleanIterator() { + override fun hasNext(): Boolean { + return iterator.hasNext() + } + + override fun nextBoolean(): Boolean { + return iterator.next().value + } + } + } + + private class KeyCollection(private val map: DoubleBooleanMap) : IDoubleIterable { + override fun iterator(): DoubleIterator { + return KeyIterator(map.iterator()) + } + + private class KeyIterator(private val iterator: Iterator<DoubleBooleanMapEntry>) : + DoubleIterator() { + override fun hasNext(): Boolean { + return iterator.hasNext() + } + + override fun nextDouble(): Double { + return iterator.next().key + } + } + } +} diff --git a/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/collections/DoubleDoubleMap.kt b/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/collections/DoubleDoubleMap.kt new file mode 100644 index 000000000..5f231b134 --- /dev/null +++ b/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/collections/DoubleDoubleMap.kt @@ -0,0 +1,121 @@ +package alphaTab.collections + +public open class DoubleDoubleMapEntry { + private var _key: Double = 0.0 + public var key: Double + get() = _key + internal set(value) { + _key = value + } + + private var _value: Double = 0.0 + public var value: Double + get() = _value + internal set(value) { + _value = value + } +} + +public class DoubleDoubleMapEntryInternal : DoubleDoubleMapEntry(), IMapEntryInternal { + public override var hashCode: Int = 0 + public override var next: Int = 0 + + override fun reset() { + key = 0.0 + value = 0.0 + } +} + +public class DoubleDoubleMap : MapBase<DoubleDoubleMapEntry, DoubleDoubleMapEntryInternal>() { + public fun has(key: Double): Boolean { + return findEntryInternal(key, + { entry, k -> entry.key == k }) >= 0 + } + + public fun get(key: Double): Double { + val i = findEntryInternal(key, + { entry, k -> entry.key == k }) + if (i >= 0) { + return entries[i].value + } + throw KeyNotFoundException() + } + + public fun set(key: Double, value: Double) { + insert(key, value) + } + + private fun insert(key: Double, value: Double) { + insertInternal(key, value, + { entry, k -> entry.key = k }, + { entry, v -> entry.value = v }, + { entry, k -> entry.key == k } + ) + } + + public fun delete(key: Double) { + deleteInternal(key.hashCode()) + } + + private var _values: ValueCollection? = null + public fun values(): IDoubleIterable { + _values = _values ?: ValueCollection(this) + return _values!! + } + + private var _keys: KeyCollection? = null + public fun keys(): IDoubleIterable { + _keys = _keys ?: KeyCollection(this) + return _keys!! + } + + + override fun createEntries(size: Int): Array<DoubleDoubleMapEntryInternal> { + return Array(size) { + DoubleDoubleMapEntryInternal() + } + } + + override fun createEntries( + size: Int, + old: Array<DoubleDoubleMapEntryInternal> + ): Array<DoubleDoubleMapEntryInternal> { + return Array(size) { + if (it < old.size) old[it] else DoubleDoubleMapEntryInternal() + } + } + + private class ValueCollection(private val map: DoubleDoubleMap) : IDoubleIterable { + override fun iterator(): DoubleIterator { + return ValueIterator(map.iterator()) + } + + private class ValueIterator(private val iterator: Iterator<DoubleDoubleMapEntry>) : + DoubleIterator() { + override fun hasNext(): Boolean { + return iterator.hasNext() + } + + override fun nextDouble(): Double { + return iterator.next().value + } + } + } + + private class KeyCollection(private val map: DoubleDoubleMap) : IDoubleIterable { + override fun iterator(): DoubleIterator { + return KeyIterator(map.iterator()) + } + + private class KeyIterator(private val iterator: Iterator<DoubleDoubleMapEntry>) : + DoubleIterator() { + override fun hasNext(): Boolean { + return iterator.hasNext() + } + + override fun nextDouble(): Double { + return iterator.next().key + } + } + } +} diff --git a/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/collections/DoubleList.kt b/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/collections/DoubleList.kt new file mode 100644 index 000000000..0ccc29045 --- /dev/null +++ b/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/collections/DoubleList.kt @@ -0,0 +1,137 @@ +package alphaTab.collections + +import alphaTab.core.toInvariantString + +public interface IDoubleIterable : Iterable<Double> { + override fun iterator(): DoubleIterator +} + +public class DoubleList : IDoubleIterable { + companion object { + private const val DefaultCapacity: Int = 4 + } + + private var _items: DoubleArray + private var _size: Int = 0 + + public val length: Double get() = _size.toDouble() + + public constructor() { + _items = DoubleArray(0) + } + + public constructor(size: Int) { + _items = DoubleArray(size) + _size = size + } + + public constructor(vararg elements: Double) { + _items = elements + _size = elements.size + } + + private constructor(elements: DoubleArray, size: Int) { + _items = elements + _size = size + } + + public operator fun get(index: Int): Double { + return _items[index] + } + + public operator fun set(index: Int, value: Double) { + _items[index] = value + } + + public fun push(item: Double) { + val array = _items + val size = _size + if (size < array.size) { + _size = size + 1 + array[size] = item + } else { + addWithResize(item) + } + } + + private fun addWithResize(item: Double) { + val size = _size + grow(size + 1) + _size = (size + 1) + _items[size] = item + } + + private fun grow(capacity: Int) { + var newCapacity = if (_items.isEmpty()) DefaultCapacity else 2 * _items.size + if (newCapacity < capacity) { + newCapacity = capacity + } + + val newItems = DoubleArray(newCapacity) + if (_size > 0) { + _items.copyInto(newItems, 0, 0, _size) + } + _items = newItems + } + + public fun join(separator: String): String { + return this.map<String> { it.toInvariantString() }.joinToString(separator) + } + + public fun <TOut> map(transform: (v: Double) -> TOut): List<TOut> { + val mapped = List<TOut>() + for (el in this) { + mapped.push(transform(el)) + } + return mapped + } + + public fun map(transform: (v: Double) -> Double): DoubleList { + val mapped = DoubleList(_size) + for (i in 0 until _size) { + mapped[i] = transform(_items[i]) + } + return mapped + } + + public fun reverse(): DoubleList { + _items.reverse(0, _size) + return this + } + + public fun fill(value: Double): DoubleList { + _items.fill(value) + return this + } + + public fun slice(): DoubleList { + val copy = DoubleArray(_size) { + _items[it] + } + return DoubleList(copy, _size) + } + + public fun sort() { + _items.sort(0, _size) + } + + public override fun iterator(): DoubleIterator { + return Iterator(this) + } + + private class Iterator(private val list: DoubleList) : DoubleIterator() { + private var _index = 0 + override fun hasNext(): Boolean { + return _index < list._size + } + + override fun nextDouble(): Double { + if (_index >= list._size) { + throw NoSuchElementException("List has no more elements") + } + val value = list[_index] + _index++ + return value + } + } +} diff --git a/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/collections/DoubleObjectMap.kt b/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/collections/DoubleObjectMap.kt new file mode 100644 index 000000000..e812b634b --- /dev/null +++ b/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/collections/DoubleObjectMap.kt @@ -0,0 +1,151 @@ +package alphaTab.collections + +public open class DoubleObjectMapEntry<TValue> { + private var _key: Double + public var key: Double + get() = _key + internal set(value) { + _key = value + } + + private var _value: TValue + public var value: TValue + get() = _value + internal set(value) { + _value = value + } + + public operator fun component1(): Double { + return _key + } + + public operator fun component2(): TValue { + return _value + } + + public constructor() { + _key = 0.0 + _value = null as TValue + } + + public constructor(key: Double, value: TValue) { + _key = key + _value = value + } +} + +public class DoubleObjectMapEntryInternal<TValue> : DoubleObjectMapEntry<TValue>(), + IMapEntryInternal { + public override var hashCode: Int = 0 + public override var next: Int = 0 + + override fun reset() { + key = 0.0 + value = null as TValue + } +} + +public class DoubleObjectMap<TValue> : + MapBase<DoubleObjectMapEntry<TValue>, DoubleObjectMapEntryInternal<TValue>> { + public constructor() + public constructor(iterable: Iterable<DoubleObjectMapEntry<TValue>>) { + for (it in iterable) { + set(it.key, it.value) + } + } + + public fun has(key: Double): Boolean { + return findEntryInternal(key, + { entry, k -> entry.key == k }) >= 0 + } + + public fun get(key: Double): TValue { + val i = findEntryInternal(key, + { entry, k -> entry.key == k }) + if (i >= 0) { + return entries[i].value + } + throw KeyNotFoundException() + } + + public fun set(key: Double, value: TValue) { + insert(key, value) + } + + private fun insert(key: Double, value: TValue) { + insertInternal( + key, value as Any, + { entry, k -> entry.key = k }, + { entry, v -> entry.value = v as TValue }, + { entry, k -> entry.key == k } + ) + } + + public fun delete(key: Double) { + deleteInternal(key.hashCode()) + } + + private var _values: ValueCollection<TValue>? = null + public fun values(): Iterable<TValue> { + _values = _values ?: ValueCollection(this) + return _values!! + } + + private var _keys: KeyCollection<TValue>? = null + public fun keys(): IDoubleIterable { + _keys = _keys ?: KeyCollection(this) + return _keys!! + } + + override fun createEntries(size: Int): Array<DoubleObjectMapEntryInternal<TValue>> { + return Array(size) { + DoubleObjectMapEntryInternal() + } + } + + override fun createEntries( + size: Int, + old: Array<DoubleObjectMapEntryInternal<TValue>> + ): Array<DoubleObjectMapEntryInternal<TValue>> { + return Array(size) { + if (it < old.size) old[it] else DoubleObjectMapEntryInternal() + } + } + + + private class ValueCollection<TValue>(private val map: DoubleObjectMap<TValue>) : + Iterable<TValue> { + override fun iterator(): Iterator<TValue> { + return ValueIterator(map.iterator()) + } + + private class ValueIterator<TValue>(private val iterator: Iterator<DoubleObjectMapEntry<TValue>>) : + Iterator<TValue> { + override fun hasNext(): Boolean { + return iterator.hasNext() + } + + override fun next(): TValue { + return iterator.next().value + } + } + } + + private class KeyCollection<TValue>(private val map: DoubleObjectMap<TValue>) : + IDoubleIterable { + override fun iterator(): DoubleIterator { + return ValueIterator(map.iterator()) + } + + private class ValueIterator<TValue>(private val iterator: Iterator<DoubleObjectMapEntry<TValue>>) : + DoubleIterator() { + override fun hasNext(): Boolean { + return iterator.hasNext() + } + + override fun nextDouble(): Double { + return iterator.next().key + } + } + } +} diff --git a/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/collections/HashHelpers.kt b/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/collections/HashHelpers.kt new file mode 100644 index 000000000..33a4149a9 --- /dev/null +++ b/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/collections/HashHelpers.kt @@ -0,0 +1,53 @@ +package alphaTab.collections + +import kotlin.math.sqrt + +class HashHelpers { + companion object { + private val _primes: IntArray = intArrayOf( + 3, 7, 11, 17, 23, 29, 37, 47, 59, 71, 89, 107, 131, 163, 197, 239, 293, 353, 431, 521, 631, 761, 919, + 1103, 1327, 1597, 1931, 2333, 2801, 3371, 4049, 4861, 5839, 7013, 8419, 10103, 12143, 14591, + 17519, 21023, 25229, 30293, 36353, 43627, 52361, 62851, 75431, 90523, 108631, 130363, 156437, + 187751, 225307, 270371, 324449, 389357, 467237, 560689, 672827, 807403, 968897, 1162687, 1395263, + 1674319, 2009191, 2411033, 2893249, 3471899, 4166287, 4999559, 5999471, 7199369 + ) + private const val hashPrime = 101 + + public fun expandPrime(oldSize:Int): Int { + val newSize = 2 * oldSize + return getPrime(newSize) + } + + public fun getPrime(min: Int): Int { + for (prime in _primes) { + if (prime >= min) { + return prime + } + } + + //outside of our predefined table. + //compute the hard way. + var i = min or 1 + while (i < Int.MAX_VALUE) { + if (isPrime(i) && (i - 1) % hashPrime != 0) return i + i += 2 + } + return min + } + + private fun isPrime(candidate: Int): Boolean { + if (candidate and 1 != 0) { + val limit = sqrt(candidate.toDouble()).toInt() + var divisor = 3 + while (divisor <= limit) { + if (candidate % divisor == 0) { + return false + } + divisor += 2 + } + return true + } + return candidate == 2 + } + } +} diff --git a/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/collections/KeyNotFoundException.kt b/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/collections/KeyNotFoundException.kt new file mode 100644 index 000000000..38b0cd82a --- /dev/null +++ b/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/collections/KeyNotFoundException.kt @@ -0,0 +1,3 @@ +package alphaTab.collections + +internal class KeyNotFoundException : Exception() diff --git a/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/collections/List.kt b/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/collections/List.kt new file mode 100644 index 000000000..220880f6f --- /dev/null +++ b/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/collections/List.kt @@ -0,0 +1,105 @@ +package alphaTab.collections + +public class List<T> : Iterable<T> { + private val _data: MutableList<T> + + val length: Double + get() = _data.size.toDouble() + + public constructor() { + _data = ArrayList() + } + + public constructor(vararg items: T) { + _data = items.toMutableList() + } + + @Suppress("UNCHECKED_CAST") + public constructor(size: Int) { + _data = ArrayList() + var remaining = size + while (remaining-- > 0) { + _data.add(null as T) + } + } + + public constructor(items: Iterable<T>) { + _data = items.toMutableList() + } + + private constructor(items: MutableList<T>) { + _data = items + } + + public override fun iterator(): Iterator<T> { + return _data.iterator() + } + + public fun push(item: T) { + _data.add(item) + } + + public operator fun get(index: Int): T { + return _data[index] + } + + public operator fun set(index: Int, value: T) { + _data[index] = value + } + + public fun filter(predicate: (T) -> Boolean): List<T> { + return List(_data.filter(predicate)) + } + + public fun indexOf(value: T): Double { + return _data.indexOf(value).toDouble() + } + + public fun pop(): T { + return _data.removeLast() + } + + public fun sort(comparison: (a: T, b: T) -> Double) { + _data.sortWith { a, b -> comparison(a, b).toInt() } + } + + public fun <TOut> map(transform: (v: T) -> TOut): List<TOut> { + return List(_data.map(transform)) + } + + public fun map(transform: (v: T) -> Double): DoubleList { + val mapped = DoubleList(_data.size) + _data.forEachIndexed { index, item -> + mapped[index] = transform(item) + } + return mapped + } + + public fun reverse(): List<T> { + _data.reverse() + return this + } + + public fun slice(): List<T> { + return List(ArrayList(_data)) + } + + public fun slice(start: Double): List<T> { + return List(_data.subList(start.toInt(), _data.size)) + } + + public fun splice(start: Double, deleteCount: Double, vararg newElements: T) { + var actualStart = start.toInt() + if (actualStart < 0) + { + actualStart += _data.size + } + + _data.subList(start.toInt(), (start + deleteCount).toInt()).clear() + _data.addAll(start.toInt(), newElements.toList()) + } + + public fun join(separator: String): String { + return _data.joinToString(separator) + } +} diff --git a/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/collections/Map.kt b/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/collections/Map.kt new file mode 100644 index 000000000..f0559a9a3 --- /dev/null +++ b/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/collections/Map.kt @@ -0,0 +1,55 @@ +package alphaTab.collections + +public data class MapEntry<TKey, TValue>( + public val key:TKey, + public val value:TValue +) + +public class Map<TKey, TValue> { + private val _data: LinkedHashMap<TKey, TValue> + + public constructor() { + _data = LinkedHashMap() + } + + public constructor(entries: Iterable<MapEntry<TKey, TValue>>) { + _data = LinkedHashMap() + _data.putAll(entries.map { Pair(it.key, it.value) }) + } + + public val size: Double + get() = _data.size.toDouble() + + public fun has(key: TKey): Boolean { + return _data.containsKey(key) + } + + public fun get(key: TKey): TValue { + @Suppress("UNCHECKED_CAST") + return _data[key] as TValue + } + + public fun set(key: TKey, value: TValue) { + _data[key] = value + } + + public fun delete(key: TKey) { + _data.remove(key) + } + + public fun values(): Iterable<TValue> { + return _data.values + } + + public fun keys(): Iterable<TKey> { + return _data.keys + } + + public fun clear() { + _data.clear() + } + + public operator fun iterator(): Iterator<MapEntry<TKey, TValue>> { + return _data.map { MapEntry(it.key, it.value) }.iterator() + } +} diff --git a/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/collections/MapBase.kt b/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/collections/MapBase.kt new file mode 100644 index 000000000..837b766f9 --- /dev/null +++ b/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/collections/MapBase.kt @@ -0,0 +1,197 @@ +package alphaTab.collections + +public interface IMapEntryInternal { + public fun reset() + public var hashCode: Int + public var next: Int +} + +internal inline fun + <reified TEntryType, reified TInternalEntryType : IMapEntryInternal, reified TKey> + MapBase<TEntryType, TInternalEntryType>.findEntryInternal( + key: TKey, + keyEquals: (entry: TInternalEntryType, key: TKey) -> Boolean +): Int { + val buckets = _buckets + val entries = this.entries + if (buckets != null) { + val hashCode = key.hashCode() and 0x7FFFFFFF + var i = buckets[hashCode % buckets.size] + while (i >= 0) { + if (entries[i].hashCode == hashCode && keyEquals(entries[i], key)) { + return i + } + + i = entries[i].next + } + } + return -1 +} + +internal inline fun + <reified TEntryType, reified TInternalEntryType : IMapEntryInternal, reified TKey, reified TValue> + MapBase<TEntryType, TInternalEntryType>.insertInternal( + key: TKey, + value: TValue, + setKey: (entry: TInternalEntryType, key: TKey) -> Unit, + setValue: (entry: TInternalEntryType, value: TValue) -> Unit, + keyEquals: (entry: TInternalEntryType, key: TKey) -> Boolean +) { + var buckets = _buckets + if (buckets == null) { + buckets = initialize(0) + } + + val hashCode = key.hashCode() and 0x7FFFFFFF + var targetBucket = hashCode % buckets.size + + var i = buckets[targetBucket] + while (i >= 0) { + if (entries[i].hashCode == hashCode && keyEquals(entries[i], key)) { + setValue(entries[i], value) + return + } + + i = entries[i].next + } + + val index: Int + if (_freeCount > 0) { + index = _freeList + _freeList = entries[index].next + _freeCount-- + } else { + if (_count == entries.size) { + buckets = resize() + targetBucket = hashCode % buckets.size + } + index = _count + _count++ + } + + val entry = entries[index] + entry.hashCode = hashCode + entry.next = buckets[targetBucket] + setKey(entry, key) + setValue(entry, value) + buckets[targetBucket] = index +} + + +public abstract class MapBase<TEntryType, TInternalEntryType : IMapEntryInternal> : + Iterable<TEntryType> { + internal var _count: Int = 0 + internal var _freeCount: Int = 0 + internal var _buckets: IntArray? = null + internal var _freeList: Int = 0 + + internal var entries: Array<TInternalEntryType> = createEntries(0) + internal fun initialize(capacity: Int): IntArray { + val size = HashHelpers.getPrime(capacity) + _buckets = IntArray(size) { + -1 + } + entries = createEntries(size) + _freeList = -1 + return _buckets!! + } + + internal abstract fun createEntries(size: Int): Array<TInternalEntryType> + internal abstract fun createEntries( + size: Int, + old: Array<TInternalEntryType> + ): Array<TInternalEntryType> + + public val size: Double + get() = (_count - _freeCount).toDouble() + + internal fun resize(): IntArray { + return resize(HashHelpers.expandPrime(_count)) + } + + private fun resize(newSize: Int): IntArray { + val newBuckets = IntArray(newSize) { + -1 + } + val newEntries = createEntries(newSize, entries) + var i = 0 + while (i < _count) { + if (newEntries[i].hashCode >= 0) { + val bucket = newEntries[i].hashCode % newSize + newEntries[i].next = newBuckets[bucket] + newBuckets[bucket] = i + } + i++ + } + _buckets = newBuckets + entries = newEntries + return newBuckets + } + + protected fun deleteInternal(keyHashCode: Int) { + val buckets = _buckets + if (buckets != null) { + val hashCode = keyHashCode and 0x7FFFFFFF + val bucket = hashCode % buckets.size + var last = -1 + var i = buckets[bucket] + while (i >= 0) { + if (entries[i].hashCode == hashCode) { + if (last < 0) { + buckets[bucket] = entries[i].next + } else { + entries[last].next = entries[i].next + } + entries[i].hashCode = -1 + entries[i].next = _freeList + entries[i].reset() + _freeList = i + _freeCount++ + return + } + + last = i + i = entries[i].next + } + } + } + + public fun clear() { + if (_count > 0) { + _buckets?.fill(-1) + entries = createEntries(0) + _freeList = -1 + _count = 0 + _freeCount = 0 + } + } + + override fun iterator(): Iterator<TEntryType> { + return MapIterator(this) + } + + private class MapIterator<TEntryType, TInternalEntryType : IMapEntryInternal> + (private val map: MapBase<TEntryType, TInternalEntryType>) : Iterator<TEntryType> { + private var _entryIndex = 0 + private var _index = 0 + + override fun hasNext(): Boolean { + return _index < map.size + } + + override fun next(): TEntryType { + while (_entryIndex < map._count) { + if (map.entries[_entryIndex].hashCode >= 0) { + val currentValue = map.entries[_entryIndex] as TEntryType + _entryIndex++ + _index++ + return currentValue + } + _entryIndex++ + } + throw NoSuchElementException("Map has no more elements") + } + } + + +} diff --git a/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/collections/ObjectBooleanMap.kt b/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/collections/ObjectBooleanMap.kt new file mode 100644 index 000000000..9b6b21540 --- /dev/null +++ b/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/collections/ObjectBooleanMap.kt @@ -0,0 +1,150 @@ +package alphaTab.collections + +public open class ObjectBooleanMapEntry<TKey> { + private var _key: TKey + public var key: TKey + get() = _key + internal set(value) { + _key = value + } + + private var _value: Boolean + public var value: Boolean + get() = _value + internal set(value) { + _value = value + } + + + public constructor() { + _key = null as TKey + _value = false + } + + public operator fun component1(): TKey { + return _key + } + + public operator fun component2(): Boolean { + return _value + } + + public constructor(key: TKey, value: Boolean) { + _key = key + _value = value + } +} + +public class ObjectBooleanMapEntryInternal<TKey> : ObjectBooleanMapEntry<TKey>(), + IMapEntryInternal { + public override var hashCode: Int = 0 + public override var next: Int = 0 + + override fun reset() { + key = null as TKey + value = false + } +} + +public class ObjectBooleanMap<TKey> : + MapBase<ObjectBooleanMapEntry<TKey>, ObjectBooleanMapEntryInternal<TKey>> { + public constructor() + public constructor(iterable: Iterable<ObjectBooleanMapEntry<TKey>>) { + for (it in iterable) { + set(it.key, it.value) + } + } + + public fun has(key: TKey): Boolean { + return findEntryInternal(key as Any, + { entry, k -> entry.key == (k as TKey) }) >= 0 + } + + public fun get(key: TKey): Boolean { + val i = findEntryInternal(key as Any, + { entry, k -> entry.key == (k as TKey) }) + if (i >= 0) { + return entries[i].value + } + throw KeyNotFoundException() + } + + public fun set(key: TKey, value: Boolean) { + insert(key, value) + } + + private fun insert(key: TKey, value: Boolean) { + insertInternal( + key as Any, value, + { entry, k -> entry.key = k as TKey }, + { entry, v -> entry.value = v }, + { entry, k -> entry.key == (k as TKey) } + ) + } + + public fun delete(key: TKey) { + deleteInternal(key.hashCode()) + } + + private var _values: ValueCollection<TKey>? = null + public fun values(): IBooleanIterable { + _values = _values ?: ValueCollection(this) + return _values!! + } + + private var _keys: KeyCollection<TKey>? = null + public fun keys(): Iterable<TKey> { + _keys = _keys ?: KeyCollection(this) + return _keys!! + } + + override fun createEntries(size: Int): Array<ObjectBooleanMapEntryInternal<TKey>> { + return Array(size) { + ObjectBooleanMapEntryInternal() + } + } + + override fun createEntries( + size: Int, + old: Array<ObjectBooleanMapEntryInternal<TKey>> + ): Array<ObjectBooleanMapEntryInternal<TKey>> { + return Array(size) { + if (it < old.size) old[it] else ObjectBooleanMapEntryInternal() + } + } + + private class ValueCollection<TKey>(private val map: ObjectBooleanMap<TKey>) : + IBooleanIterable { + override fun iterator(): BooleanIterator { + return ValueIterator(map.iterator()) + } + + private class ValueIterator<TKey>(private val iterator: Iterator<ObjectBooleanMapEntry<TKey>>) : + BooleanIterator() { + override fun hasNext(): Boolean { + return iterator.hasNext() + } + + override fun nextBoolean(): Boolean { + return iterator.next().value + } + } + } + + + private class KeyCollection<TKey>(private val map: ObjectBooleanMap<TKey>) : Iterable<TKey> { + override fun iterator(): Iterator<TKey> { + return KeyIterator(map.iterator()) + } + + private class KeyIterator<TKey>(private val iterator: Iterator<ObjectBooleanMapEntry<TKey>>) : Iterator<TKey> { + override fun hasNext(): Boolean { + return iterator.hasNext() + } + + override fun next(): TKey { + return iterator.next().key + } + } + } +} diff --git a/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/collections/ObjectDoubleMap.kt b/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/collections/ObjectDoubleMap.kt new file mode 100644 index 000000000..af420d3cf --- /dev/null +++ b/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/collections/ObjectDoubleMap.kt @@ -0,0 +1,142 @@ +package alphaTab.collections + +public open class ObjectDoubleMapEntry<TKey> { + private var _key: TKey + public var key: TKey + get() = _key + internal set(value) { + _key = value + } + + private var _value: Double + public var value: Double + get() = _value + internal set(value) { + _value = value + } + + public constructor() { + _key = null as TKey + _value = 0.0 + } + + public constructor(key: TKey, value: Double) { + _key = key + _value = value + } +} + +public class ObjectDoubleMapEntryInternal<TKey> : ObjectDoubleMapEntry<TKey>(), + IMapEntryInternal { + public override var hashCode: Int = 0 + public override var next: Int = 0 + + override fun reset() { + key = null as TKey + value = 0.0 + } +} + +public class ObjectDoubleMap<TKey> : + MapBase<ObjectDoubleMapEntry<TKey>, ObjectDoubleMapEntryInternal<TKey>> { + public constructor() + public constructor(iterable: Iterable<ObjectDoubleMapEntry<TKey>>) { + for (it in iterable) { + set(it.key, it.value) + } + } + + public fun has(key: TKey): Boolean { + return findEntryInternal(key as Any, + { entry, k -> entry.key == (k as TKey) }) >= 0 + } + + public fun get(key: TKey): Double { + val i = findEntryInternal(key as Any, + { entry, k -> entry.key == (k as TKey) }) + if (i >= 0) { + return entries[i].value + } + throw KeyNotFoundException() + } + + public fun set(key: TKey, value: Double) { + insert(key, value) + } + + private fun insert(key: TKey, value: Double) { + insertInternal( + key as Any, value, + { entry, k -> entry.key = k as TKey }, + { entry, v -> entry.value = v }, + { entry, k -> entry.key == (k as TKey) } + ) + } + + public fun delete(key: TKey) { + deleteInternal(key.hashCode()) + } + + private var _values: ValueCollection<TKey>? = null + public fun values(): IDoubleIterable { + _values = _values ?: ValueCollection(this) + return _values!! + } + + private var _keys: KeyCollection<TKey>? = null + public fun keys(): Iterable<TKey> { + _keys = _keys ?: KeyCollection(this) + return _keys!! + } + + override fun createEntries(size: Int): Array<ObjectDoubleMapEntryInternal<TKey>> { + return Array(size) { + ObjectDoubleMapEntryInternal() + } + } + + override fun createEntries( + size: Int, + old: Array<ObjectDoubleMapEntryInternal<TKey>> + ): Array<ObjectDoubleMapEntryInternal<TKey>> { + return Array(size) { + if (it < old.size) old[it] else ObjectDoubleMapEntryInternal() + } + } + + private class ValueCollection<TKey>(private val map: ObjectDoubleMap<TKey>) : + IDoubleIterable { + override fun iterator(): DoubleIterator { + return ValueIterator(map.iterator()) + } + + private class ValueIterator<TKey>(private val iterator: Iterator<ObjectDoubleMapEntry<TKey>>) : + DoubleIterator() { + override fun hasNext(): Boolean { + return iterator.hasNext() + } + + override fun nextDouble(): Double { + return iterator.next().value + } + } + } + + + private class KeyCollection<TKey>(private val map: ObjectDoubleMap<TKey>) : Iterable<TKey> { + override fun iterator(): Iterator<TKey> { + return KeyIterator(map.iterator()) + } + + private class KeyIterator<TKey>(private val iterator: Iterator<ObjectDoubleMapEntry<TKey>>) : + Iterator<TKey> { + override fun hasNext(): Boolean { + return iterator.hasNext() + } + + override fun next(): TKey { + return iterator.next().key + } + } + } +} diff --git a/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/core/Globals.kt b/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/core/Globals.kt index 1cf928f22..bec4834d9 100644 --- a/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/core/Globals.kt +++ b/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/core/Globals.kt @@ -1,11 +1,7 @@ package alphaTab.core +import alphaTab.collections.List import alphaTab.core.ecmaScript.RegExp -import system.globalization.CultureInfo - -expect class LateInitList<T> : MutableList<T> { - public constructor(size:Int) -} @kotlin.ExperimentalUnsignedTypes expect fun UByteArray.decodeToFloatArray(): FloatArray @@ -16,6 +12,11 @@ expect fun UByteArray.decodeToDoubleArray(): DoubleArray @kotlin.ExperimentalUnsignedTypes expect fun UByteArray.decodeToString(encoding: String): String +fun <T : Comparable<T> > List<T>.sort(): Unit { + this.sort { a,b -> + a.compareTo(b).toDouble() + } +} fun String.substr(startIndex: Double, length: Double): String { return this.substring(startIndex.toInt(), (startIndex + length).toInt()) } @@ -24,86 +25,35 @@ fun String.substr(startIndex: Double): String { return this.substring(startIndex.toInt()) } -fun String.replace(pattern: RegExp, replacement: String): String { - return pattern.replace(this, replacement) -} - -fun <T> MutableList<T>.sort(comparer: ((a: T, b: T) -> Double)) { - this.sortWith { a, b -> comparer(a, b).toInt() } -} - -fun <TIn, TOut> MutableList<TIn>.mapTo(transform: (v: TIn) -> TOut): MutableList<TOut> { - return this.map(transform).toMutableList() -} - -fun <T> MutableList<T>.filterBy(predicate: (T) -> Boolean): MutableList<T> { - return this.filter(predicate).toMutableList() -} - -fun <T> MutableList<T>.slice(): MutableList<T> { - return this.toMutableList() -} - -fun <T> MutableList<T>.slice(start: Double): MutableList<T> { - return this.subList(start.toInt(), this.size) -} - -fun <T> MutableList<T>.rev(): MutableList<T> { - this.reverse() - return this +fun String.splitBy(separator:String): List<String> { + return List(this.split(separator)) } -fun <T> MutableList<T>.fillWith(value: T): MutableList<T> { - this.fill(value) - return this -} - -fun <T> MutableList<T>.splice(start: Double, deleteCount: Double, vararg newItems: T) { - if (deleteCount > 0) { - this.removeAll(this.subList(start.toInt(), (start + deleteCount).toInt())) - } - this.addAll(start.toInt(), newItems.asList()) +fun String.replace(pattern: RegExp, replacement: String): String { + return pattern.replace(this, replacement) } -fun <T> MutableList<T>.pop(): T { - return this.removeLast() +fun Iterable<Char>.toCharArray(): CharArray { + return this.toList().toCharArray() } fun String.indexOfInDouble(item: String): Double { return this.indexOf(item).toDouble() } -fun Double.toString(base: Double): String { +fun Double.toInvariantString(base: Double): String { return this.toInt().toString(base.toInt()) } expect fun Double.toInvariantString(): String - -fun Double.toString(cultureInfo: CultureInfo): String { - if (cultureInfo.isInvariant) { - return this.toInvariantString() - } else { - return this.toString() - } +fun IAlphaTabEnum.toInvariantString(): String { + return this.toString() } fun String.lastIndexOfInDouble(item: String): Double { return this.lastIndexOf(item).toDouble() } -fun <T> List<T>.indexOfInDouble(item: T): Double { - return this.indexOf(item).toDouble() -} - -@kotlin.jvm.JvmName("joinDouble") -fun Iterable<Double>.join(separator: String): String { - return this.map { it.toInvariantString() }.join(separator) -} - -fun <T> Iterable<T>.join(separator: String): String { - return this.joinToString(separator) -} - operator fun Double.plus(str: String): String { return this.toString() + str } @@ -120,10 +70,9 @@ fun String.charCodeAt(index: Double): Double { return this[index.toInt()].code.toDouble() } -fun String.split(delimiter: String): MutableList<String> { +fun String.split(delimiter: String): List<String> { @Suppress("CHANGING_ARGUMENTS_EXECUTION_ORDER_FOR_NAMED_VARARGS") - return this.split(delimiters = arrayOf(delimiter), ignoreCase = false, limit = 0) - .toMutableList() + return alphaTab.collections.List(this.split(delimiters = arrayOf(delimiter), ignoreCase = false, limit = 0)) } fun String.substring(startIndex: Double, endIndex: Double): String { @@ -191,3 +140,12 @@ class Globals { } } } + +public fun List<Char>.toCharArray(): CharArray { + val result = CharArray(length.toInt()) + var index = 0 + for (element in this) { + result[index++] = element + } + return result +} diff --git a/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/core/TypeHelper.kt b/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/core/TypeHelper.kt index befd2902b..5a75829a2 100644 --- a/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/core/TypeHelper.kt +++ b/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/core/TypeHelper.kt @@ -50,5 +50,10 @@ class TypeHelper { public fun <K, V> createMapEntry(k: K, v: V): Pair<K, V> { return Pair(k, v) } + + public fun <T> setInitializer(vararg values:T) : Iterable<T> + { + return values.map { it } + } } } diff --git a/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/core/ecmaScript/Array.kt b/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/core/ecmaScript/Array.kt index 56013def1..11a9d1646 100644 --- a/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/core/ecmaScript/Array.kt +++ b/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/core/ecmaScript/Array.kt @@ -2,11 +2,12 @@ package alphaTab.core.ecmaScript class Array { companion object { - public fun <T> from(x: Iterable<T>): MutableList<T> { - return x.toMutableList() + public fun <T> from(x: Iterable<T>): alphaTab.collections.List<T> { + return alphaTab.collections.List(x) } public fun isArray(x:Any?):Boolean { - return x is MutableList<*> + return x is alphaTab.collections.List<*> } + } } diff --git a/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/core/ecmaScript/DataView.kt b/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/core/ecmaScript/DataView.kt deleted file mode 100644 index 97722e139..000000000 --- a/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/core/ecmaScript/DataView.kt +++ /dev/null @@ -1,66 +0,0 @@ -package alphaTab.core.ecmaScript - -import alphaTab.core.BitConverter - -@ExperimentalUnsignedTypes -class DataView { - private val _buffer: ArrayBuffer - - public constructor(buffer: ArrayBuffer) { - this._buffer = buffer - } - - public fun getUint8(offset:Double): Double { - return _buffer.raw[offset.toInt()].toDouble() - } - - public fun setUint16(offset: Double, value: Double, littleEndian: Boolean) { - BitConverter.put( - _buffer.raw.asByteArray(), - offset.toInt(), - value.toUInt().toUShort(), - littleEndian - ) - } - - public fun getInt16(offset: Double, littleEndian: Boolean): Double { - return BitConverter.getInt16(_buffer.raw.asByteArray(), offset.toInt(), littleEndian) - .toDouble() - } - - public fun setInt16(offset: Double, value: Double, littleEndian: Boolean) { - BitConverter.put( - _buffer.raw.asByteArray(), - offset.toInt(), - value.toInt().toShort(), - littleEndian - ) - } - - public fun getUint32(offset: Double, littleEndian: Boolean): Double { - return BitConverter.getUint32(_buffer.raw.asByteArray(), offset.toInt(), littleEndian) - .toDouble() - } - - public fun getInt32(offset: Double, littleEndian: Boolean): Double { - return BitConverter.getInt32(_buffer.raw.asByteArray(), offset.toInt(), littleEndian) - .toDouble() - } - - public fun setInt32(offset: Double, value: Double, littleEndian: Boolean) { - BitConverter.put(_buffer.raw.asByteArray(), offset.toInt(), value.toInt(), littleEndian) - } - - public fun getUint16(offset: Double, littleEndian: Boolean): Double { - return BitConverter.getUint16(_buffer.raw.asByteArray(), offset.toInt(), littleEndian) - .toDouble() - } - - public fun setUint8(offset: Double, value: Double) { - _buffer.raw[offset.toInt()] = value.toUInt().toUByte() - } - - public fun getInt8(offset: Double): Double { - return _buffer.raw[offset.toInt()].toByte().toDouble() - } -} diff --git a/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/core/ecmaScript/Error.kt b/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/core/ecmaScript/Error.kt index cea490747..baa8ad808 100644 --- a/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/core/ecmaScript/Error.kt +++ b/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/core/ecmaScript/Error.kt @@ -1,13 +1,5 @@ package alphaTab.core.ecmaScript -open class Error : Throwable { - public override val message: String +import kotlin.Exception - public constructor() : super() { - this.message = "" - } - - public constructor(msg: String) : super(msg) { - this.message = msg; - } -} +typealias Error = Exception diff --git a/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/core/ecmaScript/Float32Array.kt b/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/core/ecmaScript/Float32Array.kt index 7334ea114..8e12e4e4f 100644 --- a/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/core/ecmaScript/Float32Array.kt +++ b/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/core/ecmaScript/Float32Array.kt @@ -23,7 +23,7 @@ public class Float32Array : Iterable<Float> { data = x } - public constructor(x: List<Double>) { + public constructor(x: Iterable<Double>) { this.data = x.map { d -> d.toFloat() }.toFloatArray() } diff --git a/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/core/ecmaScript/Map.kt b/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/core/ecmaScript/Map.kt deleted file mode 100644 index 338b48c0c..000000000 --- a/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/core/ecmaScript/Map.kt +++ /dev/null @@ -1,53 +0,0 @@ -package alphaTab.core.ecmaScript - -import kotlin.collections.Map - -class Map<TKey, TValue> : Iterable<Map.Entry<TKey, TValue>> { - private val _map: LinkedHashMap <TKey, TValue> - - public val size: Double - get() { - return _map.size.toDouble() - } - - public constructor() { - _map = LinkedHashMap () - } - - public fun keys(): MutableList<TKey> { - return _map.keys.toMutableList() - } - - public fun values(): MutableList<TValue> { - return _map.values.toMutableList() - } - - public constructor(entries: Iterable<Pair<TKey, TValue>>) { - _map = LinkedHashMap () - _map.putAll(entries) - } - - public fun get(key: TKey): TValue? { - return _map[key] - } - - public fun set(key: TKey, value: TValue) { - _map[key] = value - } - - public fun has(key: TKey): Boolean { - return _map.containsKey(key) - } - - public fun delete(key: TKey) { - _map.remove(key) - } - - public fun clear() { - _map.clear() - } - - override fun iterator(): Iterator<Map.Entry<TKey, TValue>> { - return _map.iterator() - } -} diff --git a/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/core/ecmaScript/Uint8Array.kt b/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/core/ecmaScript/Uint8Array.kt index 32286d732..b04e582da 100644 --- a/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/core/ecmaScript/Uint8Array.kt +++ b/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/core/ecmaScript/Uint8Array.kt @@ -2,51 +2,55 @@ package alphaTab.core.ecmaScript @ExperimentalUnsignedTypes class Uint8Array : Iterable<UByte> { - private val _data: UByteArray + private val _data: ArrayBuffer public val buffer: ArrayBuffer get() { - return ArrayBuffer(_data) + return _data } - public constructor(x: List<Double>) { - this._data = x.map { d -> d.toInt().toUByte() }.toUByteArray() + public constructor(x: Iterable<Double>) { + this._data = ArrayBuffer(x.map { d -> d.toInt().toUByte() }.toUByteArray()) } public constructor(size: Double) { - this._data = UByteArray(size.toInt()) + this._data = ArrayBuffer(UByteArray(size.toInt())) } - internal constructor(data: UByteArray) { + public constructor(data: UByteArray) { + this._data = ArrayBuffer(data) + } + + public constructor(data:ArrayBuffer) { this._data = data } public val length: Double get() { - return _data.size.toDouble() + return _data.raw.size.toDouble() } public operator fun get(idx: Int): Double { - return _data[idx].toDouble() + return _data.raw[idx].toDouble() } public operator fun get(idx: Double): Double { - return _data[idx.toInt()].toDouble() + return _data.raw[idx.toInt()].toDouble() } public operator fun set(idx: Int, value: Double) { - _data[idx] = value.toInt().toUByte() + _data.raw[idx] = value.toInt().toUByte() } public fun set(subarray: Uint8Array, pos: Double) { - subarray._data.copyInto(_data, pos.toInt(), 0, subarray._data.size) + subarray._data.raw.copyInto(_data.raw, pos.toInt(), 0, subarray._data.raw.size) } override fun iterator(): Iterator<UByte> { - return _data.iterator() + return _data.raw.iterator() } public fun subarray(begin: Double, end: Double): Uint8Array { - return Uint8Array(_data.copyOfRange(begin.toInt(), end.toInt())) + return Uint8Array(_data.raw.copyOfRange(begin.toInt(), end.toInt())) } } diff --git a/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/core/ecmaScript/ValueTypeMap.kt b/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/core/ecmaScript/ValueTypeMap.kt deleted file mode 100644 index bc7e27e9b..000000000 --- a/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/core/ecmaScript/ValueTypeMap.kt +++ /dev/null @@ -1,45 +0,0 @@ -package alphaTab.core.ecmaScript - -import kotlin.collections.Map - -class ValueTypeMap<TKey, TValue> : Iterable<Map.Entry<TKey, TValue>> { - private val _map: HashMap<TKey, TValue> - - public val size: Double - get() { - return _map.size.toDouble() - } - - public constructor() { - _map = HashMap() - } - - public constructor(entries: Iterable<Pair<TKey, TValue>>) { - _map = HashMap() - _map.putAll(entries) - } - - public fun get(key: TKey): TValue? { - return _map[key] - } - - public fun set(key: TKey, value: TValue) { - _map[key] = value - } - - public fun has(key: TKey): Boolean { - return _map.containsKey(key) - } - - public fun delete(key: TKey) { - _map.remove(key) - } - - public fun clear() { - _map.clear() - } - - override fun iterator(): Iterator<Map.Entry<TKey, TValue>> { - return _map.iterator() - } -} diff --git a/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/io/JsonHelperPartials.kt b/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/io/JsonHelperPartials.kt index d6ebb8b98..1774779c4 100644 --- a/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/io/JsonHelperPartials.kt +++ b/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/io/JsonHelperPartials.kt @@ -16,7 +16,7 @@ internal open class JsonHelperPartials { } } - if (o is alphaTab.core.ecmaScript.Map<*, *>) { + if (o is alphaTab.collections.Map<*, *>) { for (kvp in o) { func(kvp.value, (kvp.key!!) as String) } @@ -30,7 +30,7 @@ internal open class JsonHelperPartials { } } - if (o is alphaTab.core.ecmaScript.Map<*, *>) { + if (o is alphaTab.collections.Map<*, *>) { for (kvp in o) { func(kvp.value, (kvp.key!!) as String) } diff --git a/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/io/TypeConversions.kt b/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/io/TypeConversions.kt new file mode 100644 index 000000000..dd19f1247 --- /dev/null +++ b/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/io/TypeConversions.kt @@ -0,0 +1,16 @@ +package alphaTab.io + +import alphaTab.core.ecmaScript.Uint8Array + +expect class TypeConversions { + companion object { + public fun float64ToBytes(v: Double): Uint8Array + public fun bytesToFloat64(bytes: Uint8Array): Double + public fun uint16ToInt16(v: Double): Double + public fun int16ToUint32(v: Double): Double + public fun int32ToUint16(v: Double): Double + public fun int32ToInt16(v: Double): Double + public fun int32ToUint32(v: Double): Double + public fun uint8ToInt8(v: Double): Double + } +} diff --git a/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/core/Lazy.kt b/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/util/Lazy.kt similarity index 94% rename from src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/core/Lazy.kt rename to src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/util/Lazy.kt index 83e573c95..ef73fe6b0 100644 --- a/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/core/Lazy.kt +++ b/src.kotlin/alphaTab/src/commonMain/kotlin/alphaTab/util/Lazy.kt @@ -1,4 +1,4 @@ -package alphaTab.core +package alphaTab.util internal object UninitializedValue diff --git a/src.kotlin/alphaTab/src/commonMain/kotlin/system/Action.kt b/src.kotlin/alphaTab/src/commonMain/kotlin/system/Action.kt deleted file mode 100644 index 159a5a1a0..000000000 --- a/src.kotlin/alphaTab/src/commonMain/kotlin/system/Action.kt +++ /dev/null @@ -1,3 +0,0 @@ -package system - -typealias Action = () -> Unit; diff --git a/src.kotlin/alphaTab/src/commonMain/kotlin/system/globalization/CultureInfo.kt b/src.kotlin/alphaTab/src/commonMain/kotlin/system/globalization/CultureInfo.kt deleted file mode 100644 index b9a6fae31..000000000 --- a/src.kotlin/alphaTab/src/commonMain/kotlin/system/globalization/CultureInfo.kt +++ /dev/null @@ -1,13 +0,0 @@ -package system.globalization - -class CultureInfo { - companion object { - public var invariantCulture = CultureInfo(true) - } - - public val isInvariant:Boolean; - - public constructor(isInvariant:Boolean) { - this.isInvariant = isInvariant; - } -} diff --git a/src.kotlin/alphaTab/src/jvmCommon/kotlin/alphaTab/core/GlobalsImpl.kt b/src.kotlin/alphaTab/src/jvmCommon/kotlin/alphaTab/core/GlobalsImpl.kt index aa10e9087..f9db44285 100644 --- a/src.kotlin/alphaTab/src/jvmCommon/kotlin/alphaTab/core/GlobalsImpl.kt +++ b/src.kotlin/alphaTab/src/jvmCommon/kotlin/alphaTab/core/GlobalsImpl.kt @@ -7,14 +7,6 @@ import java.text.DecimalFormat import java.text.NumberFormat import java.util.* -actual class LateInitList<T> : java.util.ArrayList<T>, MutableList<T> { - @Suppress("UNCHECKED_CAST") - public actual constructor(size: Int) : super(arrayOfNulls<Any>(size).toList() as List<T>) { - - } -} - - @ExperimentalUnsignedTypes actual fun UByteArray.decodeToFloatArray(): FloatArray { val fb = ByteBuffer.wrap(this.toByteArray()).order(ByteOrder.LITTLE_ENDIAN).asFloatBuffer(); diff --git a/src.kotlin/alphaTab/src/jvmCommon/kotlin/alphaTab/io/TypeConversions.kt b/src.kotlin/alphaTab/src/jvmCommon/kotlin/alphaTab/io/TypeConversions.kt new file mode 100644 index 000000000..210d796ee --- /dev/null +++ b/src.kotlin/alphaTab/src/jvmCommon/kotlin/alphaTab/io/TypeConversions.kt @@ -0,0 +1,60 @@ +package alphaTab.io + +import alphaTab.core.ecmaScript.Uint8Array + +@ExperimentalUnsignedTypes +actual class TypeConversions { + actual companion object { + public actual fun float64ToBytes(v: Double): Uint8Array { + val l = java.lang.Double.doubleToLongBits(v); + return Uint8Array( + ubyteArrayOf( + ((l shl 56) and 0xFF).toUByte(), + ((l shl 48) and 0xFF).toUByte(), + ((l shl 40) and 0xFF).toUByte(), + ((l shl 32) and 0xFF).toUByte(), + ((l shl 24) and 0xFF).toUByte(), + ((l shl 16) and 0xFF).toUByte(), + ((l shl 8) and 0xFF).toUByte(), + ((l shl 0) and 0xFF).toUByte() + ) + ) + } + + public actual fun bytesToFloat64(bytes: Uint8Array): Double { + val l = (bytes.buffer.raw[0].toLong() shl 56) or + (bytes.buffer.raw[1].toLong() shl 48) or + (bytes.buffer.raw[2].toLong() shl 40) or + (bytes.buffer.raw[3].toLong() shl 32) or + (bytes.buffer.raw[4].toLong() shl 24) or + (bytes.buffer.raw[5].toLong() shl 16) or + (bytes.buffer.raw[6].toLong() shl 8) or + (bytes.buffer.raw[7].toLong() shl 0) + return java.lang.Double.longBitsToDouble(l); + } + + public actual fun uint16ToInt16(v: Double): Double { + return v.toUInt().toUShort().toDouble() + } + + public actual fun int16ToUint32(v: Double): Double { + return v.toInt().toShort().toUInt().toDouble() + } + + public actual fun int32ToUint16(v: Double): Double { + return v.toInt().toUShort().toDouble() + } + + public actual fun int32ToInt16(v: Double): Double { + return v.toInt().toShort().toDouble() + } + + public actual fun int32ToUint32(v: Double): Double { + return v.toInt().toUInt().toDouble() + } + + public actual fun uint8ToInt8(v: Double): Double { + return v.toUInt().toUByte().toInt().toDouble() + } + } +} diff --git a/src.kotlin/alphaTab/src/jvmMain/kotlin/alphaTab/EnvironmentPartials.kt b/src.kotlin/alphaTab/src/jvmMain/kotlin/alphaTab/EnvironmentPartials.kt index 9455656bb..cb0cc0433 100644 --- a/src.kotlin/alphaTab/src/jvmMain/kotlin/alphaTab/EnvironmentPartials.kt +++ b/src.kotlin/alphaTab/src/jvmMain/kotlin/alphaTab/EnvironmentPartials.kt @@ -5,7 +5,7 @@ import kotlin.contracts.ExperimentalContracts @ExperimentalUnsignedTypes @ExperimentalContracts -internal actual fun createPlatformSpecificRenderEngines(engines: alphaTab.core.ecmaScript.Map<String, RenderEngineFactory>) { +internal actual fun createPlatformSpecificRenderEngines(engines: alphaTab.collections.Map<String, RenderEngineFactory>) { engines.set( "skia", RenderEngineFactory(true) { SkiaCanvas() } diff --git a/src.kotlin/alphaTab/src/jvmMain/kotlin/alphaTab/platform/jvm/SkiaCanvas.kt b/src.kotlin/alphaTab/src/jvmMain/kotlin/alphaTab/platform/jvm/SkiaCanvas.kt index a84395653..877beceb3 100644 --- a/src.kotlin/alphaTab/src/jvmMain/kotlin/alphaTab/platform/jvm/SkiaCanvas.kt +++ b/src.kotlin/alphaTab/src/jvmMain/kotlin/alphaTab/platform/jvm/SkiaCanvas.kt @@ -2,6 +2,7 @@ package alphaTab.platform.jvm import alphaTab.Settings import alphaTab.core.BitConverter +import alphaTab.core.toCharArray import alphaTab.model.Color import alphaTab.model.Font import alphaTab.model.MusicFontSymbol @@ -413,19 +414,19 @@ public class SkiaCanvas : ICanvas { symbol: MusicFontSymbol, centerAtPosition: Boolean? ) { - fillMusicFontSymbols(x, y, scale, mutableListOf(symbol), centerAtPosition) + fillMusicFontSymbols(x, y, scale, alphaTab.collections.List(symbol), centerAtPosition) } override fun fillMusicFontSymbols( x: Double, y: Double, scale: Double, - symbols: MutableList<MusicFontSymbol>, + symbols: alphaTab.collections.List<MusicFontSymbol>, centerAtPosition: Boolean? ) { val s = String(symbols .filter { it != MusicFontSymbol.None } - .map { it.value.toChar() } + .map<Char> { it.value.toChar() } .toCharArray() ) diff --git a/src.kotlin/alphaTab/src/jvmTest/kotlin/alphaTab/TestPlatformPartials.kt b/src.kotlin/alphaTab/src/jvmTest/kotlin/alphaTab/TestPlatformPartials.kt index 3b142140a..40446a012 100644 --- a/src.kotlin/alphaTab/src/jvmTest/kotlin/alphaTab/TestPlatformPartials.kt +++ b/src.kotlin/alphaTab/src/jvmTest/kotlin/alphaTab/TestPlatformPartials.kt @@ -42,9 +42,12 @@ class TestPlatformPartials { } } - public fun listDirectory(path:String): MutableList<String> { + public fun listDirectory(path:String): alphaTab.collections.List<String> { val dirPath = Path.of(projectRoot, path) - return dirPath.toFile().listFiles()?.map { it.name }?.toMutableList() ?: mutableListOf() + return alphaTab.collections.List(dirPath.toFile() + .listFiles() + ?.filter { it.isFile } + ?.map { it.name } ?: emptyList()) } } } diff --git a/src.kotlin/alphaTab/src/jvmTest/kotlin/alphaTab/model/ComparisonHelpersPartials.kt b/src.kotlin/alphaTab/src/jvmTest/kotlin/alphaTab/model/ComparisonHelpersPartials.kt new file mode 100644 index 000000000..f822a85e3 --- /dev/null +++ b/src.kotlin/alphaTab/src/jvmTest/kotlin/alphaTab/model/ComparisonHelpersPartials.kt @@ -0,0 +1,38 @@ +package alphaTab.model + +import alphaTab.collections.DoubleList +import alphaTab.test.Globals +import kotlin.contracts.ExperimentalContracts + +@ExperimentalUnsignedTypes +@ExperimentalContracts +class ComparisonHelpersPartials { + companion object { + public fun compareObjects(expected: Any?, actual: Any?, path: String, ignoreKeys: alphaTab.collections.List<String>?): Boolean { + if (actual is DoubleList && expected is DoubleList) { + if (actual.length != expected.length) { + Globals.fail("""Double Array Length mismatch on hierarchy: ${path}, ${actual.length} != ${expected.length}""") + return false + } else { + var i = 0 + while (i < actual.length) { + try { + if (alphaTab.core.ecmaScript.Math.abs((actual[i]) - (expected[i])) >= 0.000001) + { + Globals.fail("""Number mismatch on hierarchy: ${path}[${i}], '${actual}' != '${expected}'""") + return false + } + } finally { + i++ + } + } + } + + return true + } + + Globals.fail("cannot compare unknown object types expected[${actual?.javaClass?.packageName}.${actual?.javaClass?.name}] expected[${expected?.javaClass?.packageName}.${expected?.javaClass?.name}]'); }") + return false + } + } +} diff --git a/src.kotlin/alphaTab/src/jvmTest/kotlin/alphaTab/test/Globals.kt b/src.kotlin/alphaTab/src/jvmTest/kotlin/alphaTab/test/Globals.kt index cbbd68b66..b6bcc17da 100644 --- a/src.kotlin/alphaTab/src/jvmTest/kotlin/alphaTab/test/Globals.kt +++ b/src.kotlin/alphaTab/src/jvmTest/kotlin/alphaTab/test/Globals.kt @@ -11,6 +11,10 @@ class Globals { public fun fail(message: Any?) { Assert.fail(message.toString()) } + + public fun fail(message: Throwable) { + Assert.fail(message.toString() + message.stackTraceToString()) + } } } diff --git a/src.kotlin/alphaTab/src/jvmTest/kotlin/alphaTab/visualTests/VisualTestHelperPartials.kt b/src.kotlin/alphaTab/src/jvmTest/kotlin/alphaTab/visualTests/VisualTestHelperPartials.kt index bce4a0b07..174441b90 100644 --- a/src.kotlin/alphaTab/src/jvmTest/kotlin/alphaTab/visualTests/VisualTestHelperPartials.kt +++ b/src.kotlin/alphaTab/src/jvmTest/kotlin/alphaTab/visualTests/VisualTestHelperPartials.kt @@ -3,6 +3,7 @@ package alphaTab.visualTests import alphaTab.Settings import alphaTab.TestPlatform import alphaTab.TestPlatformPartials +import alphaTab.collections.DoubleList import alphaTab.core.ecmaScript.Uint8Array import alphaTab.core.toInvariantString import alphaTab.importer.AlphaTexImporter @@ -30,7 +31,7 @@ class VisualTestHelperPartials { public fun runVisualTest( inputFile: String, settings: Settings? = null, - tracks: MutableList<Double>? = null, + tracks: DoubleList? = null, message: String? = null, tolerancePercent: Double = 1.0, triggerResize: Boolean = false @@ -59,7 +60,7 @@ class VisualTestHelperPartials { tex: String, referenceFileName: String, settings: Settings? = null, - tracks: MutableList<Double>? = null, + tracks: DoubleList? = null, message: String? = null, tolerancePercent: Double = 1.0 ) { @@ -86,13 +87,13 @@ class VisualTestHelperPartials { score: Score, referenceFileName: String, settings: Settings? = null, - tracks: MutableList<Double>? = null, + tracks: DoubleList? = null, message: String? = null, tolerancePercent: Double = 1.0, triggerResize: Boolean = false ) { val actualSettings = settings ?: Settings() - val actualTracks = tracks ?: ArrayList() + val actualTracks = tracks ?: DoubleList() actualSettings.core.engine = "skia" actualSettings.core.enableLazyLoading = false @@ -166,7 +167,7 @@ class VisualTestHelperPartials { } } - if (waitHandle.tryAcquire(2000, TimeUnit.MILLISECONDS)) { + if (waitHandle.tryAcquire(100000, TimeUnit.MILLISECONDS)) { if (error != null) { Assert.fail("Rendering failed with error $error ${error?.stackTraceToString()}") } else { diff --git a/src/AlphaTabApiBase.ts b/src/AlphaTabApiBase.ts index 909477996..25d7793b6 100644 --- a/src/AlphaTabApiBase.ts +++ b/src/AlphaTabApiBase.ts @@ -384,7 +384,7 @@ export class AlphaTabApiBase<TSettings> { if (this.uiFacade.canRender) { // when font is finally loaded, start rendering this.renderer.width = this.container.width; - this.renderer.renderScore(this.score!, this._trackIndexes!); + this.renderer.renderScore(this.score, this._trackIndexes); } else { this.uiFacade.canRenderChanged.on(() => this.render()); } diff --git a/src/AlphaTabError.ts b/src/AlphaTabError.ts index 5a2d0c1fb..eb450b615 100644 --- a/src/AlphaTabError.ts +++ b/src/AlphaTabError.ts @@ -8,8 +8,8 @@ export class AlphaTabError extends Error { public inner: Error | null; public type: AlphaTabErrorType; - public constructor(type: AlphaTabErrorType, message: string = "", inner?: Error) { - super(message); + public constructor(type: AlphaTabErrorType, message: string | null = "", inner?: Error) { + super(message ?? ""); this.type = type; this.inner = inner ?? null; Object.setPrototypeOf(this, AlphaTabError.prototype); diff --git a/src/exporter/GpifWriter.ts b/src/exporter/GpifWriter.ts index 3263e3a81..e39ecb322 100644 --- a/src/exporter/GpifWriter.ts +++ b/src/exporter/GpifWriter.ts @@ -571,8 +571,8 @@ export class GpifWriter { } private writeBend(properties: XmlNode, note: Note) { - if (note.hasBend && note.bendPoints.length <= 4) { - this.writeStandardBend(properties, note.bendPoints); + if (note.hasBend && note.bendPoints!.length <= 4) { + this.writeStandardBend(properties, note.bendPoints!); } } @@ -830,8 +830,8 @@ export class GpifWriter { } private writeWhammyNode(parent: XmlNode, beat: Beat) { - if (beat.hasWhammyBar && beat.whammyBarPoints.length <= 4) { - this.writeStandardWhammy(parent, beat.whammyBarPoints); + if (beat.hasWhammyBar && beat.whammyBarPoints!.length <= 4) { + this.writeStandardWhammy(parent, beat.whammyBarPoints!); } } @@ -1184,110 +1184,113 @@ export class GpifWriter { diagramCollectionProperty.attributes.set('name', name); const diagramCollectionItems = diagramCollectionProperty.addElement('Items'); - for (const [id, chord] of staff.chords) { - const diagramCollectionItem = diagramCollectionItems.addElement('Item'); - diagramCollectionItem.attributes.set('id', id); - diagramCollectionItem.attributes.set('name', chord.name); - - const diagram = diagramCollectionItem.addElement('Diagram'); - diagram.attributes.set('stringCount', chord.strings.length.toString()); - diagram.attributes.set('fretCount', '5'); - diagram.attributes.set('baseFret', (chord.firstFret - 1).toString()); - diagram.attributes.set('barStates', chord.strings.map(_ => '1').join(' ')); - - const frets: number[] = []; - const fretToStrings = new Map<number, number[]>(); - - for (let i = 0; i < chord.strings.length; i++) { - let chordFret = chord.strings[i]; - if (chordFret !== -1) { - const fretNode = diagram.addElement('Fret'); - const chordString = (chord.strings.length - 1 - i); - fretNode.attributes.set('string', chordString.toString()); - fretNode.attributes.set('fret', (chordFret - chord.firstFret + 1).toString()); - if (!fretToStrings.has(chordFret)) { - fretToStrings.set(chordFret, []); - frets.push(chordFret); + const sc = staff.chords; + if (sc) { + for (const [id, chord] of sc) { + const diagramCollectionItem = diagramCollectionItems.addElement('Item'); + diagramCollectionItem.attributes.set('id', id); + diagramCollectionItem.attributes.set('name', chord.name); + + const diagram = diagramCollectionItem.addElement('Diagram'); + diagram.attributes.set('stringCount', chord.strings.length.toString()); + diagram.attributes.set('fretCount', '5'); + diagram.attributes.set('baseFret', (chord.firstFret - 1).toString()); + diagram.attributes.set('barStates', chord.strings.map(_ => '1').join(' ')); + + const frets: number[] = []; + const fretToStrings = new Map<number, number[]>(); + + for (let i = 0; i < chord.strings.length; i++) { + let chordFret = chord.strings[i]; + if (chordFret !== -1) { + const fretNode = diagram.addElement('Fret'); + const chordString = (chord.strings.length - 1 - i); + fretNode.attributes.set('string', chordString.toString()); + fretNode.attributes.set('fret', (chordFret - chord.firstFret + 1).toString()); + if (!fretToStrings.has(chordFret)) { + fretToStrings.set(chordFret, []); + frets.push(chordFret); + } + fretToStrings.get(chordFret)!.push(chordString); } - fretToStrings.get(chordFret)!.push(chordString); } - } - frets.sort(); - - // try to rebuild the barre frets - const fingering = diagram.addElement('Fingering'); - if (chord.barreFrets.length > 0) { - const fingers = [ - Fingers.LittleFinger, - Fingers.AnnularFinger, - Fingers.MiddleFinger, - Fingers.IndexFinger, - ]; - - for (const fret of frets) { - const fretStrings = fretToStrings.get(fret)!; - if (fretStrings.length > 1 && chord.barreFrets.indexOf(fret) >= 0) { - const finger = fingers.length > 0 ? fingers.pop() : Fingers.IndexFinger; - for (const fretString of fretStrings) { - const position = fingering.addElement('Position'); - switch (finger) { - case Fingers.LittleFinger: - position.attributes.set('finger', 'Pinky'); - break; - case Fingers.AnnularFinger: - position.attributes.set('finger', 'Ring'); - break; - case Fingers.MiddleFinger: - position.attributes.set('finger', 'Middle'); - break; - case Fingers.IndexFinger: - position.attributes.set('finger', 'Index'); - break; + frets.sort(); + + // try to rebuild the barre frets + const fingering = diagram.addElement('Fingering'); + if (chord.barreFrets.length > 0) { + const fingers = [ + Fingers.LittleFinger, + Fingers.AnnularFinger, + Fingers.MiddleFinger, + Fingers.IndexFinger, + ]; + + for (const fret of frets) { + const fretStrings = fretToStrings.get(fret)!; + if (fretStrings.length > 1 && chord.barreFrets.indexOf(fret) >= 0) { + const finger = fingers.length > 0 ? fingers.pop() : Fingers.IndexFinger; + for (const fretString of fretStrings) { + const position = fingering.addElement('Position'); + switch (finger) { + case Fingers.LittleFinger: + position.attributes.set('finger', 'Pinky'); + break; + case Fingers.AnnularFinger: + position.attributes.set('finger', 'Ring'); + break; + case Fingers.MiddleFinger: + position.attributes.set('finger', 'Middle'); + break; + case Fingers.IndexFinger: + position.attributes.set('finger', 'Index'); + break; + } + position.attributes.set('fret', (fret - chord.firstFret + 1).toString()); + position.attributes.set('string', fretString.toString()); } - position.attributes.set('fret', (fret - chord.firstFret + 1).toString()); - position.attributes.set('string', fretString.toString()); } } } - } - const showName = diagram.addElement('Property'); - showName.attributes.set('name', 'ShowName'); - showName.attributes.set('type', 'bool'); - showName.attributes.set('value', chord.showName ? "true" : "false"); + const showName = diagram.addElement('Property'); + showName.attributes.set('name', 'ShowName'); + showName.attributes.set('type', 'bool'); + showName.attributes.set('value', chord.showName ? "true" : "false"); - const showDiagram = diagram.addElement('Property'); - showDiagram.attributes.set('name', 'ShowDiagram'); - showDiagram.attributes.set('type', 'bool'); - showDiagram.attributes.set('value', chord.showDiagram ? "true" : "false"); + const showDiagram = diagram.addElement('Property'); + showDiagram.attributes.set('name', 'ShowDiagram'); + showDiagram.attributes.set('type', 'bool'); + showDiagram.attributes.set('value', chord.showDiagram ? "true" : "false"); - const showFingering = diagram.addElement('Property'); - showFingering.attributes.set('name', 'ShowFingering'); - showFingering.attributes.set('type', 'bool'); - showFingering.attributes.set('value', chord.showFingering ? "true" : "false"); + const showFingering = diagram.addElement('Property'); + showFingering.attributes.set('name', 'ShowFingering'); + showFingering.attributes.set('type', 'bool'); + showFingering.attributes.set('value', chord.showFingering ? "true" : "false"); - // TODO Chord details - const chordNode = diagram.addElement('Chord'); - const keyNoteNode = chordNode.addElement('KeyNote'); - keyNoteNode.attributes.set('step', 'C'); - keyNoteNode.attributes.set('accidental', 'Natural'); + // TODO Chord details + const chordNode = diagram.addElement('Chord'); + const keyNoteNode = chordNode.addElement('KeyNote'); + keyNoteNode.attributes.set('step', 'C'); + keyNoteNode.attributes.set('accidental', 'Natural'); - const bassNoteNode = chordNode.addElement('BassNote'); - bassNoteNode.attributes.set('step', 'C'); - bassNoteNode.attributes.set('accidental', 'Natural'); + const bassNoteNode = chordNode.addElement('BassNote'); + bassNoteNode.attributes.set('step', 'C'); + bassNoteNode.attributes.set('accidental', 'Natural'); - const degree1Node = chordNode.addElement('Degree'); - degree1Node.attributes.set('interval', 'Third'); - degree1Node.attributes.set('alteration', 'Major'); - degree1Node.attributes.set('omitted', 'false'); + const degree1Node = chordNode.addElement('Degree'); + degree1Node.attributes.set('interval', 'Third'); + degree1Node.attributes.set('alteration', 'Major'); + degree1Node.attributes.set('omitted', 'false'); - const degree2Node = chordNode.addElement('Degree'); - degree2Node.attributes.set('interval', 'Fifth'); - degree2Node.attributes.set('alteration', 'Perfect'); - degree2Node.attributes.set('omitted', 'false'); + const degree2Node = chordNode.addElement('Degree'); + degree2Node.attributes.set('interval', 'Fifth'); + degree2Node.attributes.set('alteration', 'Perfect'); + degree2Node.attributes.set('omitted', 'false'); + } } } @@ -1531,13 +1534,14 @@ export class GpifWriter { } private writeFermatas(parent: XmlNode, masterBar: MasterBar) { - if (masterBar.fermata.size === 0) { + const fermataCount = (masterBar.fermata?.size ?? 0); + if (fermataCount === 0) { return; } - if (masterBar.fermata.size > 0) { + if (fermataCount > 0) { const fermatas = parent.addElement('Fermatas'); - for (const [offset, fermata] of masterBar.fermata) { + for (const [offset, fermata] of masterBar.fermata!) { this.writeFermata(fermatas, offset, fermata); } } diff --git a/src/generated/CoreSettingsSerializer.ts b/src/generated/CoreSettingsSerializer.ts index c24ad76b2..18f54d8e3 100644 --- a/src/generated/CoreSettingsSerializer.ts +++ b/src/generated/CoreSettingsSerializer.ts @@ -19,20 +19,20 @@ export class CoreSettingsSerializer { } const o = new Map<string, unknown>(); /*@target web*/ - o.set("scriptFile", obj.scriptFile); + o.set("scriptfile", obj.scriptFile); /*@target web*/ - o.set("fontDirectory", obj.fontDirectory); + o.set("fontdirectory", obj.fontDirectory); /*@target web*/ o.set("file", obj.file); /*@target web*/ o.set("tex", obj.tex); /*@target web*/ o.set("tracks", obj.tracks); - o.set("enableLazyLoading", obj.enableLazyLoading); + o.set("enablelazyloading", obj.enableLazyLoading); o.set("engine", obj.engine); - o.set("logLevel", obj.logLevel as number); - o.set("useWorkers", obj.useWorkers); - o.set("includeNoteBounds", obj.includeNoteBounds); + o.set("loglevel", obj.logLevel as number); + o.set("useworkers", obj.useWorkers); + o.set("includenotebounds", obj.includeNoteBounds); return o; } public static setProperty(obj: CoreSettings, property: string, v: unknown): boolean { diff --git a/src/generated/DisplaySettingsSerializer.ts b/src/generated/DisplaySettingsSerializer.ts index e29db092a..627d570e2 100644 --- a/src/generated/DisplaySettingsSerializer.ts +++ b/src/generated/DisplaySettingsSerializer.ts @@ -21,13 +21,13 @@ export class DisplaySettingsSerializer { } const o = new Map<string, unknown>(); o.set("scale", obj.scale); - o.set("stretchForce", obj.stretchForce); - o.set("layoutMode", obj.layoutMode as number); - o.set("staveProfile", obj.staveProfile as number); - o.set("barsPerRow", obj.barsPerRow); - o.set("startBar", obj.startBar); - o.set("barCount", obj.barCount); - o.set("barCountPerPartial", obj.barCountPerPartial); + o.set("stretchforce", obj.stretchForce); + o.set("layoutmode", obj.layoutMode as number); + o.set("staveprofile", obj.staveProfile as number); + o.set("barsperrow", obj.barsPerRow); + o.set("startbar", obj.startBar); + o.set("barcount", obj.barCount); + o.set("barcountperpartial", obj.barCountPerPartial); o.set("resources", RenderingResourcesSerializer.toJson(obj.resources)); o.set("padding", obj.padding); return o; diff --git a/src/generated/ImporterSettingsSerializer.ts b/src/generated/ImporterSettingsSerializer.ts index 1bdfc5177..e904e7052 100644 --- a/src/generated/ImporterSettingsSerializer.ts +++ b/src/generated/ImporterSettingsSerializer.ts @@ -18,8 +18,8 @@ export class ImporterSettingsSerializer { } const o = new Map<string, unknown>(); o.set("encoding", obj.encoding); - o.set("mergePartGroupsInMusicXml", obj.mergePartGroupsInMusicXml); - o.set("beatTextAsLyrics", obj.beatTextAsLyrics); + o.set("mergepartgroupsinmusicxml", obj.mergePartGroupsInMusicXml); + o.set("beattextaslyrics", obj.beatTextAsLyrics); return o; } public static setProperty(obj: ImporterSettings, property: string, v: unknown): boolean { diff --git a/src/generated/NotationSettingsSerializer.ts b/src/generated/NotationSettingsSerializer.ts index a817bf41b..1c97325c6 100644 --- a/src/generated/NotationSettingsSerializer.ts +++ b/src/generated/NotationSettingsSerializer.ts @@ -21,23 +21,23 @@ export class NotationSettingsSerializer { return null; } const o = new Map<string, unknown>(); - o.set("notationMode", obj.notationMode as number); - o.set("fingeringMode", obj.fingeringMode as number); + o.set("notationmode", obj.notationMode as number); + o.set("fingeringmode", obj.fingeringMode as number); { const m = new Map<string, unknown>(); o.set("elements", m); - for (const [k, v] of obj.elements) { + for (const [k, v] of obj.elements!) { m.set(k.toString(), v); } } - o.set("rhythmMode", obj.rhythmMode as number); - o.set("rhythmHeight", obj.rhythmHeight); - o.set("transpositionPitches", obj.transpositionPitches); - o.set("displayTranspositionPitches", obj.displayTranspositionPitches); - o.set("smallGraceTabNotes", obj.smallGraceTabNotes); - o.set("extendBendArrowsOnTiedNotes", obj.extendBendArrowsOnTiedNotes); - o.set("extendLineEffectsToBeatEnd", obj.extendLineEffectsToBeatEnd); - o.set("slurHeight", obj.slurHeight); + o.set("rhythmmode", obj.rhythmMode as number); + o.set("rhythmheight", obj.rhythmHeight); + o.set("transpositionpitches", obj.transpositionPitches); + o.set("displaytranspositionpitches", obj.displayTranspositionPitches); + o.set("smallgracetabnotes", obj.smallGraceTabNotes); + o.set("extendbendarrowsontiednotes", obj.extendBendArrowsOnTiedNotes); + o.set("extendlineeffectstobeatend", obj.extendLineEffectsToBeatEnd); + o.set("slurheight", obj.slurHeight); return o; } public static setProperty(obj: NotationSettings, property: string, v: unknown): boolean { diff --git a/src/generated/PlayerSettingsSerializer.ts b/src/generated/PlayerSettingsSerializer.ts index 87944ee49..54d338ebe 100644 --- a/src/generated/PlayerSettingsSerializer.ts +++ b/src/generated/PlayerSettingsSerializer.ts @@ -20,24 +20,24 @@ export class PlayerSettingsSerializer { return null; } const o = new Map<string, unknown>(); - o.set("soundFont", obj.soundFont); - o.set("scrollElement", obj.scrollElement); - o.set("enablePlayer", obj.enablePlayer); - o.set("enableCursor", obj.enableCursor); - o.set("enableAnimatedBeatCursor", obj.enableAnimatedBeatCursor); - o.set("enableElementHighlighting", obj.enableElementHighlighting); - o.set("enableUserInteraction", obj.enableUserInteraction); - o.set("scrollOffsetX", obj.scrollOffsetX); - o.set("scrollOffsetY", obj.scrollOffsetY); - o.set("scrollMode", obj.scrollMode as number); - o.set("scrollSpeed", obj.scrollSpeed); + o.set("soundfont", obj.soundFont); + o.set("scrollelement", obj.scrollElement); + o.set("enableplayer", obj.enablePlayer); + o.set("enablecursor", obj.enableCursor); + o.set("enableanimatedbeatcursor", obj.enableAnimatedBeatCursor); + o.set("enableelementhighlighting", obj.enableElementHighlighting); + o.set("enableuserinteraction", obj.enableUserInteraction); + o.set("scrolloffsetx", obj.scrollOffsetX); + o.set("scrolloffsety", obj.scrollOffsetY); + o.set("scrollmode", obj.scrollMode as number); + o.set("scrollspeed", obj.scrollSpeed); /*@target web*/ - o.set("nativeBrowserSmoothScroll", obj.nativeBrowserSmoothScroll); - o.set("songBookBendDuration", obj.songBookBendDuration); - o.set("songBookDipDuration", obj.songBookDipDuration); + o.set("nativebrowsersmoothscroll", obj.nativeBrowserSmoothScroll); + o.set("songbookbendduration", obj.songBookBendDuration); + o.set("songbookdipduration", obj.songBookDipDuration); o.set("vibrato", VibratoPlaybackSettingsSerializer.toJson(obj.vibrato)); o.set("slide", SlidePlaybackSettingsSerializer.toJson(obj.slide)); - o.set("playTripletFeel", obj.playTripletFeel); + o.set("playtripletfeel", obj.playTripletFeel); return o; } public static setProperty(obj: PlayerSettings, property: string, v: unknown): boolean { diff --git a/src/generated/RenderingResourcesSerializer.ts b/src/generated/RenderingResourcesSerializer.ts index e7bb535d8..fb0bed86e 100644 --- a/src/generated/RenderingResourcesSerializer.ts +++ b/src/generated/RenderingResourcesSerializer.ts @@ -19,23 +19,23 @@ export class RenderingResourcesSerializer { return null; } const o = new Map<string, unknown>(); - o.set("copyrightFont", Font.toJson(obj.copyrightFont)); - o.set("titleFont", Font.toJson(obj.titleFont)); - o.set("subTitleFont", Font.toJson(obj.subTitleFont)); - o.set("wordsFont", Font.toJson(obj.wordsFont)); - o.set("effectFont", Font.toJson(obj.effectFont)); - o.set("fretboardNumberFont", Font.toJson(obj.fretboardNumberFont)); - o.set("tablatureFont", Font.toJson(obj.tablatureFont)); - o.set("graceFont", Font.toJson(obj.graceFont)); - o.set("staffLineColor", Color.toJson(obj.staffLineColor)); - o.set("barSeparatorColor", Color.toJson(obj.barSeparatorColor)); - o.set("barNumberFont", Font.toJson(obj.barNumberFont)); - o.set("barNumberColor", Color.toJson(obj.barNumberColor)); - o.set("fingeringFont", Font.toJson(obj.fingeringFont)); - o.set("markerFont", Font.toJson(obj.markerFont)); - o.set("mainGlyphColor", Color.toJson(obj.mainGlyphColor)); - o.set("secondaryGlyphColor", Color.toJson(obj.secondaryGlyphColor)); - o.set("scoreInfoColor", Color.toJson(obj.scoreInfoColor)); + o.set("copyrightfont", Font.toJson(obj.copyrightFont)); + o.set("titlefont", Font.toJson(obj.titleFont)); + o.set("subtitlefont", Font.toJson(obj.subTitleFont)); + o.set("wordsfont", Font.toJson(obj.wordsFont)); + o.set("effectfont", Font.toJson(obj.effectFont)); + o.set("fretboardnumberfont", Font.toJson(obj.fretboardNumberFont)); + o.set("tablaturefont", Font.toJson(obj.tablatureFont)); + o.set("gracefont", Font.toJson(obj.graceFont)); + o.set("stafflinecolor", Color.toJson(obj.staffLineColor)); + o.set("barseparatorcolor", Color.toJson(obj.barSeparatorColor)); + o.set("barnumberfont", Font.toJson(obj.barNumberFont)); + o.set("barnumbercolor", Color.toJson(obj.barNumberColor)); + o.set("fingeringfont", Font.toJson(obj.fingeringFont)); + o.set("markerfont", Font.toJson(obj.markerFont)); + o.set("mainglyphcolor", Color.toJson(obj.mainGlyphColor)); + o.set("secondaryglyphcolor", Color.toJson(obj.secondaryGlyphColor)); + o.set("scoreinfocolor", Color.toJson(obj.scoreInfoColor)); return o; } public static setProperty(obj: RenderingResources, property: string, v: unknown): boolean { diff --git a/src/generated/SlidePlaybackSettingsSerializer.ts b/src/generated/SlidePlaybackSettingsSerializer.ts index 5c32ce414..d2cd24c86 100644 --- a/src/generated/SlidePlaybackSettingsSerializer.ts +++ b/src/generated/SlidePlaybackSettingsSerializer.ts @@ -17,9 +17,9 @@ export class SlidePlaybackSettingsSerializer { return null; } const o = new Map<string, unknown>(); - o.set("simpleSlidePitchOffset", obj.simpleSlidePitchOffset); - o.set("simpleSlideDurationRatio", obj.simpleSlideDurationRatio); - o.set("shiftSlideDurationRatio", obj.shiftSlideDurationRatio); + o.set("simpleslidepitchoffset", obj.simpleSlidePitchOffset); + o.set("simpleslidedurationratio", obj.simpleSlideDurationRatio); + o.set("shiftslidedurationratio", obj.shiftSlideDurationRatio); return o; } public static setProperty(obj: SlidePlaybackSettings, property: string, v: unknown): boolean { diff --git a/src/generated/VibratoPlaybackSettingsSerializer.ts b/src/generated/VibratoPlaybackSettingsSerializer.ts index 092c24d4a..f623538f4 100644 --- a/src/generated/VibratoPlaybackSettingsSerializer.ts +++ b/src/generated/VibratoPlaybackSettingsSerializer.ts @@ -17,14 +17,14 @@ export class VibratoPlaybackSettingsSerializer { return null; } const o = new Map<string, unknown>(); - o.set("noteWideLength", obj.noteWideLength); - o.set("noteWideAmplitude", obj.noteWideAmplitude); - o.set("noteSlightLength", obj.noteSlightLength); - o.set("noteSlightAmplitude", obj.noteSlightAmplitude); - o.set("beatWideLength", obj.beatWideLength); - o.set("beatWideAmplitude", obj.beatWideAmplitude); - o.set("beatSlightLength", obj.beatSlightLength); - o.set("beatSlightAmplitude", obj.beatSlightAmplitude); + o.set("notewidelength", obj.noteWideLength); + o.set("notewideamplitude", obj.noteWideAmplitude); + o.set("noteslightlength", obj.noteSlightLength); + o.set("noteslightamplitude", obj.noteSlightAmplitude); + o.set("beatwidelength", obj.beatWideLength); + o.set("beatwideamplitude", obj.beatWideAmplitude); + o.set("beatslightlength", obj.beatSlightLength); + o.set("beatslightamplitude", obj.beatSlightAmplitude); return o; } public static setProperty(obj: VibratoPlaybackSettings, property: string, v: unknown): boolean { diff --git a/src/generated/model/AutomationSerializer.ts b/src/generated/model/AutomationSerializer.ts index 46983de67..8c2f04fa9 100644 --- a/src/generated/model/AutomationSerializer.ts +++ b/src/generated/model/AutomationSerializer.ts @@ -11,17 +11,17 @@ export class AutomationSerializer { if (!m) { return; } - JsonHelper.forEach(m, (v, k) => this.setProperty(obj, k.toLowerCase(), v)); + JsonHelper.forEach(m, (v, k) => this.setProperty(obj, k, v)); } public static toJson(obj: Automation | null): Map<string, unknown> | null { if (!obj) { return null; } const o = new Map<string, unknown>(); - o.set("isLinear", obj.isLinear); + o.set("islinear", obj.isLinear); o.set("type", obj.type as number); o.set("value", obj.value); - o.set("ratioPosition", obj.ratioPosition); + o.set("ratioposition", obj.ratioPosition); o.set("text", obj.text); return o; } diff --git a/src/generated/model/BarSerializer.ts b/src/generated/model/BarSerializer.ts index 5773b2549..0f7a88fa5 100644 --- a/src/generated/model/BarSerializer.ts +++ b/src/generated/model/BarSerializer.ts @@ -15,7 +15,7 @@ export class BarSerializer { if (!m) { return; } - JsonHelper.forEach(m, (v, k) => this.setProperty(obj, k.toLowerCase(), v)); + JsonHelper.forEach(m, (v, k) => this.setProperty(obj, k, v)); } public static toJson(obj: Bar | null): Map<string, unknown> | null { if (!obj) { @@ -24,9 +24,9 @@ export class BarSerializer { const o = new Map<string, unknown>(); o.set("id", obj.id); o.set("clef", obj.clef as number); - o.set("clefOttava", obj.clefOttava as number); + o.set("clefottava", obj.clefOttava as number); o.set("voices", obj.voices.map(i => VoiceSerializer.toJson(i))); - o.set("simileMark", obj.simileMark as number); + o.set("similemark", obj.simileMark as number); return o; } public static setProperty(obj: Bar, property: string, v: unknown): boolean { @@ -42,9 +42,9 @@ export class BarSerializer { return true; case "voices": obj.voices = []; - for (const o of v as (Map<string, unknown> | null)[]) { + for (const o of (v as (Map<string, unknown> | null)[])) { const i = new Voice(); - VoiceSerializer.fromJson(i, o) + VoiceSerializer.fromJson(i, o); obj.addVoice(i); } return true; diff --git a/src/generated/model/BeatCloner.ts b/src/generated/model/BeatCloner.ts index f9396dbb8..e01487f59 100644 --- a/src/generated/model/BeatCloner.ts +++ b/src/generated/model/BeatCloner.ts @@ -12,7 +12,7 @@ export class BeatCloner { const clone = new Beat(); clone.index = original.index; clone.notes = []; - for (const i of original.notes) { + for (const i of original.notes!) { clone.addNote(NoteCloner.clone(i)); } clone.isEmpty = original.isEmpty; @@ -23,7 +23,7 @@ export class BeatCloner { clone.isLetRing = original.isLetRing; clone.isPalmMute = original.isPalmMute; clone.automations = []; - for (const i of original.automations) { + for (const i of original.automations!) { clone.automations.push(AutomationCloner.clone(i)); } clone.dots = original.dots; @@ -40,9 +40,11 @@ export class BeatCloner { clone.tupletNumerator = original.tupletNumerator; clone.isContinuedWhammy = original.isContinuedWhammy; clone.whammyBarType = original.whammyBarType; - clone.whammyBarPoints = []; - for (const i of original.whammyBarPoints) { - clone.addWhammyBarPoint(BendPointCloner.clone(i)); + if (original.whammyBarPoints) { + clone.whammyBarPoints = []; + for (const i of original.whammyBarPoints!) { + clone.addWhammyBarPoint(BendPointCloner.clone(i)); + } } clone.vibrato = original.vibrato; clone.chordId = original.chordId; diff --git a/src/generated/model/BeatSerializer.ts b/src/generated/model/BeatSerializer.ts index 1c31d658f..468a090ad 100644 --- a/src/generated/model/BeatSerializer.ts +++ b/src/generated/model/BeatSerializer.ts @@ -28,7 +28,7 @@ export class BeatSerializer { if (!m) { return; } - JsonHelper.forEach(m, (v, k) => this.setProperty(obj, k.toLowerCase(), v)); + JsonHelper.forEach(m, (v, k) => this.setProperty(obj, k, v)); } public static toJson(obj: Beat | null): Map<string, unknown> | null { if (!obj) { @@ -37,41 +37,43 @@ export class BeatSerializer { const o = new Map<string, unknown>(); o.set("id", obj.id); o.set("notes", obj.notes.map(i => NoteSerializer.toJson(i))); - o.set("isEmpty", obj.isEmpty); - o.set("whammyStyle", obj.whammyStyle as number); + o.set("isempty", obj.isEmpty); + o.set("whammystyle", obj.whammyStyle as number); o.set("ottava", obj.ottava as number); - o.set("isLegatoOrigin", obj.isLegatoOrigin); + o.set("islegatoorigin", obj.isLegatoOrigin); o.set("duration", obj.duration as number); o.set("automations", obj.automations.map(i => AutomationSerializer.toJson(i))); o.set("dots", obj.dots); - o.set("fadeIn", obj.fadeIn); + o.set("fadein", obj.fadeIn); o.set("lyrics", obj.lyrics); - o.set("hasRasgueado", obj.hasRasgueado); + o.set("hasrasgueado", obj.hasRasgueado); o.set("pop", obj.pop); o.set("slap", obj.slap); o.set("tap", obj.tap); o.set("text", obj.text); - o.set("brushType", obj.brushType as number); - o.set("brushDuration", obj.brushDuration); - o.set("tupletDenominator", obj.tupletDenominator); - o.set("tupletNumerator", obj.tupletNumerator); - o.set("isContinuedWhammy", obj.isContinuedWhammy); - o.set("whammyBarType", obj.whammyBarType as number); - o.set("whammyBarPoints", obj.whammyBarPoints.map(i => BendPointSerializer.toJson(i))); + o.set("brushtype", obj.brushType as number); + o.set("brushduration", obj.brushDuration); + o.set("tupletdenominator", obj.tupletDenominator); + o.set("tupletnumerator", obj.tupletNumerator); + o.set("iscontinuedwhammy", obj.isContinuedWhammy); + o.set("whammybartype", obj.whammyBarType as number); + if (obj.whammyBarPoints !== null) { + o.set("whammybarpoints", obj.whammyBarPoints?.map(i => BendPointSerializer.toJson(i))); + } o.set("vibrato", obj.vibrato as number); - o.set("chordId", obj.chordId); - o.set("graceType", obj.graceType as number); - o.set("pickStroke", obj.pickStroke as number); - o.set("tremoloSpeed", obj.tremoloSpeed as number | null); + o.set("chordid", obj.chordId); + o.set("gracetype", obj.graceType as number); + o.set("pickstroke", obj.pickStroke as number); + o.set("tremolospeed", obj.tremoloSpeed as number | null); o.set("crescendo", obj.crescendo as number); - o.set("displayStart", obj.displayStart); - o.set("playbackStart", obj.playbackStart); - o.set("displayDuration", obj.displayDuration); - o.set("playbackDuration", obj.playbackDuration); + o.set("displaystart", obj.displayStart); + o.set("playbackstart", obj.playbackStart); + o.set("displayduration", obj.displayDuration); + o.set("playbackduration", obj.playbackDuration); o.set("dynamics", obj.dynamics as number); - o.set("invertBeamDirection", obj.invertBeamDirection); - o.set("preferredBeamDirection", obj.preferredBeamDirection as number | null); - o.set("beamingMode", obj.beamingMode as number); + o.set("invertbeamdirection", obj.invertBeamDirection); + o.set("preferredbeamdirection", obj.preferredBeamDirection as number | null); + o.set("beamingmode", obj.beamingMode as number); return o; } public static setProperty(obj: Beat, property: string, v: unknown): boolean { @@ -81,9 +83,9 @@ export class BeatSerializer { return true; case "notes": obj.notes = []; - for (const o of v as (Map<string, unknown> | null)[]) { + for (const o of (v as (Map<string, unknown> | null)[])) { const i = new Note(); - NoteSerializer.fromJson(i, o) + NoteSerializer.fromJson(i, o); obj.addNote(i); } return true; @@ -104,9 +106,9 @@ export class BeatSerializer { return true; case "automations": obj.automations = []; - for (const o of v as (Map<string, unknown> | null)[]) { + for (const o of (v as (Map<string, unknown> | null)[])) { const i = new Automation(); - AutomationSerializer.fromJson(i, o) + AutomationSerializer.fromJson(i, o); obj.automations.push(i); } return true; @@ -153,11 +155,13 @@ export class BeatSerializer { obj.whammyBarType = JsonHelper.parseEnum<WhammyType>(v, WhammyType)!; return true; case "whammybarpoints": - obj.whammyBarPoints = []; - for (const o of v as (Map<string, unknown> | null)[]) { - const i = new BendPoint(); - BendPointSerializer.fromJson(i, o) - obj.addWhammyBarPoint(i); + if (v) { + obj.whammyBarPoints = []; + for (const o of (v as (Map<string, unknown> | null)[])) { + const i = new BendPoint(); + BendPointSerializer.fromJson(i, o); + obj.addWhammyBarPoint(i); + } } return true; case "vibrato": diff --git a/src/generated/model/BendPointSerializer.ts b/src/generated/model/BendPointSerializer.ts index cb27f39d3..4d08a2af4 100644 --- a/src/generated/model/BendPointSerializer.ts +++ b/src/generated/model/BendPointSerializer.ts @@ -10,7 +10,7 @@ export class BendPointSerializer { if (!m) { return; } - JsonHelper.forEach(m, (v, k) => this.setProperty(obj, k.toLowerCase(), v)); + JsonHelper.forEach(m, (v, k) => this.setProperty(obj, k, v)); } public static toJson(obj: BendPoint | null): Map<string, unknown> | null { if (!obj) { diff --git a/src/generated/model/ChordSerializer.ts b/src/generated/model/ChordSerializer.ts index bfce0f51b..39da50893 100644 --- a/src/generated/model/ChordSerializer.ts +++ b/src/generated/model/ChordSerializer.ts @@ -10,7 +10,7 @@ export class ChordSerializer { if (!m) { return; } - JsonHelper.forEach(m, (v, k) => this.setProperty(obj, k.toLowerCase(), v)); + JsonHelper.forEach(m, (v, k) => this.setProperty(obj, k, v)); } public static toJson(obj: Chord | null): Map<string, unknown> | null { if (!obj) { @@ -18,12 +18,12 @@ export class ChordSerializer { } const o = new Map<string, unknown>(); o.set("name", obj.name); - o.set("firstFret", obj.firstFret); + o.set("firstfret", obj.firstFret); o.set("strings", obj.strings); - o.set("barreFrets", obj.barreFrets); - o.set("showName", obj.showName); - o.set("showDiagram", obj.showDiagram); - o.set("showFingering", obj.showFingering); + o.set("barrefrets", obj.barreFrets); + o.set("showname", obj.showName); + o.set("showdiagram", obj.showDiagram); + o.set("showfingering", obj.showFingering); return o; } public static setProperty(obj: Chord, property: string, v: unknown): boolean { diff --git a/src/generated/model/FermataSerializer.ts b/src/generated/model/FermataSerializer.ts index e4335acb6..904824fec 100644 --- a/src/generated/model/FermataSerializer.ts +++ b/src/generated/model/FermataSerializer.ts @@ -11,7 +11,7 @@ export class FermataSerializer { if (!m) { return; } - JsonHelper.forEach(m, (v, k) => this.setProperty(obj, k.toLowerCase(), v)); + JsonHelper.forEach(m, (v, k) => this.setProperty(obj, k, v)); } public static toJson(obj: Fermata | null): Map<string, unknown> | null { if (!obj) { diff --git a/src/generated/model/InstrumentArticulationSerializer.ts b/src/generated/model/InstrumentArticulationSerializer.ts index 8aa3863c2..f0d67f047 100644 --- a/src/generated/model/InstrumentArticulationSerializer.ts +++ b/src/generated/model/InstrumentArticulationSerializer.ts @@ -12,21 +12,21 @@ export class InstrumentArticulationSerializer { if (!m) { return; } - JsonHelper.forEach(m, (v, k) => this.setProperty(obj, k.toLowerCase(), v)); + JsonHelper.forEach(m, (v, k) => this.setProperty(obj, k, v)); } public static toJson(obj: InstrumentArticulation | null): Map<string, unknown> | null { if (!obj) { return null; } const o = new Map<string, unknown>(); - o.set("elementType", obj.elementType); - o.set("staffLine", obj.staffLine); - o.set("noteHeadDefault", obj.noteHeadDefault as number); - o.set("noteHeadHalf", obj.noteHeadHalf as number); - o.set("noteHeadWhole", obj.noteHeadWhole as number); - o.set("techniqueSymbol", obj.techniqueSymbol as number); - o.set("techniqueSymbolPlacement", obj.techniqueSymbolPlacement as number); - o.set("outputMidiNumber", obj.outputMidiNumber); + o.set("elementtype", obj.elementType); + o.set("staffline", obj.staffLine); + o.set("noteheaddefault", obj.noteHeadDefault as number); + o.set("noteheadhalf", obj.noteHeadHalf as number); + o.set("noteheadwhole", obj.noteHeadWhole as number); + o.set("techniquesymbol", obj.techniqueSymbol as number); + o.set("techniquesymbolplacement", obj.techniqueSymbolPlacement as number); + o.set("outputmidinumber", obj.outputMidiNumber); return o; } public static setProperty(obj: InstrumentArticulation, property: string, v: unknown): boolean { diff --git a/src/generated/model/MasterBarSerializer.ts b/src/generated/model/MasterBarSerializer.ts index c93a8f894..92490a0c1 100644 --- a/src/generated/model/MasterBarSerializer.ts +++ b/src/generated/model/MasterBarSerializer.ts @@ -19,34 +19,34 @@ export class MasterBarSerializer { if (!m) { return; } - JsonHelper.forEach(m, (v, k) => this.setProperty(obj, k.toLowerCase(), v)); + JsonHelper.forEach(m, (v, k) => this.setProperty(obj, k, v)); } public static toJson(obj: MasterBar | null): Map<string, unknown> | null { if (!obj) { return null; } const o = new Map<string, unknown>(); - o.set("alternateEndings", obj.alternateEndings); - o.set("keySignature", obj.keySignature as number); - o.set("keySignatureType", obj.keySignatureType as number); - o.set("isDoubleBar", obj.isDoubleBar); - o.set("isRepeatStart", obj.isRepeatStart); - o.set("repeatCount", obj.repeatCount); - o.set("timeSignatureNumerator", obj.timeSignatureNumerator); - o.set("timeSignatureDenominator", obj.timeSignatureDenominator); - o.set("timeSignatureCommon", obj.timeSignatureCommon); - o.set("tripletFeel", obj.tripletFeel as number); + o.set("alternateendings", obj.alternateEndings); + o.set("keysignature", obj.keySignature as number); + o.set("keysignaturetype", obj.keySignatureType as number); + o.set("isdoublebar", obj.isDoubleBar); + o.set("isrepeatstart", obj.isRepeatStart); + o.set("repeatcount", obj.repeatCount); + o.set("timesignaturenumerator", obj.timeSignatureNumerator); + o.set("timesignaturedenominator", obj.timeSignatureDenominator); + o.set("timesignaturecommon", obj.timeSignatureCommon); + o.set("tripletfeel", obj.tripletFeel as number); o.set("section", SectionSerializer.toJson(obj.section)); - o.set("tempoAutomation", AutomationSerializer.toJson(obj.tempoAutomation)); - { + o.set("tempoautomation", AutomationSerializer.toJson(obj.tempoAutomation)); + if (obj.fermata !== null) { const m = new Map<string, unknown>(); o.set("fermata", m); - for (const [k, v] of obj.fermata) { + for (const [k, v] of obj.fermata!) { m.set(k.toString(), FermataSerializer.toJson(v)); } } o.set("start", obj.start); - o.set("isAnacrusis", obj.isAnacrusis); + o.set("isanacrusis", obj.isAnacrusis); return o; } public static setProperty(obj: MasterBar, property: string, v: unknown): boolean { @@ -86,7 +86,7 @@ export class MasterBarSerializer { JsonHelper.forEach(v, (v, k) => { const i = new Fermata(); FermataSerializer.fromJson(i, v as Map<string, unknown>); - obj.fermata.set(parseInt(k), i); + obj.addFermata(parseInt(k), i); }); return true; case "start": diff --git a/src/generated/model/NoteCloner.ts b/src/generated/model/NoteCloner.ts index 8cc7eb248..889383993 100644 --- a/src/generated/model/NoteCloner.ts +++ b/src/generated/model/NoteCloner.ts @@ -13,9 +13,11 @@ export class NoteCloner { clone.bendType = original.bendType; clone.bendStyle = original.bendStyle; clone.isContinuedBend = original.isContinuedBend; - clone.bendPoints = []; - for (const i of original.bendPoints) { - clone.addBendPoint(BendPointCloner.clone(i)); + if (original.bendPoints) { + clone.bendPoints = []; + for (const i of original.bendPoints!) { + clone.addBendPoint(BendPointCloner.clone(i)); + } } clone.fret = original.fret; clone.string = original.string; @@ -25,11 +27,7 @@ export class NoteCloner { clone.isVisible = original.isVisible; clone.isLeftHandTapped = original.isLeftHandTapped; clone.isHammerPullOrigin = original.isHammerPullOrigin; - clone.hammerPullOriginNoteId = original.hammerPullOriginNoteId; - clone.hammerPullDestinationNoteId = original.hammerPullDestinationNoteId; clone.isSlurDestination = original.isSlurDestination; - clone.slurOriginNoteId = original.slurOriginNoteId; - clone.slurDestinationNoteId = original.slurDestinationNoteId; clone.harmonicType = original.harmonicType; clone.harmonicValue = original.harmonicValue; clone.isGhost = original.isGhost; @@ -40,8 +38,6 @@ export class NoteCloner { clone.slideInType = original.slideInType; clone.slideOutType = original.slideOutType; clone.vibrato = original.vibrato; - clone.tieOriginNoteId = original.tieOriginNoteId; - clone.tieDestinationNoteId = original.tieDestinationNoteId; clone.isTieDestination = original.isTieDestination; clone.leftHandFinger = original.leftHandFinger; clone.rightHandFinger = original.rightHandFinger; diff --git a/src/generated/model/NoteSerializer.ts b/src/generated/model/NoteSerializer.ts index 1e47ffbaf..b5b8f2c6e 100644 --- a/src/generated/model/NoteSerializer.ts +++ b/src/generated/model/NoteSerializer.ts @@ -23,7 +23,7 @@ export class NoteSerializer { if (!m) { return; } - JsonHelper.forEach(m, (v, k) => this.setProperty(obj, k.toLowerCase(), v)); + JsonHelper.forEach(m, (v, k) => this.setProperty(obj, k, v)); } public static toJson(obj: Note | null): Map<string, unknown> | null { if (!obj) { @@ -32,44 +32,41 @@ export class NoteSerializer { const o = new Map<string, unknown>(); o.set("id", obj.id); o.set("accentuated", obj.accentuated as number); - o.set("bendType", obj.bendType as number); - o.set("bendStyle", obj.bendStyle as number); - o.set("isContinuedBend", obj.isContinuedBend); - o.set("bendPoints", obj.bendPoints.map(i => BendPointSerializer.toJson(i))); + o.set("bendtype", obj.bendType as number); + o.set("bendstyle", obj.bendStyle as number); + o.set("iscontinuedbend", obj.isContinuedBend); + if (obj.bendPoints !== null) { + o.set("bendpoints", obj.bendPoints?.map(i => BendPointSerializer.toJson(i))); + } o.set("fret", obj.fret); o.set("string", obj.string); o.set("octave", obj.octave); o.set("tone", obj.tone); - o.set("percussionArticulation", obj.percussionArticulation); - o.set("isVisible", obj.isVisible); - o.set("isLeftHandTapped", obj.isLeftHandTapped); - o.set("isHammerPullOrigin", obj.isHammerPullOrigin); - o.set("hammerPullOriginNoteId", obj.hammerPullOriginNoteId); - o.set("hammerPullDestinationNoteId", obj.hammerPullDestinationNoteId); - o.set("isSlurDestination", obj.isSlurDestination); - o.set("slurOriginNoteId", obj.slurOriginNoteId); - o.set("slurDestinationNoteId", obj.slurDestinationNoteId); - o.set("harmonicType", obj.harmonicType as number); - o.set("harmonicValue", obj.harmonicValue); - o.set("isGhost", obj.isGhost); - o.set("isLetRing", obj.isLetRing); - o.set("isPalmMute", obj.isPalmMute); - o.set("isDead", obj.isDead); - o.set("isStaccato", obj.isStaccato); - o.set("slideInType", obj.slideInType as number); - o.set("slideOutType", obj.slideOutType as number); + o.set("percussionarticulation", obj.percussionArticulation); + o.set("isvisible", obj.isVisible); + o.set("islefthandtapped", obj.isLeftHandTapped); + o.set("ishammerpullorigin", obj.isHammerPullOrigin); + o.set("isslurdestination", obj.isSlurDestination); + o.set("harmonictype", obj.harmonicType as number); + o.set("harmonicvalue", obj.harmonicValue); + o.set("isghost", obj.isGhost); + o.set("isletring", obj.isLetRing); + o.set("ispalmmute", obj.isPalmMute); + o.set("isdead", obj.isDead); + o.set("isstaccato", obj.isStaccato); + o.set("slideintype", obj.slideInType as number); + o.set("slideouttype", obj.slideOutType as number); o.set("vibrato", obj.vibrato as number); - o.set("tieOriginNoteId", obj.tieOriginNoteId); - o.set("tieDestinationNoteId", obj.tieDestinationNoteId); - o.set("isTieDestination", obj.isTieDestination); - o.set("leftHandFinger", obj.leftHandFinger as number); - o.set("rightHandFinger", obj.rightHandFinger as number); - o.set("isFingering", obj.isFingering); - o.set("trillValue", obj.trillValue); - o.set("trillSpeed", obj.trillSpeed as number); - o.set("durationPercent", obj.durationPercent); - o.set("accidentalMode", obj.accidentalMode as number); + o.set("istiedestination", obj.isTieDestination); + o.set("lefthandfinger", obj.leftHandFinger as number); + o.set("righthandfinger", obj.rightHandFinger as number); + o.set("isfingering", obj.isFingering); + o.set("trillvalue", obj.trillValue); + o.set("trillspeed", obj.trillSpeed as number); + o.set("durationpercent", obj.durationPercent); + o.set("accidentalmode", obj.accidentalMode as number); o.set("dynamics", obj.dynamics as number); + obj.toJson(o); return o; } public static setProperty(obj: Note, property: string, v: unknown): boolean { @@ -90,11 +87,13 @@ export class NoteSerializer { obj.isContinuedBend = v! as boolean; return true; case "bendpoints": - obj.bendPoints = []; - for (const o of v as (Map<string, unknown> | null)[]) { - const i = new BendPoint(); - BendPointSerializer.fromJson(i, o) - obj.addBendPoint(i); + if (v) { + obj.bendPoints = []; + for (const o of (v as (Map<string, unknown> | null)[])) { + const i = new BendPoint(); + BendPointSerializer.fromJson(i, o); + obj.addBendPoint(i); + } } return true; case "fret": @@ -121,21 +120,9 @@ export class NoteSerializer { case "ishammerpullorigin": obj.isHammerPullOrigin = v! as boolean; return true; - case "hammerpulloriginnoteid": - obj.hammerPullOriginNoteId = v! as number; - return true; - case "hammerpulldestinationnoteid": - obj.hammerPullDestinationNoteId = v! as number; - return true; case "isslurdestination": obj.isSlurDestination = v! as boolean; return true; - case "sluroriginnoteid": - obj.slurOriginNoteId = v! as number; - return true; - case "slurdestinationnoteid": - obj.slurDestinationNoteId = v! as number; - return true; case "harmonictype": obj.harmonicType = JsonHelper.parseEnum<HarmonicType>(v, HarmonicType)!; return true; @@ -166,12 +153,6 @@ export class NoteSerializer { case "vibrato": obj.vibrato = JsonHelper.parseEnum<VibratoType>(v, VibratoType)!; return true; - case "tieoriginnoteid": - obj.tieOriginNoteId = v! as number; - return true; - case "tiedestinationnoteid": - obj.tieDestinationNoteId = v! as number; - return true; case "istiedestination": obj.isTieDestination = v! as boolean; return true; @@ -200,7 +181,7 @@ export class NoteSerializer { obj.dynamics = JsonHelper.parseEnum<DynamicValue>(v, DynamicValue)!; return true; } - return false; + return obj.setProperty(property, v); } } diff --git a/src/generated/model/PlaybackInformationSerializer.ts b/src/generated/model/PlaybackInformationSerializer.ts index c80226b3b..62f9ab5cc 100644 --- a/src/generated/model/PlaybackInformationSerializer.ts +++ b/src/generated/model/PlaybackInformationSerializer.ts @@ -10,7 +10,7 @@ export class PlaybackInformationSerializer { if (!m) { return; } - JsonHelper.forEach(m, (v, k) => this.setProperty(obj, k.toLowerCase(), v)); + JsonHelper.forEach(m, (v, k) => this.setProperty(obj, k, v)); } public static toJson(obj: PlaybackInformation | null): Map<string, unknown> | null { if (!obj) { @@ -21,10 +21,10 @@ export class PlaybackInformationSerializer { o.set("balance", obj.balance); o.set("port", obj.port); o.set("program", obj.program); - o.set("primaryChannel", obj.primaryChannel); - o.set("secondaryChannel", obj.secondaryChannel); - o.set("isMute", obj.isMute); - o.set("isSolo", obj.isSolo); + o.set("primarychannel", obj.primaryChannel); + o.set("secondarychannel", obj.secondaryChannel); + o.set("ismute", obj.isMute); + o.set("issolo", obj.isSolo); return o; } public static setProperty(obj: PlaybackInformation, property: string, v: unknown): boolean { diff --git a/src/generated/model/RenderStylesheetSerializer.ts b/src/generated/model/RenderStylesheetSerializer.ts index c233a68e8..2132b1829 100644 --- a/src/generated/model/RenderStylesheetSerializer.ts +++ b/src/generated/model/RenderStylesheetSerializer.ts @@ -10,14 +10,14 @@ export class RenderStylesheetSerializer { if (!m) { return; } - JsonHelper.forEach(m, (v, k) => this.setProperty(obj, k.toLowerCase(), v)); + JsonHelper.forEach(m, (v, k) => this.setProperty(obj, k, v)); } public static toJson(obj: RenderStylesheet | null): Map<string, unknown> | null { if (!obj) { return null; } const o = new Map<string, unknown>(); - o.set("hideDynamics", obj.hideDynamics); + o.set("hidedynamics", obj.hideDynamics); return o; } public static setProperty(obj: RenderStylesheet, property: string, v: unknown): boolean { diff --git a/src/generated/model/ScoreSerializer.ts b/src/generated/model/ScoreSerializer.ts index ee67ddaf0..df30c54a5 100644 --- a/src/generated/model/ScoreSerializer.ts +++ b/src/generated/model/ScoreSerializer.ts @@ -15,7 +15,7 @@ export class ScoreSerializer { if (!m) { return; } - JsonHelper.forEach(m, (v, k) => this.setProperty(obj, k.toLowerCase(), v)); + JsonHelper.forEach(m, (v, k) => this.setProperty(obj, k, v)); } public static toJson(obj: Score | null): Map<string, unknown> | null { if (!obj) { @@ -28,13 +28,13 @@ export class ScoreSerializer { o.set("instructions", obj.instructions); o.set("music", obj.music); o.set("notices", obj.notices); - o.set("subTitle", obj.subTitle); + o.set("subtitle", obj.subTitle); o.set("title", obj.title); o.set("words", obj.words); o.set("tab", obj.tab); o.set("tempo", obj.tempo); - o.set("tempoLabel", obj.tempoLabel); - o.set("masterBars", obj.masterBars.map(i => MasterBarSerializer.toJson(i))); + o.set("tempolabel", obj.tempoLabel); + o.set("masterbars", obj.masterBars.map(i => MasterBarSerializer.toJson(i))); o.set("tracks", obj.tracks.map(i => TrackSerializer.toJson(i))); o.set("stylesheet", RenderStylesheetSerializer.toJson(obj.stylesheet)); return o; @@ -79,17 +79,17 @@ export class ScoreSerializer { return true; case "masterbars": obj.masterBars = []; - for (const o of v as (Map<string, unknown> | null)[]) { + for (const o of (v as (Map<string, unknown> | null)[])) { const i = new MasterBar(); - MasterBarSerializer.fromJson(i, o) + MasterBarSerializer.fromJson(i, o); obj.addMasterBar(i); } return true; case "tracks": obj.tracks = []; - for (const o of v as (Map<string, unknown> | null)[]) { + for (const o of (v as (Map<string, unknown> | null)[])) { const i = new Track(); - TrackSerializer.fromJson(i, o) + TrackSerializer.fromJson(i, o); obj.addTrack(i); } return true; diff --git a/src/generated/model/SectionSerializer.ts b/src/generated/model/SectionSerializer.ts index 789025096..5ccd55603 100644 --- a/src/generated/model/SectionSerializer.ts +++ b/src/generated/model/SectionSerializer.ts @@ -10,7 +10,7 @@ export class SectionSerializer { if (!m) { return; } - JsonHelper.forEach(m, (v, k) => this.setProperty(obj, k.toLowerCase(), v)); + JsonHelper.forEach(m, (v, k) => this.setProperty(obj, k, v)); } public static toJson(obj: Section | null): Map<string, unknown> | null { if (!obj) { diff --git a/src/generated/model/StaffSerializer.ts b/src/generated/model/StaffSerializer.ts index d3ccfbb53..40a3280b0 100644 --- a/src/generated/model/StaffSerializer.ts +++ b/src/generated/model/StaffSerializer.ts @@ -15,7 +15,7 @@ export class StaffSerializer { if (!m) { return; } - JsonHelper.forEach(m, (v, k) => this.setProperty(obj, k.toLowerCase(), v)); + JsonHelper.forEach(m, (v, k) => this.setProperty(obj, k, v)); } public static toJson(obj: Staff | null): Map<string, unknown> | null { if (!obj) { @@ -23,30 +23,30 @@ export class StaffSerializer { } const o = new Map<string, unknown>(); o.set("bars", obj.bars.map(i => BarSerializer.toJson(i))); - { + if (obj.chords !== null) { const m = new Map<string, unknown>(); o.set("chords", m); - for (const [k, v] of obj.chords) { + for (const [k, v] of obj.chords!) { m.set(k.toString(), ChordSerializer.toJson(v)); } } o.set("capo", obj.capo); - o.set("transpositionPitch", obj.transpositionPitch); - o.set("displayTranspositionPitch", obj.displayTranspositionPitch); - o.set("stringTuning", TuningSerializer.toJson(obj.stringTuning)); - o.set("showTablature", obj.showTablature); - o.set("showStandardNotation", obj.showStandardNotation); - o.set("isPercussion", obj.isPercussion); - o.set("standardNotationLineCount", obj.standardNotationLineCount); + o.set("transpositionpitch", obj.transpositionPitch); + o.set("displaytranspositionpitch", obj.displayTranspositionPitch); + o.set("stringtuning", TuningSerializer.toJson(obj.stringTuning)); + o.set("showtablature", obj.showTablature); + o.set("showstandardnotation", obj.showStandardNotation); + o.set("ispercussion", obj.isPercussion); + o.set("standardnotationlinecount", obj.standardNotationLineCount); return o; } public static setProperty(obj: Staff, property: string, v: unknown): boolean { switch (property) { case "bars": obj.bars = []; - for (const o of v as (Map<string, unknown> | null)[]) { + for (const o of (v as (Map<string, unknown> | null)[])) { const i = new Bar(); - BarSerializer.fromJson(i, o) + BarSerializer.fromJson(i, o); obj.addBar(i); } return true; diff --git a/src/generated/model/TrackSerializer.ts b/src/generated/model/TrackSerializer.ts index eaeadd9e2..b4e820398 100644 --- a/src/generated/model/TrackSerializer.ts +++ b/src/generated/model/TrackSerializer.ts @@ -16,7 +16,7 @@ export class TrackSerializer { if (!m) { return; } - JsonHelper.forEach(m, (v, k) => this.setProperty(obj, k.toLowerCase(), v)); + JsonHelper.forEach(m, (v, k) => this.setProperty(obj, k, v)); } public static toJson(obj: Track | null): Map<string, unknown> | null { if (!obj) { @@ -24,20 +24,20 @@ export class TrackSerializer { } const o = new Map<string, unknown>(); o.set("staves", obj.staves.map(i => StaffSerializer.toJson(i))); - o.set("playbackInfo", PlaybackInformationSerializer.toJson(obj.playbackInfo)); + o.set("playbackinfo", PlaybackInformationSerializer.toJson(obj.playbackInfo)); o.set("color", Color.toJson(obj.color)); o.set("name", obj.name); - o.set("shortName", obj.shortName); - o.set("percussionArticulations", obj.percussionArticulations.map(i => InstrumentArticulationSerializer.toJson(i))); + o.set("shortname", obj.shortName); + o.set("percussionarticulations", obj.percussionArticulations.map(i => InstrumentArticulationSerializer.toJson(i))); return o; } public static setProperty(obj: Track, property: string, v: unknown): boolean { switch (property) { case "staves": obj.staves = []; - for (const o of v as (Map<string, unknown> | null)[]) { + for (const o of (v as (Map<string, unknown> | null)[])) { const i = new Staff(); - StaffSerializer.fromJson(i, o) + StaffSerializer.fromJson(i, o); obj.addStaff(i); } return true; @@ -52,9 +52,9 @@ export class TrackSerializer { return true; case "percussionarticulations": obj.percussionArticulations = []; - for (const o of v as (Map<string, unknown> | null)[]) { + for (const o of (v as (Map<string, unknown> | null)[])) { const i = new InstrumentArticulation(); - InstrumentArticulationSerializer.fromJson(i, o) + InstrumentArticulationSerializer.fromJson(i, o); obj.percussionArticulations.push(i); } return true; diff --git a/src/generated/model/TuningSerializer.ts b/src/generated/model/TuningSerializer.ts index 8293eb823..5fe512dc2 100644 --- a/src/generated/model/TuningSerializer.ts +++ b/src/generated/model/TuningSerializer.ts @@ -10,14 +10,14 @@ export class TuningSerializer { if (!m) { return; } - JsonHelper.forEach(m, (v, k) => this.setProperty(obj, k.toLowerCase(), v)); + JsonHelper.forEach(m, (v, k) => this.setProperty(obj, k, v)); } public static toJson(obj: Tuning | null): Map<string, unknown> | null { if (!obj) { return null; } const o = new Map<string, unknown>(); - o.set("isStandard", obj.isStandard); + o.set("isstandard", obj.isStandard); o.set("name", obj.name); o.set("tunings", obj.tunings); return o; diff --git a/src/generated/model/VoiceSerializer.ts b/src/generated/model/VoiceSerializer.ts index df0ec2fca..51845b884 100644 --- a/src/generated/model/VoiceSerializer.ts +++ b/src/generated/model/VoiceSerializer.ts @@ -12,7 +12,7 @@ export class VoiceSerializer { if (!m) { return; } - JsonHelper.forEach(m, (v, k) => this.setProperty(obj, k.toLowerCase(), v)); + JsonHelper.forEach(m, (v, k) => this.setProperty(obj, k, v)); } public static toJson(obj: Voice | null): Map<string, unknown> | null { if (!obj) { @@ -21,7 +21,7 @@ export class VoiceSerializer { const o = new Map<string, unknown>(); o.set("id", obj.id); o.set("beats", obj.beats.map(i => BeatSerializer.toJson(i))); - o.set("isEmpty", obj.isEmpty); + o.set("isempty", obj.isEmpty); return o; } public static setProperty(obj: Voice, property: string, v: unknown): boolean { @@ -31,9 +31,9 @@ export class VoiceSerializer { return true; case "beats": obj.beats = []; - for (const o of v as (Map<string, unknown> | null)[]) { + for (const o of (v as (Map<string, unknown> | null)[])) { const i = new Beat(); - BeatSerializer.fromJson(i, o) + BeatSerializer.fromJson(i, o); obj.addBeat(i); } return true; diff --git a/src/importer/AlphaTexImporter.ts b/src/importer/AlphaTexImporter.ts index b184045b3..93db6d11c 100644 --- a/src/importer/AlphaTexImporter.ts +++ b/src/importer/AlphaTexImporter.ts @@ -66,7 +66,7 @@ export class AlphaTexError extends AlphaTabError { public symbol: AlphaTexSymbols = AlphaTexSymbols.No; public symbolData: unknown = null; - public constructor(message: string) { + public constructor(message: string | null) { super(AlphaTabErrorType.AlphaTex, message); Object.setPrototypeOf(this, AlphaTexError.prototype); } @@ -125,7 +125,7 @@ export class AlphaTexImporter extends ScoreImporter { private _staffHasExplicitTuning: boolean = false; private _staffTuningApplied: boolean = false; - public logErrors:boolean = false; + public logErrors: boolean = false; public constructor() { super(); @@ -140,11 +140,11 @@ export class AlphaTexImporter extends ScoreImporter { this._input = tex; this.settings = settings; } - + public readScore(): Score { try { - if(this.data.length > 0) { + if (this.data.length > 0) { this._input = IOHelper.toString(this.data.readAll(), this.settings.importer.encoding); } this._allowTuning = true; @@ -199,7 +199,7 @@ export class AlphaTexImporter extends ScoreImporter { } else { e = AlphaTexError.symbolError(this._curChPos, nonterm, expected, expected, this._syData); } - if(this.logErrors) { + if (this.logErrors) { Logger.error(this.name, e.message!); } throw e; @@ -207,7 +207,7 @@ export class AlphaTexImporter extends ScoreImporter { private errorMessage(message: string): void { let e: AlphaTexError = AlphaTexError.errorMessage(this._curChPos, message); - if(this.logErrors) { + if (this.logErrors) { Logger.error(this.name, e.message!); } throw e; @@ -737,7 +737,7 @@ export class AlphaTexImporter extends ScoreImporter { this.error('tuning', AlphaTexSymbols.Tuning, true); break; } - if (strings !== this._currentStaff.tuning.length && this._currentStaff.chords.size > 0) { + if (strings !== this._currentStaff.tuning.length && (this._currentStaff.chords?.size ?? 0) > 0) { this.errorMessage('Tuning must be defined before any chord'); } return true; @@ -1286,22 +1286,24 @@ export class AlphaTexImporter extends ScoreImporter { beat.addWhammyBarPoint(new BendPoint(offset, value)); this._sy = this.newSy(); } - while (beat.whammyBarPoints.length > 60) { - beat.removeWhammyBarPoint(beat.whammyBarPoints.length - 1); - } - // set positions - if (!exact) { - let count: number = beat.whammyBarPoints.length; - let step: number = (60 / count) | 0; - let i: number = 0; - while (i < count) { - beat.whammyBarPoints[i].offset = Math.min(60, i * step); - i++; + if (beat.whammyBarPoints != null) { + while (beat.whammyBarPoints.length > 60) { + beat.removeWhammyBarPoint(beat.whammyBarPoints.length - 1); + } + // set positions + if (!exact) { + let count: number = beat.whammyBarPoints.length; + let step: number = (60 / count) | 0; + let i: number = 0; + while (i < count) { + beat.whammyBarPoints[i].offset = Math.min(60, i * step); + i++; + } + } else { + beat.whammyBarPoints.sort((a, b) => { + return a.offset - b.offset; + }); } - } else { - beat.whammyBarPoints.sort((a, b) => { - return a.offset - b.offset; - }); } this._allowNegatives = false; if (this._sy !== AlphaTexSymbols.RParensis) { @@ -1315,7 +1317,7 @@ export class AlphaTexImporter extends ScoreImporter { this._sy = this.newSy(); let chordName: string = (this._syData as string); let chordId: string = this.getChordId(this._currentStaff, chordName); - if (!this._currentStaff.chords.has(chordId)) { + if (!this._currentStaff.hasChord(chordId)) { let chord: Chord = new Chord(); chord.showDiagram = false; chord.name = chordName; @@ -1452,7 +1454,7 @@ export class AlphaTexImporter extends ScoreImporter { } private isNoteText(txt: string) { - return txt === 'x' || txt === '-' || txt === 'r'; + return txt === 'x' || txt === '-' || txt === 'r'; } private note(beat: Beat): boolean { @@ -1561,23 +1563,27 @@ export class AlphaTexImporter extends ScoreImporter { note.addBendPoint(new BendPoint(offset, value)); this._sy = this.newSy(); } - while (note.bendPoints.length > 60) { - note.bendPoints.splice(note.bendPoints.length - 1, 1); - } - // set positions - if (exact) { - note.bendPoints.sort((a, b) => { - return a.offset - b.offset; - }); - } else { - let count: number = note.bendPoints.length; - let step: number = (60 / (count - 1)) | 0; - let i: number = 0; - while (i < count) { - note.bendPoints[i].offset = Math.min(60, i * step); - i++; + const points = note.bendPoints; + if(points != null){ + while (points.length > 60) { + points.splice(points.length - 1, 1); + } + // set positions + if (exact) { + points.sort((a, b) => { + return a.offset - b.offset; + }); + } else { + let count: number = points.length; + let step: number = (60 / (count - 1)) | 0; + let i: number = 0; + while (i < count) { + points[i].offset = Math.min(60, i * step); + i++; + } } } + if (this._sy !== AlphaTexSymbols.RParensis) { this.error('bend-effect', AlphaTexSymbols.RParensis, true); } @@ -1857,7 +1863,7 @@ export class AlphaTexImporter extends ScoreImporter { this._sy = this.newSy(); } else { if (bar.index === 0) { - if(!this.handleStaffMeta()) { + if (!this.handleStaffMeta()) { this.error('measure-effects', AlphaTexSymbols.String, false); } } else { diff --git a/src/importer/CapellaParser.ts b/src/importer/CapellaParser.ts index 7f2085038..d5f211013 100644 --- a/src/importer/CapellaParser.ts +++ b/src/importer/CapellaParser.ts @@ -884,7 +884,7 @@ export class CapellaParser { } } else if (c.attributes.has('end') && this._tieStarts.length > 0 && !note.isTieDestination) { note.isTieDestination = true; - note.tieOriginNoteId = this._tieStarts[0].id; + note.tieOrigin = this._tieStarts[0]; this._tieStarts.splice(0, 1); this._tieStartIds.delete(note.id); } diff --git a/src/importer/GpxFileSystem.ts b/src/importer/GpxFileSystem.ts index be47f19f5..2325efcc7 100644 --- a/src/importer/GpxFileSystem.ts +++ b/src/importer/GpxFileSystem.ts @@ -1,7 +1,7 @@ import { UnsupportedFormatError } from '@src/importer/UnsupportedFormatError'; -import { BitReader, EndOfReaderError } from '@src/io/BitReader'; +import { BitReader } from '@src/io/BitReader'; import { ByteBuffer } from '@src/io/ByteBuffer'; -import { IReadable } from '@src/io/IReadable'; +import { EndOfReaderError, IReadable } from '@src/io/IReadable'; /** * this public class represents a file within the GpxFileSystem diff --git a/src/importer/MusicXmlImporter.ts b/src/importer/MusicXmlImporter.ts index b64c7a899..39c5a97a8 100644 --- a/src/importer/MusicXmlImporter.ts +++ b/src/importer/MusicXmlImporter.ts @@ -812,7 +812,7 @@ export class MusicXmlImporter extends ScoreImporter { } } else if (element.getAttribute('type') === 'stop' && this._tieStarts.length > 0 && !note.isTieDestination) { note.isTieDestination = true; - note.tieOriginNoteId = this._tieStarts[0].id; + note.tieOrigin = this._tieStarts[0]; this._tieStarts.splice(0, 1); this._tieStartIds.delete(note.id); } @@ -856,8 +856,8 @@ export class MusicXmlImporter extends ScoreImporter { if (this._slurStarts.has(slurNumber)) { note.isSlurDestination = true; let slurStart: Note = this._slurStarts.get(slurNumber)!; - slurStart.slurDestinationNoteId = note.id; - note.slurOriginNoteId = note.id; + slurStart.slurDestination = note; + note.slurOrigin = note; } break; } diff --git a/src/importer/UnsupportedFormatError.ts b/src/importer/UnsupportedFormatError.ts index 0530808f1..c15268578 100644 --- a/src/importer/UnsupportedFormatError.ts +++ b/src/importer/UnsupportedFormatError.ts @@ -5,10 +5,10 @@ import { AlphaTabError, AlphaTabErrorType } from "@src/AlphaTabError"; * binary data does not contain a reader compatible structure. */ export class UnsupportedFormatError extends AlphaTabError { - public inner: Error | null; + public override inner: Error | null; - public constructor(message: string = 'Unsupported format', inner: Error | null = null) { - super(AlphaTabErrorType.Format, message); + public constructor(message: string | null = null, inner: Error | null = null) { + super(AlphaTabErrorType.Format, message ?? 'Unsupported format'); this.inner = inner; Object.setPrototypeOf(this, UnsupportedFormatError.prototype); } diff --git a/src/io/BitReader.ts b/src/io/BitReader.ts index 1bae9c5e8..408606cc5 100644 --- a/src/io/BitReader.ts +++ b/src/io/BitReader.ts @@ -1,13 +1,5 @@ import { ByteBuffer } from '@src/io/ByteBuffer'; -import { IReadable } from '@src/io/IReadable'; -import { AlphaTabError, AlphaTabErrorType } from '@src/AlphaTabError'; - -export class EndOfReaderError extends AlphaTabError { - public constructor() { - super(AlphaTabErrorType.Format, 'Unexpected end of data within reader'); - Object.setPrototypeOf(this, EndOfReaderError.prototype); - } -} +import { EndOfReaderError, IReadable } from '@src/io/IReadable'; /** * This utility public class allows bitwise reading of a stream diff --git a/src/io/ByteBuffer.ts b/src/io/ByteBuffer.ts index 2b3cacb79..4d20a8757 100644 --- a/src/io/ByteBuffer.ts +++ b/src/io/ByteBuffer.ts @@ -4,7 +4,6 @@ import { IOHelper } from '@src/io/IOHelper'; export class ByteBuffer implements IWriteable, IReadable { private _buffer!: Uint8Array; - private _capacity: number = 0; public length: number = 0; public position: number = 0; @@ -24,7 +23,6 @@ export class ByteBuffer implements IWriteable, IReadable { public static withCapacity(capacity: number): ByteBuffer { let buffer: ByteBuffer = new ByteBuffer(); buffer._buffer = new Uint8Array(capacity); - buffer._capacity = capacity; return buffer; } @@ -32,7 +30,6 @@ export class ByteBuffer implements IWriteable, IReadable { let buffer: ByteBuffer = new ByteBuffer(); buffer._buffer = data; buffer.length = data.length - buffer._capacity = buffer.length; return buffer; } @@ -49,19 +46,6 @@ export class ByteBuffer implements IWriteable, IReadable { this.position += offset; } - private setCapacity(value: number): void { - if (value !== this._capacity) { - if (value > 0) { - let newBuffer: Uint8Array = new Uint8Array(value); - if (this.length > 0) { - newBuffer.set(this._buffer.subarray(0, 0 + this.length), 0); - } - this._buffer = newBuffer; - } - this._capacity = value; - } - } - public readByte(): number { let n: number = this.length - this.position; if (n <= 0) { @@ -78,54 +62,49 @@ export class ByteBuffer implements IWriteable, IReadable { if (n <= 0) { return 0; } - if (n <= 8) { - let byteCount: number = n; - while (--byteCount >= 0) { - buffer[offset + byteCount] = this._buffer[this.position + byteCount]; - } - } else { - buffer.set(this._buffer.subarray(this.position, this.position + n), offset); - } + buffer.set(this._buffer.subarray(this.position, this.position + n), offset); this.position += n; return n; } public writeByte(value: number): void { - let buffer: Uint8Array = new Uint8Array(1); - buffer[0] = value; - this.write(buffer, 0, 1); + let i: number = this.position + 1; + this.ensureCapacity(i); + this._buffer[this.position] = value & 0xFF; + if (i > this.length) { + this.length = i; + } + this.position = i; } public write(buffer: Uint8Array, offset: number, count: number): void { let i: number = this.position + count; + this.ensureCapacity(i); + + let count1: number = Math.min(count, buffer.length - offset); + this._buffer.set(buffer.subarray(offset, offset + count1), this.position); + if (i > this.length) { - if (i > this._capacity) { - this.ensureCapacity(i); - } this.length = i; } - if (count <= 8 && buffer !== this._buffer) { - let byteCount: number = count; - while (--byteCount >= 0) { - this._buffer[this.position + byteCount] = buffer[offset + byteCount]; - } - } else { - let count1: number = Math.min(count, buffer.length - offset); - this._buffer.set(buffer.subarray(offset, offset + count1), this.position); - } this.position = i; } private ensureCapacity(value: number): void { - if (value > this._capacity) { + if (value > this._buffer.length) { let newCapacity: number = value; if (newCapacity < 256) { newCapacity = 256; } - if (newCapacity < this._capacity * 2) { - newCapacity = this._capacity * 2; + if (newCapacity < this._buffer.length * 2) { + newCapacity = this._buffer.length * 2; + } + + let newBuffer: Uint8Array = new Uint8Array(newCapacity); + if (this.length > 0) { + newBuffer.set(this._buffer.subarray(0, 0 + this.length), 0); } - this.setCapacity(newCapacity); + this._buffer = newBuffer; } } diff --git a/src/io/IOHelper.ts b/src/io/IOHelper.ts index 96520a8f0..1a7c5fec9 100644 --- a/src/io/IOHelper.ts +++ b/src/io/IOHelper.ts @@ -157,26 +157,26 @@ export class IOHelper { } public static writeInt32BE(o: IWriteable, v: number) { - o.writeByte((v >> 24) & 0xFF); - o.writeByte((v >> 16) & 0xFF); - o.writeByte((v >> 8) & 0xFF); - o.writeByte((v >> 0) & 0xFF); + o.writeByte((v >> 24) & 0xff); + o.writeByte((v >> 16) & 0xff); + o.writeByte((v >> 8) & 0xff); + o.writeByte((v >> 0) & 0xff); } public static writeInt32LE(o: IWriteable, v: number) { - o.writeByte((v >> 0) & 0xFF); - o.writeByte((v >> 8) & 0xFF); - o.writeByte((v >> 16) & 0xFF); - o.writeByte((v >> 24) & 0xFF); + o.writeByte((v >> 0) & 0xff); + o.writeByte((v >> 8) & 0xff); + o.writeByte((v >> 16) & 0xff); + o.writeByte((v >> 24) & 0xff); } public static writeUInt16LE(o: IWriteable, v: number) { - o.writeByte((v >> 0) & 0xFF); - o.writeByte((v >> 8) & 0xFF); + o.writeByte((v >> 0) & 0xff); + o.writeByte((v >> 8) & 0xff); } public static writeInt16LE(o: IWriteable, v: number) { - o.writeByte((v >> 0) & 0xFF); - o.writeByte((v >> 8) & 0xFF); + o.writeByte((v >> 0) & 0xff); + o.writeByte((v >> 8) & 0xff); } } diff --git a/src/io/IReadable.ts b/src/io/IReadable.ts index f7737305b..56965ba25 100644 --- a/src/io/IReadable.ts +++ b/src/io/IReadable.ts @@ -1,3 +1,5 @@ +import { AlphaTabError, AlphaTabErrorType } from "@src/AlphaTabError"; + /** * Represents a stream of binary data that can be read from. */ @@ -44,3 +46,10 @@ export interface IReadable { */ readAll(): Uint8Array; } + +export class EndOfReaderError extends AlphaTabError { + public constructor() { + super(AlphaTabErrorType.Format, 'Unexpected end of data within reader'); + Object.setPrototypeOf(this, EndOfReaderError.prototype); + } +} diff --git a/src/io/TypeConversions.ts b/src/io/TypeConversions.ts index ef44ac904..f60b2c62c 100644 --- a/src/io/TypeConversions.ts +++ b/src/io/TypeConversions.ts @@ -1,7 +1,22 @@ +/** + * @target web + */ export class TypeConversions { + private static _conversionBuffer: ArrayBuffer = new ArrayBuffer(8); + private static _conversionByteArray: Uint8Array = new Uint8Array(TypeConversions._conversionBuffer); private static _dataView = new DataView(TypeConversions._conversionBuffer); + public static float64ToBytes(v: number): Uint8Array { + TypeConversions._dataView.setFloat64(0, v, true); + return this._conversionByteArray; + } + + public static bytesToFloat64(bytes: Uint8Array): number { + TypeConversions._conversionByteArray.set(bytes, 0); + throw TypeConversions._dataView.getFloat64(0, true); + } + public static uint16ToInt16(v: number): number { TypeConversions._dataView.setUint16(0, v, true); return TypeConversions._dataView.getInt16(0, true); diff --git a/src/midi/MetaDataEvent.ts b/src/midi/MetaDataEvent.ts index 0f3ffec50..b0cc18e4a 100644 --- a/src/midi/MetaDataEvent.ts +++ b/src/midi/MetaDataEvent.ts @@ -10,7 +10,7 @@ export class MetaDataEvent extends MetaEvent { this.data = data; } - public writeTo(s: IWriteable): void { + public override writeTo(s: IWriteable): void { s.writeByte(0xff); s.writeByte(this.metaStatus as number); let l: number = this.data.length; diff --git a/src/midi/MetaEvent.ts b/src/midi/MetaEvent.ts index ff6240788..2cf9d91d4 100644 --- a/src/midi/MetaEvent.ts +++ b/src/midi/MetaEvent.ts @@ -22,11 +22,11 @@ export enum MetaEventType { } export class MetaEvent extends MidiEvent { - public get channel(): number { + public override get channel(): number { return -1; } - public get command(): MidiEventType { + public override get command(): MidiEventType { return (this.message & 0x00000ff) as MidiEventType; } diff --git a/src/midi/MetaNumberEvent.ts b/src/midi/MetaNumberEvent.ts index ebe1af152..e57289d97 100644 --- a/src/midi/MetaNumberEvent.ts +++ b/src/midi/MetaNumberEvent.ts @@ -10,7 +10,7 @@ export class MetaNumberEvent extends MetaEvent { this.value = value; } - public writeTo(s: IWriteable): void { + public override writeTo(s: IWriteable): void { s.writeByte(0xff); s.writeByte(this.metaStatus as number); MidiFile.writeVariableInt(s, 3); diff --git a/src/midi/Midi20PerNotePitchBendEvent.ts b/src/midi/Midi20PerNotePitchBendEvent.ts index bebf7bb53..3616fa29b 100644 --- a/src/midi/Midi20PerNotePitchBendEvent.ts +++ b/src/midi/Midi20PerNotePitchBendEvent.ts @@ -19,7 +19,7 @@ export class Midi20PerNotePitchBendEvent extends MidiEvent { * Writes the midi event as binary into the given stream. * @param s The stream to write to. */ - public writeTo(s: IWriteable): void { + public override writeTo(s: IWriteable): void { let b: Uint8Array = new Uint8Array([ 0x40, this.message & 0xff, diff --git a/src/midi/MidiFileGenerator.ts b/src/midi/MidiFileGenerator.ts index 1c41a27da..0060fc60e 100644 --- a/src/midi/MidiFileGenerator.ts +++ b/src/midi/MidiFileGenerator.ts @@ -433,9 +433,9 @@ export class MidiFileGenerator { let initialBend: number = 0; if (note.hasBend) { - initialBend = MidiFileGenerator.getPitchWheel(note.bendPoints[0].value); + initialBend = MidiFileGenerator.getPitchWheel(note.bendPoints![0].value); } else if (note.beat.hasWhammyBar) { - initialBend = MidiFileGenerator.getPitchWheel(note.beat.whammyBarPoints[0].value); + initialBend = MidiFileGenerator.getPitchWheel(note.beat.whammyBarPoints![0].value); } else if ( note.isTieDestination || (note.slideOrigin && note.slideOrigin.slideOutType === SlideOutType.Legato) @@ -815,7 +815,7 @@ export class MidiFileGenerator { noteKey: number, channel: number ): void { - let bendPoints: BendPoint[] = note.bendPoints; + let bendPoints: BendPoint[] = note.bendPoints!; let track: Track = note.beat.voice.bar.staff.track; const addBend: (tick: number, value: number) => void = (tick, value) => { @@ -840,11 +840,11 @@ export class MidiFileGenerator { case BendType.Bend: case BendType.BendRelease: case BendType.PrebendBend: - finalBendValue = note.tieDestination!.bendPoints[1].value; + finalBendValue = note.tieDestination!.bendPoints![1].value; break; case BendType.Prebend: case BendType.PrebendRelease: - finalBendValue = note.tieDestination!.bendPoints[0].value; + finalBendValue = note.tieDestination!.bendPoints![0].value; break; } duration = Math.max( @@ -874,22 +874,22 @@ export class MidiFileGenerator { playedBendPoints = bendPoints; break; case BendStyle.Gradual: - playedBendPoints.push(new BendPoint(0, note.bendPoints[0].value)); - if (!finalBendValue || finalBendValue < note.bendPoints[1].value) { - finalBendValue = note.bendPoints[1].value; + playedBendPoints.push(new BendPoint(0, note.bendPoints![0].value)); + if (!finalBendValue || finalBendValue < note.bendPoints![1].value) { + finalBendValue = note.bendPoints![1].value; } playedBendPoints.push(new BendPoint(BendPoint.MaxPosition, finalBendValue)); break; case BendStyle.Fast: - if (!finalBendValue || finalBendValue < note.bendPoints[1].value) { - finalBendValue = note.bendPoints[1].value; + if (!finalBendValue || finalBendValue < note.bendPoints![1].value) { + finalBendValue = note.bendPoints![1].value; } if (note.beat.graceType === GraceType.BendGrace) { this.generateSongBookWhammyOrBend( noteStart, duration, true, - [note.bendPoints[0].value, finalBendValue], + [note.bendPoints![0].value, finalBendValue], bendDuration, addBend ); @@ -898,7 +898,7 @@ export class MidiFileGenerator { noteStart, duration, false, - [note.bendPoints[0].value, finalBendValue], + [note.bendPoints![0].value, finalBendValue], bendDuration, addBend ); @@ -912,16 +912,16 @@ export class MidiFileGenerator { playedBendPoints = bendPoints; break; case BendStyle.Gradual: - playedBendPoints.push(new BendPoint(0, note.bendPoints[0].value)); - playedBendPoints.push(new BendPoint((BendPoint.MaxPosition / 2) | 0, note.bendPoints[1].value)); - playedBendPoints.push(new BendPoint(BendPoint.MaxPosition, note.bendPoints[2].value)); + playedBendPoints.push(new BendPoint(0, note.bendPoints![0].value)); + playedBendPoints.push(new BendPoint((BendPoint.MaxPosition / 2) | 0, note.bendPoints![1].value)); + playedBendPoints.push(new BendPoint(BendPoint.MaxPosition, note.bendPoints![2].value)); break; case BendStyle.Fast: this.generateSongBookWhammyOrBend( noteStart, duration, false, - [note.bendPoints[0].value, note.bendPoints[1].value, note.bendPoints[2].value], + [note.bendPoints![0].value, note.bendPoints![1].value, note.bendPoints![2].value], bendDuration, addBend ); @@ -940,20 +940,20 @@ export class MidiFileGenerator { playedBendPoints = bendPoints; break; case BendStyle.Gradual: - playedBendPoints.push(new BendPoint(0, note.bendPoints[0].value)); - playedBendPoints.push(new BendPoint(BendPoint.MaxPosition, note.bendPoints[1].value)); + playedBendPoints.push(new BendPoint(0, note.bendPoints![0].value)); + playedBendPoints.push(new BendPoint(BendPoint.MaxPosition, note.bendPoints![1].value)); break; case BendStyle.Fast: - const preBendValue: number = MidiFileGenerator.getPitchWheel(note.bendPoints[0].value); + const preBendValue: number = MidiFileGenerator.getPitchWheel(note.bendPoints![0].value); addBend(noteStart, preBendValue | 0); - if (!finalBendValue || finalBendValue < note.bendPoints[1].value) { - finalBendValue = note.bendPoints[1].value; + if (!finalBendValue || finalBendValue < note.bendPoints![1].value) { + finalBendValue = note.bendPoints![1].value; } this.generateSongBookWhammyOrBend( noteStart, duration, false, - [note.bendPoints[0].value, finalBendValue], + [note.bendPoints![0].value, finalBendValue], bendDuration, addBend ); @@ -966,17 +966,17 @@ export class MidiFileGenerator { playedBendPoints = bendPoints; break; case BendStyle.Gradual: - playedBendPoints.push(new BendPoint(0, note.bendPoints[0].value)); - playedBendPoints.push(new BendPoint(BendPoint.MaxPosition, note.bendPoints[1].value)); + playedBendPoints.push(new BendPoint(0, note.bendPoints![0].value)); + playedBendPoints.push(new BendPoint(BendPoint.MaxPosition, note.bendPoints![1].value)); break; case BendStyle.Fast: - const preBendValue: number = MidiFileGenerator.getPitchWheel(note.bendPoints[0].value); + const preBendValue: number = MidiFileGenerator.getPitchWheel(note.bendPoints![0].value); addBend(noteStart, preBendValue | 0); this.generateSongBookWhammyOrBend( noteStart, duration, false, - [note.bendPoints[0].value, note.bendPoints[1].value], + [note.bendPoints![0].value, note.bendPoints![1].value], bendDuration, addBend ); @@ -1006,7 +1006,7 @@ export class MidiFileGenerator { } private generateWhammy(beat: Beat, noteStart: number, noteDuration: MidiNoteDuration, channel: number): void { - const bendPoints: BendPoint[] = beat.whammyBarPoints; + const bendPoints: BendPoint[] = beat.whammyBarPoints!; const track: Track = beat.voice.bar.staff.track; const duration: number = noteDuration.noteOnly; // ensure prebends are slightly before the actual note. diff --git a/src/midi/SystemCommonEvent.ts b/src/midi/SystemCommonEvent.ts index d6ab24999..9c26ea6e5 100644 --- a/src/midi/SystemCommonEvent.ts +++ b/src/midi/SystemCommonEvent.ts @@ -10,11 +10,11 @@ export enum SystemCommonType { } export class SystemCommonEvent extends MidiEvent { - public get channel(): number { + public override get channel(): number { return -1; } - public get command(): MidiEventType { + public override get command(): MidiEventType { return (this.message & 0x00000ff) as MidiEventType; } diff --git a/src/midi/SystemExclusiveEvent.ts b/src/midi/SystemExclusiveEvent.ts index c2cc603c9..7e6c676df 100644 --- a/src/midi/SystemExclusiveEvent.ts +++ b/src/midi/SystemExclusiveEvent.ts @@ -50,7 +50,7 @@ export class SystemExclusiveEvent extends SystemCommonEvent { this.data = data; } - public writeTo(s: IWriteable): void { + public override writeTo(s: IWriteable): void { s.writeByte(0xf0); let l: number = this.data.length + 2; s.writeByte(this.manufacturerId); diff --git a/src/model/Automation.ts b/src/model/Automation.ts index cc14e660e..1804adc84 100644 --- a/src/model/Automation.ts +++ b/src/model/Automation.ts @@ -24,6 +24,7 @@ export enum AutomationType { * Automations are used to change the behaviour of a song. * @cloneable * @json + * @json_strict */ export class Automation { /** diff --git a/src/model/Bar.ts b/src/model/Bar.ts index a4f894c2b..34f4c0544 100644 --- a/src/model/Bar.ts +++ b/src/model/Bar.ts @@ -9,6 +9,7 @@ import { Settings } from '@src/Settings'; /** * A bar is a single block within a track, also known as Measure. * @json + * @json_strict */ export class Bar { private static _globalBarId: number = 0; @@ -89,11 +90,11 @@ export class Bar { this.voices.push(voice); } - public finish(settings: Settings): void { + public finish(settings: Settings, sharedDataBag: Map<string, unknown>): void { this.isMultiVoice = false; for (let i: number = 0, j: number = this.voices.length; i < j; i++) { let voice: Voice = this.voices[i]; - voice.finish(settings); + voice.finish(settings, sharedDataBag); if(i > 0 && !voice.isEmpty) { this.isMultiVoice = true; } diff --git a/src/model/Beat.ts b/src/model/Beat.ts index 24f4580ca..0c1866dd9 100644 --- a/src/model/Beat.ts +++ b/src/model/Beat.ts @@ -46,6 +46,7 @@ export enum BeatBeamingMode { * A beat is a single block within a bar. A beat is a combination * of several notes played at the same time. * @json + * @json_strict * @cloneable */ export class Beat { @@ -289,7 +290,7 @@ export class Beat { * @json_add addWhammyBarPoint * @clone_add addWhammyBarPoint */ - public whammyBarPoints: BendPoint[] = []; + public whammyBarPoints: BendPoint[] | null = null; /** * Gets or sets the highest point with for the highest whammy bar value. @@ -306,7 +307,7 @@ export class Beat { public minWhammyPoint: BendPoint | null = null; public get hasWhammyBar(): boolean { - return this.whammyBarType !== WhammyType.None; + return this.whammyBarPoints !== null && this.whammyBarType !== WhammyType.None; } /** @@ -324,7 +325,7 @@ export class Beat { } public get chord(): Chord | null { - return this.chordId ? this.voice.bar.staff.chords.get(this.chordId)! : null; + return this.chordId ? this.voice.bar.staff.getChord(this.chordId)! : null; } /** @@ -440,7 +441,12 @@ export class Beat { public beamingMode: BeatBeamingMode = BeatBeamingMode.Auto; public addWhammyBarPoint(point: BendPoint): void { - this.whammyBarPoints.push(point); + let points = this.whammyBarPoints; + if (points === null) { + points = []; + this.whammyBarPoints = points; + } + points.push(point); if (!this.maxWhammyPoint || point.value > this.maxWhammyPoint.value) { this.maxWhammyPoint = point; } @@ -454,18 +460,19 @@ export class Beat { public removeWhammyBarPoint(index: number): void { // check index - if (index < 0 || index >= this.whammyBarPoints.length) { + const points = this.whammyBarPoints; + if (points === null || index < 0 || index >= points.length) { return; } // remove point - this.whammyBarPoints.splice(index, 1); - let point: BendPoint = this.whammyBarPoints[index]; + points.splice(index, 1); + let point: BendPoint = points[index]; // update maxWhammy point if required if (point === this.maxWhammyPoint) { this.maxWhammyPoint = null; - for (let currentPoint of this.whammyBarPoints) { + for (let currentPoint of points) { if (!this.maxWhammyPoint || currentPoint.value > this.maxWhammyPoint.value) { this.maxWhammyPoint = currentPoint; } @@ -474,7 +481,7 @@ export class Beat { if (point === this.minWhammyPoint) { this.minWhammyPoint = null; - for (let currentPoint of this.whammyBarPoints) { + for (let currentPoint of points) { if (!this.minWhammyPoint || currentPoint.value < this.minWhammyPoint.value) { this.minWhammyPoint = currentPoint; } @@ -516,7 +523,7 @@ export class Beat { } private calculateDuration(): number { - if(this.isFullBarRest) { + if (this.isFullBarRest) { return this.voice.bar.masterBar.calculateDuration(); } let ticks: number = MidiUtils.toTicks(this.duration); @@ -577,7 +584,7 @@ export class Beat { } } - public finish(settings: Settings): void { + public finish(settings: Settings, sharedDataBag: Map<string, unknown>): void { if (this.getAutomation(AutomationType.Instrument) === null && this.index === 0 && this.voice.index === 0 && @@ -617,7 +624,7 @@ export class Beat { for (let i: number = 0, j: number = this.notes.length; i < j; i++) { let note: Note = this.notes[i]; note.dynamics = this.dynamics; - note.finish(settings); + note.finish(settings, sharedDataBag); if (note.isLetRing) { this.isLetRing = true; } @@ -707,17 +714,18 @@ export class Beat { } // try to detect what kind of bend was used and cleans unneeded points if required // Guitar Pro 6 and above (gpif.xml) uses exactly 4 points to define all whammys - if (this.whammyBarPoints.length > 0 && this.whammyBarType === WhammyType.Custom) { + const points = this.whammyBarPoints; + if (points !== null && points.length > 0 && this.whammyBarType === WhammyType.Custom) { if (displayMode === NotationMode.SongBook) { this.whammyStyle = isGradual ? BendStyle.Gradual : BendStyle.Fast; } let isContinuedWhammy: boolean = !!this.previousBeat && this.previousBeat.hasWhammyBar; this.isContinuedWhammy = isContinuedWhammy; - if (this.whammyBarPoints.length === 4) { - let origin: BendPoint = this.whammyBarPoints[0]; - let middle1: BendPoint = this.whammyBarPoints[1]; - let middle2: BendPoint = this.whammyBarPoints[2]; - let destination: BendPoint = this.whammyBarPoints[3]; + if (points.length === 4) { + let origin: BendPoint = points[0]; + let middle1: BendPoint = points[1]; + let middle2: BendPoint = points[2]; + let destination: BendPoint = points[3]; // the middle points are used for holds, anything else is a new feature we do not support yet if (middle1.value === middle2.value) { // constant decrease or increase @@ -730,15 +738,15 @@ export class Beat { } else { this.whammyBarType = WhammyType.Dive; } - this.whammyBarPoints.splice(2, 1); - this.whammyBarPoints.splice(1, 1); + points.splice(2, 1); + points.splice(1, 1); } else if ( (origin.value > middle1.value && middle1.value < destination.value) || (origin.value < middle1.value && middle1.value > destination.value) ) { this.whammyBarType = WhammyType.Dip; if (middle1.offset === middle2.offset || displayMode === NotationMode.SongBook) { - this.whammyBarPoints.splice(2, 1); + points.splice(2, 1); } } else if (origin.value === middle1.value && middle1.value === destination.value) { if (origin.value !== 0 && !isContinuedWhammy) { @@ -746,8 +754,8 @@ export class Beat { } else { this.whammyBarType = WhammyType.Hold; } - this.whammyBarPoints.splice(2, 1); - this.whammyBarPoints.splice(1, 1); + points.splice(2, 1); + points.splice(1, 1); } else { Logger.warning('Model', 'Unsupported whammy type detected, fallback to custom', null); } @@ -770,18 +778,18 @@ export class Beat { // remove bend on cloned note cloneNote.bendType = BendType.None; cloneNote.maxBendPoint = null; - cloneNote.bendPoints = []; + cloneNote.bendPoints = null; cloneNote.bendStyle = BendStyle.Default; cloneNote.id = Note.GlobalNoteId++; // fix ties if (note.isTieOrigin) { - cloneNote.tieDestinationNoteId = note.tieDestination!.id; - note.tieDestination!.tieOriginNoteId = cloneNote.id; + cloneNote.tieDestination = note.tieDestination!; + note.tieDestination!.tieOrigin = cloneNote; } if (note.isTieDestination) { - cloneNote.tieOriginNoteId = note.tieOrigin ? note.tieOrigin.id : -1; - note.tieOrigin!.tieDestinationNoteId = cloneNote.id; + cloneNote.tieOrigin = note.tieOrigin ? note.tieOrigin : null; + note.tieOrigin!.tieDestination = cloneNote; } // if the note has a bend which is continued on the next note @@ -790,7 +798,7 @@ export class Beat { let tieDestination: Note | null = Note.findTieOrigin(note); if (tieDestination && tieDestination.hasBend) { cloneNote.bendType = BendType.Hold; - let lastPoint: BendPoint = note.bendPoints[note.bendPoints.length - 1]; + let lastPoint: BendPoint = note.bendPoints![note.bendPoints!.length - 1]; cloneNote.addBendPoint(new BendPoint(0, lastPoint.value)); cloneNote.addBendPoint(new BendPoint(BendPoint.MaxPosition, lastPoint.value)); } @@ -844,6 +852,7 @@ export class Beat { return this.noteStringLookup.has(noteString); } + // TODO: can be likely eliminated public getNoteWithRealValue(noteRealValue: number): Note | null { if (this.noteValueLookup.has(noteRealValue)) { return this.noteValueLookup.get(noteRealValue)!; @@ -851,10 +860,10 @@ export class Beat { return null; } - public chain() { + public chain(sharedDataBag: Map<string, unknown>) { for (const n of this.notes) { this.noteValueLookup.set(n.realValue, n); - n.chain(); + n.chain(sharedDataBag); } } } diff --git a/src/model/BendPoint.ts b/src/model/BendPoint.ts index 9d08cf1c9..2c8104faa 100644 --- a/src/model/BendPoint.ts +++ b/src/model/BendPoint.ts @@ -3,6 +3,7 @@ * describe WhammyBar and String Bending effects. * @cloneable * @json + * @json_strict */ export class BendPoint { public static readonly MaxPosition: number = 60; diff --git a/src/model/Chord.ts b/src/model/Chord.ts index 52edc3d3d..bb64efca6 100644 --- a/src/model/Chord.ts +++ b/src/model/Chord.ts @@ -6,6 +6,7 @@ import { Staff } from '@src/model/Staff'; /** * A chord definition. * @json + * @json_strict */ export class Chord { /** diff --git a/src/model/Color.ts b/src/model/Color.ts index ca98a98ab..b1a2e85e4 100644 --- a/src/model/Color.ts +++ b/src/model/Color.ts @@ -63,78 +63,76 @@ export class Color { public static fromJson(v: unknown): Color | null { switch (typeof v) { - case 'number': - { - const c = new Color(0, 0, 0, 0); - c.raw = v! as number; - c.updateRgba(); - return c; - } - case 'string': - { - const json = v as string; - if (json.startsWith('#')) { - if (json.length === 4) { - // #RGB - return new Color( - parseInt(json.substring(1, 1), 16) * 17, - parseInt(json.substring(2, 1), 16) * 17, - parseInt(json.substring(3, 1), 16) * 17 - ); - } - - if (json.length === 5) { - // #RGBA - return new Color( - parseInt(json.substring(1, 1), 16) * 17, - parseInt(json.substring(2, 1), 16) * 17, - parseInt(json.substring(3, 1), 16) * 17, - parseInt(json.substring(4, 1), 16) * 17 - ); - } - - if (json.length === 7) { - // #RRGGBB - return new Color( - parseInt(json.substring(1, 2), 16), - parseInt(json.substring(3, 2), 16), - parseInt(json.substring(5, 2), 16) - ); - } - - if (json.length === 9) { - // #RRGGBBAA - return new Color( - parseInt(json.substring(1, 2), 16), - parseInt(json.substring(3, 2), 16), - parseInt(json.substring(5, 2), 16), - parseInt(json.substring(7, 2), 16) - ); - } - } else if (json.startsWith('rgba') || json.startsWith('rgb')) { - const start = json.indexOf('('); - const end = json.lastIndexOf(')'); - if (start === -1 || end === -1) { - throw new FormatError('No values specified for rgb/rgba function'); - } - - const numbers = json.substring(start + 1, end).split(','); - - if (numbers.length === 3) { - return new Color(parseInt(numbers[0]), parseInt(numbers[1]), parseInt(numbers[2])); - } - - if (numbers.length === 4) { - return new Color( - parseInt(numbers[0]), - parseInt(numbers[1]), - parseInt(numbers[2]), - parseFloat(numbers[3]) * 255 - ); - } + case 'number': { + const c = new Color(0, 0, 0, 0); + c.raw = v! as number; + c.updateRgba(); + return c; + } + case 'string': { + const json = v as string; + if (json.startsWith('#')) { + if (json.length === 4) { + // #RGB + return new Color( + parseInt(json.substring(1, 1), 16) * 17, + parseInt(json.substring(2, 1), 16) * 17, + parseInt(json.substring(3, 1), 16) * 17 + ); + } + + if (json.length === 5) { + // #RGBA + return new Color( + parseInt(json.substring(1, 1), 16) * 17, + parseInt(json.substring(2, 1), 16) * 17, + parseInt(json.substring(3, 1), 16) * 17, + parseInt(json.substring(4, 1), 16) * 17 + ); + } + + if (json.length === 7) { + // #RRGGBB + return new Color( + parseInt(json.substring(1, 2), 16), + parseInt(json.substring(3, 2), 16), + parseInt(json.substring(5, 2), 16) + ); + } + + if (json.length === 9) { + // #RRGGBBAA + return new Color( + parseInt(json.substring(1, 2), 16), + parseInt(json.substring(3, 2), 16), + parseInt(json.substring(5, 2), 16), + parseInt(json.substring(7, 2), 16) + ); + } + } else if (json.startsWith('rgba') || json.startsWith('rgb')) { + const start = json.indexOf('('); + const end = json.lastIndexOf(')'); + if (start === -1 || end === -1) { + throw new FormatError('No values specified for rgb/rgba function'); + } + + const numbers = json.substring(start + 1, end).split(','); + + if (numbers.length === 3) { + return new Color(parseInt(numbers[0]), parseInt(numbers[1]), parseInt(numbers[2])); + } + + if (numbers.length === 4) { + return new Color( + parseInt(numbers[0]), + parseInt(numbers[1]), + parseInt(numbers[2]), + parseFloat(numbers[3]) * 255 + ); } - return null; } + return null; + } } throw new FormatError('Unsupported format for color'); diff --git a/src/model/Fermata.ts b/src/model/Fermata.ts index 8c47b3d64..3a823f7a6 100644 --- a/src/model/Fermata.ts +++ b/src/model/Fermata.ts @@ -19,6 +19,7 @@ export enum FermataType { /** * Represents a fermata. * @json + * @json_strict */ export class Fermata { /** diff --git a/src/model/Font.ts b/src/model/Font.ts index 9e0847cf6..c62cfd664 100644 --- a/src/model/Font.ts +++ b/src/model/Font.ts @@ -154,8 +154,7 @@ class FontParser { } else if (parts.length >= 1) { this.size = parts[0]; - if (this._currentToken && - this._currentToken.text.indexOf('/') === 0) { + if (this._currentToken && this._currentToken.text.indexOf('/') === 0) { // size / line-height (with spaces befor and after slash) if (this._currentToken.text === '/') { this.nextToken(); diff --git a/src/model/InstrumentArticulation.ts b/src/model/InstrumentArticulation.ts index 385e35763..66ca9dc0a 100644 --- a/src/model/InstrumentArticulation.ts +++ b/src/model/InstrumentArticulation.ts @@ -5,6 +5,7 @@ import { MusicFontSymbol } from "./MusicFontSymbol"; /** * Describes an instrument articulation which is used for percussions. * @json + * @json_strict */ export class InstrumentArticulation { /** diff --git a/src/model/MasterBar.ts b/src/model/MasterBar.ts index 6f01444b6..cdaa23491 100644 --- a/src/model/MasterBar.ts +++ b/src/model/MasterBar.ts @@ -13,6 +13,7 @@ import { TripletFeel } from '@src/model/TripletFeel'; * The MasterBar stores information about a bar which affects * all tracks. * @json + * @json_strict */ export class MasterBar { public static readonly MaxAlternateEndings: number = 8; @@ -117,8 +118,9 @@ export class MasterBar { /** * Gets or sets the fermatas for this bar. The key is the offset of the fermata in midi ticks. + * @json_add addFermata */ - public fermata: Map<number, Fermata> = new Map<number, Fermata>(); + public fermata: Map<number, Fermata> | null = null; /** * The timeline position of the voice within the whole score. (unit: midi ticks) @@ -133,7 +135,7 @@ export class MasterBar { /** * Calculates the time spent in this bar. (unit: midi ticks) */ - public calculateDuration(respectAnacrusis:boolean = true): number { + public calculateDuration(respectAnacrusis: boolean = true): number { if (this.isAnacrusis && respectAnacrusis) { let duration: number = 0; for (let track of this.score.tracks) { @@ -157,7 +159,12 @@ export class MasterBar { * @param fermata The fermata. */ public addFermata(offset: number, fermata: Fermata): void { - this.fermata.set(offset, fermata); + let fermataMap = this.fermata; + if (fermataMap === null) { + fermataMap = new Map<number, Fermata>(); + this.fermata = fermataMap; + } + fermataMap.set(offset, fermata); } /** @@ -166,8 +173,12 @@ export class MasterBar { * @returns */ public getFermata(beat: Beat): Fermata | null { - if (this.fermata.has(beat.playbackStart)) { - return this.fermata.get(beat.playbackStart)!; + const fermataMap = this.fermata; + if (fermataMap === null) { + return null; + } + if (fermataMap.has(beat.playbackStart)) { + return fermataMap.get(beat.playbackStart)!; } return null; } diff --git a/src/model/Note.ts b/src/model/Note.ts index b636d8ab4..bc2a65b38 100644 --- a/src/model/Note.ts +++ b/src/model/Note.ts @@ -21,12 +21,22 @@ import { ModelUtils } from '@src/model/ModelUtils'; import { PickStroke } from '@src/model/PickStroke'; import { PercussionMapper } from '@src/model/PercussionMapper'; +class NoteIdBag { + public tieDestinationNoteId: number = -1; + public tieOriginNoteId: number = -1; + public slurDestinationNoteId: number = -1; + public slurOriginNoteId: number = -1; + public hammerPullDestinationNoteId: number = -1; + public hammerPullOriginNoteId: number = -1; +} + /** * A note is a single played sound on a fretted instrument. * It consists of a fret offset and a string on which the note is played on. * It also can be modified by a lot of different effects. * @cloneable * @json + * @json_strict */ export class Note { public static GlobalNoteId: number = 0; @@ -74,7 +84,7 @@ export class Note { * @clone_add addBendPoint * @json_add addBendPoint */ - public bendPoints: BendPoint[] = []; + public bendPoints: BendPoint[] | null = null; /** * Gets or sets the bend point with the highest bend value. @@ -84,7 +94,7 @@ export class Note { public maxBendPoint: BendPoint | null = null; public get hasBend(): boolean { - return this.bendType !== BendType.None; + return this.bendPoints !== null && this.bendType !== BendType.None; } public get isStringed(): boolean { @@ -257,29 +267,19 @@ export class Note { return !!this.hammerPullOrigin; } - /** - * Gets the origin note id of the hammeron/pull-off of this note. - */ - public hammerPullOriginNoteId: number = -1; - /** * Gets the origin of the hammeron/pulloff of this note. + * @clone_ignore + * @json_ignore */ - public get hammerPullOrigin(): Note | null { - return this.hammerPullOriginNoteId === -1 ? null : this.beat.voice.bar.staff.track.score.getNoteById(this.hammerPullOriginNoteId); - } - - /** - * Gets the destination note id of the hammeron/pull-off of this note. - */ - public hammerPullDestinationNoteId: number = -1; + public hammerPullOrigin: Note | null = null; /** * Gets the destination for the hammeron/pullof started by this note. + * @clone_ignore + * @json_ignore */ - public get hammerPullDestination(): Note | null { - return this.hammerPullDestinationNoteId === -1 ? null : this.beat.voice.bar.staff.track.score.getNoteById(this.hammerPullDestinationNoteId); - } + public hammerPullDestination: Note | null = null; public get isSlurOrigin(): boolean { return !!this.slurDestination; @@ -290,30 +290,19 @@ export class Note { */ public isSlurDestination: boolean = false; - - /** - * Gets the note id where the slur of this note starts. - */ - public slurOriginNoteId: number = -1; - /** * Gets or sets the note where the slur of this note starts. + * @clone_ignore + * @json_ignore */ - public get slurOrigin(): Note | null { - return this.slurOriginNoteId === -1 ? null : this.beat.voice.bar.staff.track.score.getNoteById(this.slurOriginNoteId); - } - - /** - * Gets or sets the note id where the slur of this note ends. - */ - public slurDestinationNoteId: number = -1; + public slurOrigin: Note | null = null; /** * Gets or sets the note where the slur of this note ends. + * @clone_ignore + * @json_ignore */ - public get slurDestination(): Note | null { - return this.slurDestinationNoteId === -1 ? null : this.beat.voice.bar.staff.track.score.getNoteById(this.slurDestinationNoteId); - } + public slurDestination: Note | null = null; public get isHarmonic(): boolean { return this.harmonicType !== HarmonicType.None; @@ -397,30 +386,19 @@ export class Note { */ public vibrato: VibratoType = VibratoType.None; - - /** - * Gets the origin note id of the tied if this note is tied. - */ - public tieOriginNoteId: number = -1; - /** * Gets the origin of the tied if this note is tied. + * @clone_ignore + * @json_ignore */ - public get tieOrigin(): Note | null { - return this.tieOriginNoteId === -1 ? null : this.beat.voice.bar.staff.track.score.getNoteById(this.tieOriginNoteId); - } - - /** - * Gets the desination note id of the tie. - */ - public tieDestinationNoteId: number = -1; + public tieOrigin: Note | null = null; /** * Gets the desination of the tie. + * @clone_ignore + * @json_ignore */ - public get tieDestination(): Note | null { - return this.tieDestinationNoteId === -1 ? null : this.beat.voice.bar.staff.track.score.getNoteById(this.tieDestinationNoteId); - } + public tieDestination: Note | null = null; /** * Gets or sets whether this note is ends a tied note. @@ -428,7 +406,7 @@ export class Note { public isTieDestination: boolean = false; public get isTieOrigin(): boolean { - return this.tieDestinationNoteId !== -1; + return this.tieDestination !== null; } /** @@ -632,15 +610,15 @@ export class Note { public get initialBendValue(): number { if (this.hasBend) { - return Math.floor(this.bendPoints[0].value / 2); + return Math.floor(this.bendPoints![0].value / 2); } else if (this.bendOrigin) { - return Math.floor(this.bendOrigin.bendPoints[this.bendOrigin.bendPoints.length - 1].value / 2); + return Math.floor(this.bendOrigin.bendPoints![this.bendOrigin.bendPoints!.length - 1].value / 2); } else if (this.isTieDestination && this.tieOrigin!.bendOrigin) { - return Math.floor(this.tieOrigin!.bendOrigin.bendPoints[this.tieOrigin!.bendOrigin.bendPoints.length - 1].value / 2) ; + return Math.floor(this.tieOrigin!.bendOrigin.bendPoints![this.tieOrigin!.bendOrigin.bendPoints!.length - 1].value / 2); } else if (this.beat.hasWhammyBar) { - return Math.floor(this.beat.whammyBarPoints[0].value / 2); + return Math.floor(this.beat.whammyBarPoints![0].value / 2); } else if (this.beat.isContinuedWhammy) { - return Math.floor(this.beat.previousBeat!.whammyBarPoints[this.beat.previousBeat!.whammyBarPoints.length - 1].value / 2); + return Math.floor(this.beat.previousBeat!.whammyBarPoints![this.beat.previousBeat!.whammyBarPoints!.length - 1].value / 2); } return 0; } @@ -691,17 +669,17 @@ export class Note { public get hasQuarterToneOffset(): boolean { if (this.hasBend) { - return this.bendPoints[0].value % 2 !== 0; + return this.bendPoints![0].value % 2 !== 0; } if (this.bendOrigin) { - return this.bendOrigin.bendPoints[this.bendOrigin.bendPoints.length - 1].value % 2 !== 0; + return this.bendOrigin.bendPoints![this.bendOrigin.bendPoints!.length - 1].value % 2 !== 0; } if (this.beat.hasWhammyBar) { - return this.beat.whammyBarPoints[0].value % 2 !== 0; + return this.beat.whammyBarPoints![0].value % 2 !== 0; } if (this.beat.isContinuedWhammy) { return ( - this.beat.previousBeat!.whammyBarPoints[this.beat.previousBeat!.whammyBarPoints.length - 1].value % + this.beat.previousBeat!.whammyBarPoints![this.beat.previousBeat!.whammyBarPoints!.length - 1].value % 2 !== 0 ); @@ -710,7 +688,12 @@ export class Note { } public addBendPoint(point: BendPoint): void { - this.bendPoints.push(point); + let points = this.bendPoints; + if (points === null) { + points = []; + this.bendPoints = points; + } + points.push(point); if (!this.maxBendPoint || point.value > this.maxBendPoint.value) { this.maxBendPoint = point; } @@ -719,13 +702,13 @@ export class Note { } } - public finish(settings: Settings): void { + public finish(settings: Settings, sharedDataBag: Map<string, unknown>): void { let nextNoteOnLine: Lazy<Note | null> = new Lazy<Note | null>(() => Note.nextNoteOnSameLine(this)); let isSongBook: boolean = settings && settings.notation.notationMode === NotationMode.SongBook; // connect ties if (this.isTieDestination) { - this.chain(); + this.chain(sharedDataBag); // implicit let ring if (isSongBook && this.tieOrigin && this.tieOrigin.isLetRing) { this.isLetRing = true; @@ -756,8 +739,8 @@ export class Note { if (!hammerPullDestination) { this.isHammerPullOrigin = false; } else { - this.hammerPullDestinationNoteId = hammerPullDestination.id; - hammerPullDestination.hammerPullOriginNoteId = this.id; + this.hammerPullDestination = hammerPullDestination; + hammerPullDestination.hammerPullOrigin = this; } } // set slides @@ -792,14 +775,15 @@ export class Note { } // try to detect what kind of bend was used and cleans unneeded points if required // Guitar Pro 6 and above (gpif.xml) uses exactly 4 points to define all bends - if (this.bendPoints.length > 0 && this.bendType === BendType.Custom) { + const points = this.bendPoints; + if (points != null && points.length > 0 && this.bendType === BendType.Custom) { let isContinuedBend: boolean = this.isTieDestination && this.tieOrigin!.hasBend; this.isContinuedBend = isContinuedBend; - if (this.bendPoints.length === 4) { - let origin: BendPoint = this.bendPoints[0]; - let middle1: BendPoint = this.bendPoints[1]; - let middle2: BendPoint = this.bendPoints[2]; - let destination: BendPoint = this.bendPoints[3]; + if (points.length === 4) { + let origin: BendPoint = points[0]; + let middle1: BendPoint = points[1]; + let middle2: BendPoint = points[2]; + let destination: BendPoint = points[3]; // the middle points are used for holds, anything else is a new feature we do not support yet if (middle1.value === middle2.value) { // bend higher? @@ -808,43 +792,43 @@ export class Note { this.bendType = BendType.BendRelease; } else if (!isContinuedBend && origin.value > 0) { this.bendType = BendType.PrebendBend; - this.bendPoints.splice(2, 1); - this.bendPoints.splice(1, 1); + points.splice(2, 1); + points.splice(1, 1); } else { this.bendType = BendType.Bend; - this.bendPoints.splice(2, 1); - this.bendPoints.splice(1, 1); + points.splice(2, 1); + points.splice(1, 1); } } else if (destination.value < origin.value) { // origin must be > 0 otherwise it's no release, we cannot bend negative if (isContinuedBend) { this.bendType = BendType.Release; - this.bendPoints.splice(2, 1); - this.bendPoints.splice(1, 1); + points.splice(2, 1); + points.splice(1, 1); } else { this.bendType = BendType.PrebendRelease; - this.bendPoints.splice(2, 1); - this.bendPoints.splice(1, 1); + points.splice(2, 1); + points.splice(1, 1); } } else { if (middle1.value > origin.value) { this.bendType = BendType.BendRelease; } else if (origin.value > 0 && !isContinuedBend) { this.bendType = BendType.Prebend; - this.bendPoints.splice(2, 1); - this.bendPoints.splice(1, 1); + points.splice(2, 1); + points.splice(1, 1); } else { this.bendType = BendType.Hold; - this.bendPoints.splice(2, 1); - this.bendPoints.splice(1, 1); + points.splice(2, 1); + points.splice(1, 1); } } } else { Logger.warning('Model', 'Unsupported bend type detected, fallback to custom', null); } - } else if (this.bendPoints.length === 2) { - let origin: BendPoint = this.bendPoints[0]; - let destination: BendPoint = this.bendPoints[1]; + } else if (points.length === 2) { + let origin: BendPoint = points[0]; + let destination: BendPoint = points[1]; // bend higher? if (destination.value > origin.value) { if (!isContinuedBend && origin.value > 0) { @@ -863,7 +847,7 @@ export class Note { this.bendType = BendType.Hold; } } - } else if (this.bendPoints.length === 0) { + } else if (points === null || points.length === 0) { this.bendType = BendType.None; } @@ -969,30 +953,136 @@ export class Note { return null; } - public chain() { - this.beat.voice.bar.staff.track.score.registerNote(this); - if (!this.isTieDestination) { - return; - } + private static NoteIdLookupKey = "NoteIdLookup"; + + private _noteIdBag: NoteIdBag | null = null; + public chain(sharedDataBag: Map<string, unknown>) { + // if we have some IDs from a serialization flow, + // we need to lookup/register the notes correctly + if (this._noteIdBag != null) { + // get or create lookup + let noteIdLookup: Map<number, Note>; + if (sharedDataBag.has(Note.NoteIdLookupKey)) { + noteIdLookup = sharedDataBag.get(Note.NoteIdLookupKey) as Map<number, Note>; + } else { + noteIdLookup = new Map<number, Note>(); + sharedDataBag.set(Note.NoteIdLookupKey, noteIdLookup); + } - let tieOrigin: Note | null; - if (this.tieOriginNoteId === -1) { - tieOrigin = Note.findTieOrigin(this); - this.tieOriginNoteId = tieOrigin ? tieOrigin.id : -1; - } else { - tieOrigin = this.tieOrigin; - } + // if this note is a source note for any effect, remember it for later + // the destination note will look it up for linking + if (this._noteIdBag.hammerPullDestinationNoteId !== -1 || + this._noteIdBag.tieDestinationNoteId !== -1 || + this._noteIdBag.slurDestinationNoteId !== -1) { + noteIdLookup.set(this.id, this); + } + + // on any effect destiniation, lookup the origin which should already be + // registered + if (this._noteIdBag.hammerPullOriginNoteId !== -1) { + this.hammerPullOrigin = noteIdLookup.get(this._noteIdBag.hammerPullOriginNoteId)!; + this.hammerPullOrigin.hammerPullDestination = this; + } + if (this._noteIdBag.tieOriginNoteId !== -1) { + this.tieOrigin = noteIdLookup.get(this._noteIdBag.tieOriginNoteId)!; + this.tieOrigin.tieDestination = this; + } + if (this._noteIdBag.slurOriginNoteId !== -1) { + this.slurOrigin = noteIdLookup.get(this._noteIdBag.slurOriginNoteId)!; + this.slurOrigin.slurDestination = this; + } - if (!tieOrigin) { - this.isTieDestination = false; + this._noteIdBag = null; // not needed anymore } else { - tieOrigin.tieDestinationNoteId = this.id; - this.fret = tieOrigin.fret; - this.octave = tieOrigin.octave; - this.tone = tieOrigin.tone; - if (tieOrigin.hasBend) { - this.bendOrigin = this.tieOrigin; + if (!this.isTieDestination && this.tieOrigin == null) { + return; + } + + let tieOrigin = Note.findTieOrigin(this); + if (!tieOrigin) { + this.isTieDestination = false; + } else { + tieOrigin.tieDestination = this; + this.tieOrigin = tieOrigin; + this.fret = tieOrigin.fret; + this.octave = tieOrigin.octave; + this.tone = tieOrigin.tone; + if (tieOrigin.hasBend) { + this.bendOrigin = this.tieOrigin; + } } } } + + /** + * @internal + */ + public toJson(o: Map<string, unknown>) { + // inject linked note ids into JSON + if (this.tieDestination !== null) { + o.set("tiedestinationnoteid", this.tieDestination.id) + } + if (this.tieOrigin !== null) { + o.set("tieoriginnoteid", this.tieOrigin.id) + } + if (this.slurDestination !== null) { + o.set("slurdestinationnoteid", this.slurDestination.id) + } + if (this.slurOrigin !== null) { + o.set("sluroriginnoteid", this.slurOrigin.id) + } + if (this.hammerPullOrigin !== null) { + o.set("hammerpulloriginnoteid", this.hammerPullOrigin.id) + } + if (this.hammerPullDestination !== null) { + o.set("hammerpulldestinationnoteid", this.hammerPullDestination.id) + } + } + + /** + * @internal + */ + public setProperty(property: string, v: unknown): boolean { + switch (property) { + case "tiedestinationnoteid": + if (this._noteIdBag == null) { + this._noteIdBag = new NoteIdBag(); + } + this._noteIdBag.tieDestinationNoteId = v as number; + return true; + case "tieoriginnoteid": + if (this._noteIdBag == null) { + this._noteIdBag = new NoteIdBag(); + } + this._noteIdBag.tieOriginNoteId = v as number; + return true; + + case "slurdestinationnoteid": + if (this._noteIdBag == null) { + this._noteIdBag = new NoteIdBag(); + } + this._noteIdBag.slurDestinationNoteId = v as number; + return true; + case "sluroriginnoteid": + if (this._noteIdBag == null) { + this._noteIdBag = new NoteIdBag(); + } + this._noteIdBag.slurOriginNoteId = v as number; + return true; + + case "hammerpulloriginnoteid": + if (this._noteIdBag == null) { + this._noteIdBag = new NoteIdBag(); + } + this._noteIdBag.hammerPullOriginNoteId = v as number; + return true; + case "hammerpulldestinationnoteid": + if (this._noteIdBag == null) { + this._noteIdBag = new NoteIdBag(); + } + this._noteIdBag.hammerPullDestinationNoteId = v as number; + return true; + } + return false; + } } diff --git a/src/model/PlaybackInformation.ts b/src/model/PlaybackInformation.ts index 696b09a55..a03f162f2 100644 --- a/src/model/PlaybackInformation.ts +++ b/src/model/PlaybackInformation.ts @@ -2,6 +2,7 @@ * This public class stores the midi specific information of a track needed * for playback. * @json + * @json_strict */ export class PlaybackInformation { /** diff --git a/src/model/RenderStylesheet.ts b/src/model/RenderStylesheet.ts index 445a56355..7470197e7 100644 --- a/src/model/RenderStylesheet.ts +++ b/src/model/RenderStylesheet.ts @@ -2,6 +2,7 @@ * This class represents the rendering stylesheet. * It contains settings which control the display of the score when rendered. * @json + * @json_strict */ export class RenderStylesheet { /** diff --git a/src/model/Score.ts b/src/model/Score.ts index 4a9bcc18e..6cd580e2a 100644 --- a/src/model/Score.ts +++ b/src/model/Score.ts @@ -3,16 +3,15 @@ import { RenderStylesheet } from '@src/model/RenderStylesheet'; import { RepeatGroup } from '@src/model/RepeatGroup'; import { Track } from '@src/model/Track'; import { Settings } from '@src/Settings'; -import { Note } from '@src/model/Note'; /** * The score is the root node of the complete * model. It stores the basic information of * a song and stores the sub components. * @json + * @json_strict */ export class Score { - private _noteByIdLookup: Map<number, Note> = new Map<number, Note>(); private _currentRepeatGroup: RepeatGroup = new RepeatGroup(); /** @@ -130,20 +129,9 @@ export class Score { } public finish(settings: Settings): void { - this._noteByIdLookup.clear(); - + const sharedDataBag = new Map<string, unknown>() for (let i: number = 0, j: number = this.tracks.length; i < j; i++) { - this.tracks[i].finish(settings); + this.tracks[i].finish(settings, sharedDataBag); } } - - public registerNote(note: Note) { - this._noteByIdLookup.set(note.id, note); - } - - public getNoteById(noteId: number): Note | null { - return this._noteByIdLookup.has(noteId) - ? this._noteByIdLookup.get(noteId)! - : null; - } } diff --git a/src/model/Section.ts b/src/model/Section.ts index cc1fb3821..0897874c6 100644 --- a/src/model/Section.ts +++ b/src/model/Section.ts @@ -2,6 +2,7 @@ * This public class is used to describe the beginning of a * section within a song. It acts like a marker. * @json + * @json_strict */ export class Section { /** diff --git a/src/model/Staff.ts b/src/model/Staff.ts index 058a1e9dc..40a2e72a8 100644 --- a/src/model/Staff.ts +++ b/src/model/Staff.ts @@ -8,6 +8,7 @@ import { Tuning } from '@src/model/Tuning'; * This class describes a single staff within a track. There are instruments like pianos * where a single track can contain multiple staffs. * @json + * @json_strict */ export class Staff { /** @@ -32,7 +33,7 @@ export class Staff { * Gets or sets a list of all chords defined for this staff. {@link Beat.chordId} refers to entries in this lookup. * @json_add addChord */ - public chords: Map<string, Chord> = new Map<string, Chord>(); + public chords: Map<string, Chord> | null = null; /** * Gets or sets the fret on which a capo is set. @@ -97,17 +98,31 @@ export class Staff { */ public standardNotationLineCount: number = 5; - public finish(settings: Settings): void { + public finish(settings: Settings, sharedDataBag: Map<string, unknown>): void { this.stringTuning.finish(); for (let i: number = 0, j: number = this.bars.length; i < j; i++) { - this.bars[i].finish(settings); + this.bars[i].finish(settings, sharedDataBag); } } public addChord(chordId: string, chord: Chord): void { chord.staff = this; - this.chords.set(chordId, chord); + let chordMap = this.chords; + if (chordMap === null) { + chordMap = new Map<string, Chord>(); + this.chords = chordMap; + } + chordMap.set(chordId, chord); + } + + public hasChord(chordId: string): boolean { + return this.chords?.has(chordId) ?? false + } + + public getChord(chordId: string): Chord | null { + return this.chords?.get(chordId) ?? null } + public addBar(bar: Bar): void { let bars: Bar[] = this.bars; diff --git a/src/model/Track.ts b/src/model/Track.ts index 4f7f83c30..3005d7b49 100644 --- a/src/model/Track.ts +++ b/src/model/Track.ts @@ -11,6 +11,7 @@ import { InstrumentArticulation } from '@src/model/InstrumentArticulation'; * This public class describes a single track or instrument of score. * It is bascially a list of staffs containing individual music notation kinds. * @json + * @json_strict */ export class Track { private static readonly ShortNameMaxLength: number = 10; @@ -70,7 +71,7 @@ export class Track { this.staves.push(staff); } - public finish(settings: Settings): void { + public finish(settings: Settings, sharedDataBag: Map<string, unknown>): void { if (!this.shortName) { this.shortName = this.name; if (this.shortName.length > Track.ShortNameMaxLength) { @@ -78,7 +79,7 @@ export class Track { } } for (let i: number = 0, j: number = this.staves.length; i < j; i++) { - this.staves[i].finish(settings); + this.staves[i].finish(settings, sharedDataBag); } } diff --git a/src/model/Tuning.ts b/src/model/Tuning.ts index 970b2a8d1..ff8e70896 100644 --- a/src/model/Tuning.ts +++ b/src/model/Tuning.ts @@ -1,6 +1,7 @@ /** * This public class represents a predefined string tuning. * @json + * @json_strict */ export class Tuning { private static _sevenStrings: Tuning[] = []; diff --git a/src/model/Voice.ts b/src/model/Voice.ts index fa41a0efd..26ca5ea7a 100644 --- a/src/model/Voice.ts +++ b/src/model/Voice.ts @@ -8,6 +8,7 @@ import { GraceGroup } from '@src/model/GraceGroup'; * A voice represents a group of beats * that can be played during a bar. * @json + * @json_strict */ export class Voice { private _beatLookup!: Map<number, Beat>; @@ -62,7 +63,7 @@ export class Voice { } } - private chain(beat: Beat): void { + private chain(beat: Beat, sharedDataBag: Map<string, unknown>): void { if (!this.bar) { return; } @@ -79,7 +80,7 @@ export class Voice { } } - beat.chain(); + beat.chain(sharedDataBag); } public addGraceBeat(beat: Beat): void { @@ -104,13 +105,13 @@ export class Voice { return null; } - public finish(settings: Settings): void { + public finish(settings: Settings, sharedDataBag: Map<string, unknown>): void { this._beatLookup = new Map<number, Beat>(); let currentGraceGroup: GraceGroup | null = null; for (let index: number = 0; index < this.beats.length; index++) { let beat: Beat = this.beats[index]; beat.index = index; - this.chain(beat); + this.chain(beat, sharedDataBag); if (beat.graceType === GraceType.None) { beat.graceGroup = currentGraceGroup; if (currentGraceGroup) { @@ -130,7 +131,7 @@ export class Voice { for (let i: number = 0; i < this.beats.length; i++) { let beat: Beat = this.beats[i]; beat.index = i; - beat.finish(settings); + beat.finish(settings, sharedDataBag); // if this beat is a non-grace but has grace notes // we need to first steal the duration from the right beat diff --git a/src/platform/javascript/AlphaSynthAudioWorkletOutput.ts b/src/platform/javascript/AlphaSynthAudioWorkletOutput.ts index 5e2699acf..949605229 100644 --- a/src/platform/javascript/AlphaSynthAudioWorkletOutput.ts +++ b/src/platform/javascript/AlphaSynthAudioWorkletOutput.ts @@ -88,7 +88,7 @@ export class AlphaSynthWebWorklet { } } - public process( + public override process( _inputs: Float32Array[][], outputs: Float32Array[][], _parameters: Record<string, Float32Array> @@ -158,12 +158,12 @@ export class AlphaSynthWebWorklet { export class AlphaSynthAudioWorkletOutput extends AlphaSynthWebAudioOutputBase { private _worklet: AudioWorkletNode | null = null; - public open() { + public override open() { super.open(); this.onReady(); } - public play(): void { + public override play(): void { super.play(); let ctx = this._context!; // create a script processor node which will replace the silence with the generated audio @@ -198,7 +198,7 @@ export class AlphaSynthAudioWorkletOutput extends AlphaSynthWebAudioOutputBase { } } - public pause(): void { + public override pause(): void { super.pause(); if (this._worklet) { this._worklet.port.onmessage = null; diff --git a/src/platform/javascript/AlphaSynthScriptProcessorOutput.ts b/src/platform/javascript/AlphaSynthScriptProcessorOutput.ts index 7b57e973d..1888909e5 100644 --- a/src/platform/javascript/AlphaSynthScriptProcessorOutput.ts +++ b/src/platform/javascript/AlphaSynthScriptProcessorOutput.ts @@ -14,7 +14,7 @@ export class AlphaSynthScriptProcessorOutput extends AlphaSynthWebAudioOutputBas private _bufferCount: number = 0; private _requestedBufferCount: number = 0; - public open() { + public override open() { super.open(); this._bufferCount = Math.floor( (AlphaSynthWebAudioOutputBase.TotalBufferTimeInMilliseconds * this.sampleRate) / @@ -25,7 +25,7 @@ export class AlphaSynthScriptProcessorOutput extends AlphaSynthWebAudioOutputBas this.onReady(); } - public play(): void { + public override play(): void { super.play(); let ctx = this._context!; // create a script processor node which will replace the silence with the generated audio @@ -41,7 +41,7 @@ export class AlphaSynthScriptProcessorOutput extends AlphaSynthWebAudioOutputBas this._audioNode.connect(ctx.destination, 0, 0); } - public pause(): void { + public override pause(): void { super.pause(); if (this._audioNode) { this._audioNode.disconnect(0); diff --git a/src/platform/javascript/AlphaTabApi.ts b/src/platform/javascript/AlphaTabApi.ts index ebf186cab..8a9adaeae 100644 --- a/src/platform/javascript/AlphaTabApi.ts +++ b/src/platform/javascript/AlphaTabApi.ts @@ -20,7 +20,7 @@ export class AlphaTabApi extends AlphaTabApiBase<unknown> { super(new BrowserUiFacade(element), options); } - public tex(tex: string, tracks?: number[]): void { + public override tex(tex: string, tracks?: number[]): void { let browser: BrowserUiFacade = this.uiFacade as BrowserUiFacade; super.tex(tex, browser.parseTracks(tracks)); } @@ -104,17 +104,17 @@ export class AlphaTabApi extends AlphaTabApiBase<unknown> { document.body.removeChild(dlLink); } - public changeTrackMute(tracks: Track[], mute: boolean): void { + public override changeTrackMute(tracks: Track[], mute: boolean): void { let trackList: Track[] = this.trackIndexesToTracks((this.uiFacade as BrowserUiFacade).parseTracks(tracks)); super.changeTrackMute(trackList, mute); } - public changeTrackSolo(tracks: Track[], solo: boolean): void { + public override changeTrackSolo(tracks: Track[], solo: boolean): void { let trackList: Track[] = this.trackIndexesToTracks((this.uiFacade as BrowserUiFacade).parseTracks(tracks)); super.changeTrackSolo(trackList, solo); } - public changeTrackVolume(tracks: Track[], volume: number): void { + public override changeTrackVolume(tracks: Track[], volume: number): void { let trackList: Track[] = this.trackIndexesToTracks((this.uiFacade as BrowserUiFacade).parseTracks(tracks)); super.changeTrackVolume(trackList, volume); } diff --git a/src/platform/javascript/AlphaTabWebWorker.ts b/src/platform/javascript/AlphaTabWebWorker.ts index ba6832dd6..66aa94b55 100644 --- a/src/platform/javascript/AlphaTabWebWorker.ts +++ b/src/platform/javascript/AlphaTabWebWorker.ts @@ -80,7 +80,7 @@ export class AlphaTabWebWorker { break; case 'alphaTab.renderScore': this.updateFontSizes(data.fontSizes); - let score: any = JsonConverter.jsObjectToScore(data.score, this._renderer.settings); + let score: any = data.score == null ? null : JsonConverter.jsObjectToScore(data.score, this._renderer.settings); this.renderMultiple(score, data.trackIndexes); break; case 'alphaTab.updateSettings': @@ -104,7 +104,7 @@ export class AlphaTabWebWorker { SettingsSerializer.fromJson(this._renderer.settings, json); } - private renderMultiple(score: Score, trackIndexes: number[]): void { + private renderMultiple(score: Score | null, trackIndexes: number[] | null): void { try { this._renderer.renderScore(score, trackIndexes); } catch (e) { diff --git a/src/platform/javascript/AlphaTabWorkerScoreRenderer.ts b/src/platform/javascript/AlphaTabWorkerScoreRenderer.ts index 5f98d5fbc..d28f656fa 100644 --- a/src/platform/javascript/AlphaTabWorkerScoreRenderer.ts +++ b/src/platform/javascript/AlphaTabWorkerScoreRenderer.ts @@ -122,8 +122,8 @@ export class AlphaTabWorkerScoreRenderer<T> implements IScoreRenderer { } } - public renderScore(score: Score, trackIndexes: number[]): void { - let jsObject: unknown = JsonConverter.scoreToJsObject(score); + public renderScore(score: Score | null, trackIndexes: number[] | null): void { + let jsObject: unknown = score == null ? null : JsonConverter.scoreToJsObject(score); this._worker.postMessage({ cmd: 'alphaTab.renderScore', score: jsObject, diff --git a/src/rendering/BarRendererBase.ts b/src/rendering/BarRendererBase.ts index 53f2ac6be..d77acac84 100644 --- a/src/rendering/BarRendererBase.ts +++ b/src/rendering/BarRendererBase.ts @@ -311,7 +311,7 @@ export class BarRendererBase { } protected getVoiceContainer(voice: Voice): VoiceContainerGlyph | undefined { - return this._voiceContainers.get(voice.index); + return this._voiceContainers.has(voice.index) ? this._voiceContainers.get(voice.index) : undefined; } public getBeatContainer(beat: Beat): BeatContainerGlyph | undefined { diff --git a/src/rendering/EffectBand.ts b/src/rendering/EffectBand.ts index d0a6f9401..001708dd0 100644 --- a/src/rendering/EffectBand.ts +++ b/src/rendering/EffectBand.ts @@ -18,7 +18,7 @@ export class EffectBand extends Glyph { public isLinkedToPrevious: boolean = false; public firstBeat: Beat | null = null; public lastBeat: Beat | null = null; - public height: number = 0; + public override height: number = 0; public voice: Voice; public info: EffectBarRendererInfo; public slot: EffectBandSlot | null = null; @@ -29,7 +29,7 @@ export class EffectBand extends Glyph { this.info = info; } - public doLayout(): void { + public override doLayout(): void { super.doLayout(); for (let i: number = 0; i < this.renderer.bar.voices.length; i++) { this._effectGlyphs.push(new Map<number, EffectGlyph>()); @@ -139,7 +139,7 @@ export class EffectBand extends Glyph { } } - public paint(cx: number, cy: number, canvas: ICanvas): void { + public override paint(cx: number, cy: number, canvas: ICanvas): void { super.paint(cx, cy, canvas); // canvas.LineWidth = 1; // canvas.StrokeRect(cx + X, cy + Y, Renderer.Width, Slot.Shared.Height); diff --git a/src/rendering/EffectBarRenderer.ts b/src/rendering/EffectBarRenderer.ts index 479341040..10e028792 100644 --- a/src/rendering/EffectBarRenderer.ts +++ b/src/rendering/EffectBarRenderer.ts @@ -25,7 +25,7 @@ export class EffectBarRenderer extends BarRendererBase { this._infos = infos; } - protected updateSizes(): void { + protected override updateSizes(): void { this.topOverflow = 0; this.bottomOverflow = 0; this.topPadding = 0; @@ -34,7 +34,7 @@ export class EffectBarRenderer extends BarRendererBase { super.updateSizes(); } - public finalizeRenderer(): void { + public override finalizeRenderer(): void { super.finalizeRenderer(); this.updateHeight(); } @@ -55,7 +55,7 @@ export class EffectBarRenderer extends BarRendererBase { this.height = y; } - public applyLayoutingInfo(): boolean { + public override applyLayoutingInfo(): boolean { if (!super.applyLayoutingInfo()) { return false; } @@ -77,14 +77,14 @@ export class EffectBarRenderer extends BarRendererBase { return true; } - public scaleToWidth(width: number): void { + public override scaleToWidth(width: number): void { super.scaleToWidth(width); for (let effectBand of this._bands) { effectBand.alignGlyphs(); } } - protected createBeatGlyphs(): void { + protected override createBeatGlyphs(): void { this._bands = []; this._bandLookup = new Map<string, EffectBand>(); for (let voice of this.bar.voices) { @@ -110,7 +110,7 @@ export class EffectBarRenderer extends BarRendererBase { } } - protected createVoiceGlyphs(v: Voice): void { + protected override createVoiceGlyphs(v: Voice): void { for (let b of v.beats) { // we create empty glyphs as alignment references and to get the // effect bar sized @@ -124,7 +124,7 @@ export class EffectBarRenderer extends BarRendererBase { } } - public paint(cx: number, cy: number, canvas: ICanvas): void { + public override paint(cx: number, cy: number, canvas: ICanvas): void { this.paintBackground(cx, cy, canvas); // canvas.color = new Color(255, 0, 0, 100); // canvas.fillRect(cx + this.x, cy + this.y, this.width, this.height); diff --git a/src/rendering/IScoreRenderer.ts b/src/rendering/IScoreRenderer.ts index 8fd9711ba..b220d5917 100644 --- a/src/rendering/IScoreRenderer.ts +++ b/src/rendering/IScoreRenderer.ts @@ -33,7 +33,7 @@ export interface IScoreRenderer { * @param score The score defining the tracks. * @param trackIndexes The indexes of the tracks to draw. */ - renderScore(score: Score, trackIndexes: number[]): void; + renderScore(score: Score | null, trackIndexes: number[] | null): void; /** * Initiates the rendering of a partial render result which the renderer diff --git a/src/rendering/ScoreBarRenderer.ts b/src/rendering/ScoreBarRenderer.ts index 3084eb9fe..bc397493a 100644 --- a/src/rendering/ScoreBarRenderer.ts +++ b/src/rendering/ScoreBarRenderer.ts @@ -63,7 +63,7 @@ export class ScoreBarRenderer extends BarRendererBase { return (BarRendererBase.LineSpacing + 1) * this.scale; } - protected updateSizes(): void { + protected override updateSizes(): void { let res: RenderingResources = this.resources; let glyphOverflow: number = res.tablatureFont.size / 2 + res.tablatureFont.size * 0.2; this.topPadding = glyphOverflow * this.scale; @@ -81,7 +81,7 @@ export class ScoreBarRenderer extends BarRendererBase { this._firstLineY = (fullLineHeight - actualLineHeight) / 2; } - public doLayout(): void { + public override doLayout(): void { this.updateFirstLineY(); super.doLayout(); if (!this.bar.isEmpty && this.accidentalHelper.maxLineBeat) { @@ -116,7 +116,7 @@ export class ScoreBarRenderer extends BarRendererBase { } } - public paint(cx: number, cy: number, canvas: ICanvas): void { + public override paint(cx: number, cy: number, canvas: ICanvas): void { super.paint(cx, cy, canvas); this.paintBeams(cx, cy, canvas); this.paintTuplets(cx, cy, canvas); @@ -377,11 +377,11 @@ export class ScoreBarRenderer extends BarRendererBase { return this.getScoreHeight(size); } - public get middleYPosition(): number { + public override get middleYPosition(): number { return this.getScoreY(this.bar.staff.standardNotationLineCount - 1); } - public getNoteY(note: Note, requestedPosition: NoteYPosition): number { + public override getNoteY(note: Note, requestedPosition: NoteYPosition): number { let y = super.getNoteY(note, requestedPosition); if (isNaN(y)) { // NOTE: some might request the note position before the glyphs have been created @@ -397,7 +397,7 @@ export class ScoreBarRenderer extends BarRendererBase { return this.calculateBeamYWithDirection(h, x, h.direction); } - public applyLayoutingInfo(): boolean { + public override applyLayoutingInfo(): boolean { const result = super.applyLayoutingInfo(); if (result && this.bar.isMultiVoice) { // consider rest overflows @@ -736,7 +736,7 @@ export class ScoreBarRenderer extends BarRendererBase { } } - protected createPreBeatGlyphs(): void { + protected override createPreBeatGlyphs(): void { super.createPreBeatGlyphs(); if (this.bar.masterBar.isRepeatStart) { this.addPreBeatGlyph(new RepeatOpenGlyph(0, 0, 1.5, 3)); @@ -793,7 +793,7 @@ export class ScoreBarRenderer extends BarRendererBase { this.addPreBeatGlyph(new BarNumberGlyph(0, this.getScoreHeight(-0.5), this.bar.index + 1)); } - protected createBeatGlyphs(): void { + protected override createBeatGlyphs(): void { for (let v: number = 0; v < this.bar.voices.length; v++) { let voice: Voice = this.bar.voices[v]; if (this.hasVoiceContainer(voice)) { @@ -802,7 +802,7 @@ export class ScoreBarRenderer extends BarRendererBase { } } - protected createPostBeatGlyphs(): void { + protected override createPostBeatGlyphs(): void { super.createPostBeatGlyphs(); if (this.bar.masterBar.isRepeatEnd) { this.addPostBeatGlyph(new RepeatCloseGlyph(this.x, 0)); @@ -902,7 +902,7 @@ export class ScoreBarRenderer extends BarRendererBase { ); } - protected createVoiceGlyphs(v: Voice): void { + protected override createVoiceGlyphs(v: Voice): void { for (let i: number = 0, j: number = v.beats.length; i < j; i++) { let b: Beat = v.beats[i]; let container: ScoreBeatContainerGlyph = new ScoreBeatContainerGlyph(b, this.getVoiceContainer(v)!); @@ -940,7 +940,7 @@ export class ScoreBarRenderer extends BarRendererBase { } // private static readonly Random Random = new Random(); - protected paintBackground(cx: number, cy: number, canvas: ICanvas): void { + protected override paintBackground(cx: number, cy: number, canvas: ICanvas): void { super.paintBackground(cx, cy, canvas); let res: RenderingResources = this.resources; // canvas.color = Color.random(100); @@ -959,7 +959,7 @@ export class ScoreBarRenderer extends BarRendererBase { this.paintSimileMark(cx, cy, canvas); } - public completeBeamingHelper(helper: BeamingHelper) { + public override completeBeamingHelper(helper: BeamingHelper) { // for multi-voice bars we need to register the positions // for multi-voice rest displacement to avoid collisions if (this.bar.isMultiVoice && helper.highestNoteInHelper && helper.lowestNoteInHelper) { diff --git a/src/rendering/ScoreBeatContainerGlyph.ts b/src/rendering/ScoreBeatContainerGlyph.ts index 0b526dfa8..eb7d3f3f1 100644 --- a/src/rendering/ScoreBeatContainerGlyph.ts +++ b/src/rendering/ScoreBeatContainerGlyph.ts @@ -21,7 +21,7 @@ export class ScoreBeatContainerGlyph extends BeatContainerGlyph { super(beat, voiceContainer); } - public doLayout(): void { + public override doLayout(): void { this._effectSlur = null; this._effectEndSlur = null; super.doLayout(); @@ -52,7 +52,7 @@ export class ScoreBeatContainerGlyph extends BeatContainerGlyph { } } - protected createTies(n: Note): void { + protected override createTies(n: Note): void { // create a tie if any effect requires it if (!n.isVisible) { return; diff --git a/src/rendering/ScoreRenderer.ts b/src/rendering/ScoreRenderer.ts index d63a89c45..4662fd92a 100644 --- a/src/rendering/ScoreRenderer.ts +++ b/src/rendering/ScoreRenderer.ts @@ -64,23 +64,27 @@ export class ScoreRenderer implements IScoreRenderer { return false; } - public renderScore(score: Score, trackIndexes: number[]): void { + public renderScore(score: Score | null, trackIndexes: number[] | null): void { try { this.score = score; - let tracks: Track[]; - if (!trackIndexes) { - tracks = score.tracks.slice(0); - } else { - tracks = []; - for (let track of trackIndexes) { - if (track >= 0 && track < score.tracks.length) { - tracks.push(score.tracks[track]); + let tracks: Track[] | null = null; + + if (score != null && trackIndexes != null) { + if (!trackIndexes) { + tracks = score.tracks.slice(0); + } else { + tracks = []; + for (let track of trackIndexes) { + if (track >= 0 && track < score.tracks.length) { + tracks.push(score.tracks[track]); + } } } + if (tracks.length === 0 && score.tracks.length > 0) { + tracks.push(score.tracks[0]); + } } - if (tracks.length === 0 && score.tracks.length > 0) { - tracks.push(score.tracks[0]); - } + this.tracks = tracks; this.render(); } catch (e) { @@ -126,21 +130,28 @@ export class ScoreRenderer implements IScoreRenderer { return; } this.boundsLookup = new BoundsLookup(); - if (!this.tracks || this.tracks.length === 0) { - return; - } this.recreateCanvas(); this.canvas!.lineWidth = this.settings.display.scale; this.canvas!.settings = this.settings; - Logger.debug('Rendering', 'Rendering ' + this.tracks.length + ' tracks'); - for (let i: number = 0; i < this.tracks.length; i++) { - let track: Track = this.tracks[i]; - Logger.debug('Rendering', 'Track ' + i + ': ' + track.name); + + if (!this.tracks || this.tracks.length === 0 || !this.score) { + Logger.debug('Rendering', 'Clearing rendered tracks because no score or tracks are set'); + (this.preRender as EventEmitterOfT<boolean>).trigger(false); + this._renderedTracks = null; + this.onRenderFinished(); + (this.postRenderFinished as EventEmitter).trigger(); + Logger.debug('Rendering', 'Clearing finished'); + } else { + Logger.debug('Rendering', 'Rendering ' + this.tracks.length + ' tracks'); + for (let i: number = 0; i < this.tracks.length; i++) { + let track: Track = this.tracks[i]; + Logger.debug('Rendering', 'Track ' + i + ': ' + track.name); + } + (this.preRender as EventEmitterOfT<boolean>).trigger(false); + this.recreateLayout(); + this.layoutAndRender(); + Logger.debug('Rendering', 'Rendering finished'); } - (this.preRender as EventEmitterOfT<boolean>).trigger(false); - this.recreateLayout(); - this.layoutAndRender(); - Logger.debug('Rendering', 'Rendering finished'); } public resizeRender(): void { diff --git a/src/rendering/TabBarRenderer.ts b/src/rendering/TabBarRenderer.ts index 39dcc53df..434a5403e 100644 --- a/src/rendering/TabBarRenderer.ts +++ b/src/rendering/TabBarRenderer.ts @@ -51,7 +51,7 @@ export class TabBarRenderer extends BarRendererBase { return (TabBarRenderer.TabLineSpacing + 1) * this.scale; } - protected updateSizes(): void { + protected override updateSizes(): void { let res: RenderingResources = this.resources; let numberOverflow: number = (res.tablatureFont.size / 2 + res.tablatureFont.size * 0.2) * this.scale; this.topPadding = numberOverflow; @@ -72,7 +72,7 @@ export class TabBarRenderer extends BarRendererBase { this._firstLineY = (res.tablatureFont.size / 2 + res.tablatureFont.size * 0.2) * this.scale; } - public doLayout(): void { + public override doLayout(): void { this.updateFirstLineY(); super.doLayout(); if (this.settings.notation.rhythmMode !== TabRhythmMode.Hidden) { @@ -93,7 +93,7 @@ export class TabBarRenderer extends BarRendererBase { } } - protected createPreBeatGlyphs(): void { + protected override createPreBeatGlyphs(): void { super.createPreBeatGlyphs(); if (this.bar.masterBar.isRepeatStart) { this.addPreBeatGlyph(new RepeatOpenGlyph(0, 0, 1.5, 3)); @@ -145,7 +145,7 @@ export class TabBarRenderer extends BarRendererBase { ); } - protected createVoiceGlyphs(v: Voice): void { + protected override createVoiceGlyphs(v: Voice): void { for (let i: number = 0, j: number = v.beats.length; i < j; i++) { let b: Beat = v.beats[i]; let container: TabBeatContainerGlyph = new TabBeatContainerGlyph(b, this.getVoiceContainer(v)!); @@ -155,7 +155,7 @@ export class TabBarRenderer extends BarRendererBase { } } - protected createPostBeatGlyphs(): void { + protected override createPostBeatGlyphs(): void { super.createPostBeatGlyphs(); if (this.bar.masterBar.isRepeatEnd) { this.addPostBeatGlyph(new RepeatCloseGlyph(this.x, 0)); @@ -181,11 +181,11 @@ export class TabBarRenderer extends BarRendererBase { return this.lineOffset * line; } - public get middleYPosition(): number { + public override get middleYPosition(): number { return this.getTabY(this.bar.staff.tuning.length - 1); } - protected paintBackground(cx: number, cy: number, canvas: ICanvas): void { + protected override paintBackground(cx: number, cy: number, canvas: ICanvas): void { super.paintBackground(cx, cy, canvas); let res: RenderingResources = this.resources; // @@ -241,7 +241,7 @@ export class TabBarRenderer extends BarRendererBase { this.paintSimileMark(cx, cy, canvas); } - public paint(cx: number, cy: number, canvas: ICanvas): void { + public override paint(cx: number, cy: number, canvas: ICanvas): void { super.paint(cx, cy, canvas); if (this.settings.notation.rhythmMode !== TabRhythmMode.Hidden) { this.paintBeams(cx, cy, canvas); diff --git a/src/rendering/TabBarRendererFactory.ts b/src/rendering/TabBarRendererFactory.ts index f29b2a4bd..73d329b7b 100644 --- a/src/rendering/TabBarRendererFactory.ts +++ b/src/rendering/TabBarRendererFactory.ts @@ -26,7 +26,7 @@ export class TabBarRendererFactory extends BarRendererFactory { this.hideOnPercussionTrack = true; } - public canCreate(track: Track, staff: Staff): boolean { + public override canCreate(track: Track, staff: Staff): boolean { return staff.tuning.length > 0 && super.canCreate(track, staff); } diff --git a/src/rendering/effects/DummyEffectGlyph.ts b/src/rendering/effects/DummyEffectGlyph.ts index 579805218..12a0c8082 100644 --- a/src/rendering/effects/DummyEffectGlyph.ts +++ b/src/rendering/effects/DummyEffectGlyph.ts @@ -12,12 +12,12 @@ export class DummyEffectGlyph extends EffectGlyph { this._h = h; } - public doLayout(): void { + public override doLayout(): void { this.width = this._w * this.scale; this.height = this._h * this.scale; } - public paint(cx: number, cy: number, canvas: ICanvas): void { + public override paint(cx: number, cy: number, canvas: ICanvas): void { let c = canvas.color; canvas.color = Color.random(); canvas.fillRect(cx + this.x, cy + this.y, this.width, this.height); diff --git a/src/rendering/effects/HarmonicsEffectInfo.ts b/src/rendering/effects/HarmonicsEffectInfo.ts index b85a03166..74b38b181 100644 --- a/src/rendering/effects/HarmonicsEffectInfo.ts +++ b/src/rendering/effects/HarmonicsEffectInfo.ts @@ -13,7 +13,7 @@ export class HarmonicsEffectInfo extends NoteEffectInfoBase { private _beat: Beat | null = null; private _effectId: string; - public get effectId(): string { + public override get effectId(): string { return this._effectId; } diff --git a/src/rendering/effects/OttaviaEffectInfo.ts b/src/rendering/effects/OttaviaEffectInfo.ts index 34da9c1b8..a95098696 100644 --- a/src/rendering/effects/OttaviaEffectInfo.ts +++ b/src/rendering/effects/OttaviaEffectInfo.ts @@ -11,7 +11,7 @@ import { NotationElement } from '@src/NotationSettings'; export class OttaviaEffectInfo extends EffectBarRendererInfo { private _aboveStaff: boolean; - public get effectId(): string { + public override get effectId(): string { return 'ottavia-' + (this._aboveStaff ? 'above' : 'below'); } diff --git a/src/rendering/glyphs/AccentuationGlyph.ts b/src/rendering/glyphs/AccentuationGlyph.ts index ac0d290aa..1e295057c 100644 --- a/src/rendering/glyphs/AccentuationGlyph.ts +++ b/src/rendering/glyphs/AccentuationGlyph.ts @@ -21,12 +21,12 @@ export class AccentuationGlyph extends MusicFontGlyph { } } - public doLayout(): void { + public override doLayout(): void { this.width = 9 * this.scale; this.height = 9 * this.scale; } - public paint(cx: number, cy: number, canvas: ICanvas): void { + public override paint(cx: number, cy: number, canvas: ICanvas): void { super.paint(cx - 2 * this.scale, cy + this.height, canvas); } } diff --git a/src/rendering/glyphs/AccidentalGlyph.ts b/src/rendering/glyphs/AccidentalGlyph.ts index 1712cdd41..2dc75ec0c 100644 --- a/src/rendering/glyphs/AccidentalGlyph.ts +++ b/src/rendering/glyphs/AccidentalGlyph.ts @@ -35,7 +35,7 @@ export class AccidentalGlyph extends MusicFontGlyph { return MusicFontSymbol.None; } - public doLayout(): void { + public override doLayout(): void { switch (this._accidentalType) { case AccidentalType.DoubleFlat: this.width = 18; diff --git a/src/rendering/glyphs/AccidentalGroupGlyph.ts b/src/rendering/glyphs/AccidentalGroupGlyph.ts index 1f40ed60d..4bf76e7ef 100644 --- a/src/rendering/glyphs/AccidentalGroupGlyph.ts +++ b/src/rendering/glyphs/AccidentalGroupGlyph.ts @@ -13,7 +13,7 @@ export class AccidentalGroupGlyph extends GlyphGroup { super(0, 0); } - public doLayout(): void { + public override doLayout(): void { if (!this.glyphs || this.glyphs.length === 0) { this.width = 0; return; diff --git a/src/rendering/glyphs/AlternateEndingsGlyph.ts b/src/rendering/glyphs/AlternateEndingsGlyph.ts index 3f4740724..956d0d353 100644 --- a/src/rendering/glyphs/AlternateEndingsGlyph.ts +++ b/src/rendering/glyphs/AlternateEndingsGlyph.ts @@ -18,7 +18,7 @@ export class AlternateEndingsGlyph extends EffectGlyph { } } - public doLayout(): void { + public override doLayout(): void { super.doLayout(); this.height = this.renderer.resources.wordsFont.size + (AlternateEndingsGlyph.Padding * this.scale + 2); let endingsStrings: string = ''; @@ -29,7 +29,7 @@ export class AlternateEndingsGlyph extends EffectGlyph { this._endingsString = endingsStrings; } - public paint(cx: number, cy: number, canvas: ICanvas): void { + public override paint(cx: number, cy: number, canvas: ICanvas): void { super.paint(cx, cy, canvas); let baseline: TextBaseline = canvas.textBaseline; canvas.textBaseline = TextBaseline.Top; diff --git a/src/rendering/glyphs/ArticStaccatoAboveGlyph.ts b/src/rendering/glyphs/ArticStaccatoAboveGlyph.ts index 0460ca19a..7470050c3 100644 --- a/src/rendering/glyphs/ArticStaccatoAboveGlyph.ts +++ b/src/rendering/glyphs/ArticStaccatoAboveGlyph.ts @@ -8,12 +8,12 @@ export class ArticStaccatoAboveGlyph extends MusicFontGlyph { super(x, y, NoteHeadGlyph.GraceScale, MusicFontSymbol.ArticStaccatoAbove); } - public doLayout(): void { + public override doLayout(): void { this.width = NoteHeadGlyph.QuarterNoteHeadWidth * this.scale; this.height = 7 * this.scale; } - public paint(cx: number, cy: number, canvas: ICanvas): void { + public override paint(cx: number, cy: number, canvas: ICanvas): void { super.paint(cx + 3 * this.scale, cy + 5 * this.scale, canvas); } } diff --git a/src/rendering/glyphs/BarNumberGlyph.ts b/src/rendering/glyphs/BarNumberGlyph.ts index f285be205..8f1cb4eac 100644 --- a/src/rendering/glyphs/BarNumberGlyph.ts +++ b/src/rendering/glyphs/BarNumberGlyph.ts @@ -11,12 +11,12 @@ export class BarNumberGlyph extends Glyph { this._number = num; } - public doLayout(): void { + public override doLayout(): void { this.renderer.scoreRenderer.canvas!.font = this.renderer.resources.barNumberFont; this.width = this.renderer.scoreRenderer.canvas!.measureText(this._number.toString()) + 5 * this.scale; } - public paint(cx: number, cy: number, canvas: ICanvas): void { + public override paint(cx: number, cy: number, canvas: ICanvas): void { if (!this.renderer.staff.isFirstInAccolade) { return; } diff --git a/src/rendering/glyphs/BarSeperatorGlyph.ts b/src/rendering/glyphs/BarSeperatorGlyph.ts index ca1eb9698..20256656b 100644 --- a/src/rendering/glyphs/BarSeperatorGlyph.ts +++ b/src/rendering/glyphs/BarSeperatorGlyph.ts @@ -6,7 +6,7 @@ export class BarSeperatorGlyph extends Glyph { super(x, y); } - public doLayout(): void { + public override doLayout(): void { if (this.renderer.isLast) { this.width = 15 * this.scale; } else if ( @@ -23,7 +23,7 @@ export class BarSeperatorGlyph extends Glyph { } } - public paint(cx: number, cy: number, canvas: ICanvas): void { + public override paint(cx: number, cy: number, canvas: ICanvas): void { let blockWidth: number = 4 * this.scale; let top: number = cy + this.y + this.renderer.topPadding; let bottom: number = cy + this.y + this.renderer.height - this.renderer.bottomPadding; diff --git a/src/rendering/glyphs/BeatContainerGlyph.ts b/src/rendering/glyphs/BeatContainerGlyph.ts index 6f90c20dd..c1df82293 100644 --- a/src/rendering/glyphs/BeatContainerGlyph.ts +++ b/src/rendering/glyphs/BeatContainerGlyph.ts @@ -76,7 +76,7 @@ export class BeatContainerGlyph extends Glyph { this.updateWidth(); } - public doLayout(): void { + public override doLayout(): void { this.preNotes.x = 0; this.preNotes.renderer = this.renderer; this.preNotes.container = this; @@ -136,7 +136,7 @@ export class BeatContainerGlyph extends Glyph { return 'b' + beat.id; } - public paint(cx: number, cy: number, canvas: ICanvas): void { + public override paint(cx: number, cy: number, canvas: ICanvas): void { if (this.beat.voice.isEmpty) { return; } diff --git a/src/rendering/glyphs/BeatGlyphBase.ts b/src/rendering/glyphs/BeatGlyphBase.ts index 958c953f2..1c9066036 100644 --- a/src/rendering/glyphs/BeatGlyphBase.ts +++ b/src/rendering/glyphs/BeatGlyphBase.ts @@ -11,7 +11,7 @@ export class BeatGlyphBase extends GlyphGroup { super(0, 0); } - public doLayout(): void { + public override doLayout(): void { // left to right layout let w: number = 0; if (this.glyphs) { diff --git a/src/rendering/glyphs/BeatVibratoGlyph.ts b/src/rendering/glyphs/BeatVibratoGlyph.ts index 2bf97313c..5b242e6e6 100644 --- a/src/rendering/glyphs/BeatVibratoGlyph.ts +++ b/src/rendering/glyphs/BeatVibratoGlyph.ts @@ -12,7 +12,7 @@ export class BeatVibratoGlyph extends GroupedEffectGlyph { this._type = type; } - public doLayout(): void { + public override doLayout(): void { super.doLayout(); switch (this._type) { case VibratoType.Slight: diff --git a/src/rendering/glyphs/BendNoteHeadGroupGlyph.ts b/src/rendering/glyphs/BendNoteHeadGroupGlyph.ts index df192d462..b318ed6a3 100644 --- a/src/rendering/glyphs/BendNoteHeadGroupGlyph.ts +++ b/src/rendering/glyphs/BendNoteHeadGroupGlyph.ts @@ -77,7 +77,7 @@ export class BendNoteHeadGroupGlyph extends ScoreNoteChordGlyphBase { this.isEmpty = false; } - public doLayout(): void { + public override doLayout(): void { let x: number = 0; if (this._showParenthesis) { this._preNoteParenthesis!.x = x; @@ -103,7 +103,7 @@ export class BendNoteHeadGroupGlyph extends ScoreNoteChordGlyphBase { } } - public paint(cx: number, cy: number, canvas: ICanvas): void { + public override paint(cx: number, cy: number, canvas: ICanvas): void { // canvas.Color = Color.Random(); // canvas.FillRect(cx + X, cy + Y, Width, 10); // canvas.Color = Renderer.Resources.MainGlyphColor; diff --git a/src/rendering/glyphs/ChordDiagramGlyph.ts b/src/rendering/glyphs/ChordDiagramGlyph.ts index 7e9e2f489..e7b26d8b4 100644 --- a/src/rendering/glyphs/ChordDiagramGlyph.ts +++ b/src/rendering/glyphs/ChordDiagramGlyph.ts @@ -21,7 +21,7 @@ export class ChordDiagramGlyph extends EffectGlyph { this._chord = chord; } - public doLayout(): void { + public override doLayout(): void { super.doLayout(); const scale = this.scale; let res: RenderingResources = this.renderer.resources; @@ -43,7 +43,7 @@ export class ChordDiagramGlyph extends EffectGlyph { 2 * ChordDiagramGlyph.Padding * scale; } - public paint(cx: number, cy: number, canvas: ICanvas): void { + public override paint(cx: number, cy: number, canvas: ICanvas): void { cx += this.x + ChordDiagramGlyph.Padding * this.scale + this._firstFretSpacing; cy += this.y; let w: number = this.width - 2 * ChordDiagramGlyph.Padding * this.scale + this.scale - this._firstFretSpacing; diff --git a/src/rendering/glyphs/CircleGlyph.ts b/src/rendering/glyphs/CircleGlyph.ts index 5b5392655..fd6d09e40 100644 --- a/src/rendering/glyphs/CircleGlyph.ts +++ b/src/rendering/glyphs/CircleGlyph.ts @@ -9,11 +9,11 @@ export class CircleGlyph extends Glyph { this._size = size; } - public doLayout(): void { + public override doLayout(): void { this.width = this._size + 3 * this.scale; } - public paint(cx: number, cy: number, canvas: ICanvas): void { + public override paint(cx: number, cy: number, canvas: ICanvas): void { canvas.fillCircle(cx + this.x, cy + this.y, this._size); } } diff --git a/src/rendering/glyphs/ClefGlyph.ts b/src/rendering/glyphs/ClefGlyph.ts index ad02ab76f..f75f0cac8 100644 --- a/src/rendering/glyphs/ClefGlyph.ts +++ b/src/rendering/glyphs/ClefGlyph.ts @@ -15,7 +15,7 @@ export class ClefGlyph extends MusicFontGlyph { this._clefOttava = clefOttava; } - public doLayout(): void { + public override doLayout(): void { switch (this._clef) { case Clef.Neutral: this.width = 15 * this.scale; @@ -46,7 +46,7 @@ export class ClefGlyph extends MusicFontGlyph { } } - public paint(cx: number, cy: number, canvas: ICanvas): void { + public override paint(cx: number, cy: number, canvas: ICanvas): void { super.paint(cx, cy, canvas); let numberGlyph: Glyph; let top: boolean = false; diff --git a/src/rendering/glyphs/CrescendoGlyph.ts b/src/rendering/glyphs/CrescendoGlyph.ts index a685c6064..51355202f 100644 --- a/src/rendering/glyphs/CrescendoGlyph.ts +++ b/src/rendering/glyphs/CrescendoGlyph.ts @@ -16,7 +16,7 @@ export class CrescendoGlyph extends GroupedEffectGlyph { this.y = y; } - public doLayout(): void { + public override doLayout(): void { super.doLayout(); this.height = 17 * this.scale; } diff --git a/src/rendering/glyphs/DeadNoteHeadGlyph.ts b/src/rendering/glyphs/DeadNoteHeadGlyph.ts index 80395aefd..0cb828da7 100644 --- a/src/rendering/glyphs/DeadNoteHeadGlyph.ts +++ b/src/rendering/glyphs/DeadNoteHeadGlyph.ts @@ -10,7 +10,7 @@ export class DeadNoteHeadGlyph extends MusicFontGlyph { this._isGrace = isGrace; } - public doLayout(): void { + public override doLayout(): void { this.width = 9 * (this._isGrace ? NoteHeadGlyph.GraceScale : 1) * this.scale; this.height = NoteHeadGlyph.NoteHeadHeight * this.scale; } diff --git a/src/rendering/glyphs/DiamondNoteHeadGlyph.ts b/src/rendering/glyphs/DiamondNoteHeadGlyph.ts index 58e7135f2..5821005c5 100644 --- a/src/rendering/glyphs/DiamondNoteHeadGlyph.ts +++ b/src/rendering/glyphs/DiamondNoteHeadGlyph.ts @@ -23,7 +23,7 @@ export class DiamondNoteHeadGlyph extends MusicFontGlyph { } } - public doLayout(): void { + public override doLayout(): void { this.width = 9 * (this._isGrace ? NoteHeadGlyph.GraceScale : 1) * this.scale; this.height = NoteHeadGlyph.NoteHeadHeight * this.scale; } diff --git a/src/rendering/glyphs/DigitGlyph.ts b/src/rendering/glyphs/DigitGlyph.ts index ab48188d0..41bd0f988 100644 --- a/src/rendering/glyphs/DigitGlyph.ts +++ b/src/rendering/glyphs/DigitGlyph.ts @@ -11,7 +11,7 @@ export class DigitGlyph extends MusicFontGlyph { this._scale = scale; } - public doLayout(): void { + public override doLayout(): void { this.width = this.getDigitWidth(this._digit) * this.scale * this._scale; } diff --git a/src/rendering/glyphs/DynamicsGlyph.ts b/src/rendering/glyphs/DynamicsGlyph.ts index 0184e589d..8a4326a8d 100644 --- a/src/rendering/glyphs/DynamicsGlyph.ts +++ b/src/rendering/glyphs/DynamicsGlyph.ts @@ -7,7 +7,7 @@ export class DynamicsGlyph extends MusicFontGlyph { super(x, y, 0.6, DynamicsGlyph.getSymbol(dynamics)); } - public doLayout(): void { + public override doLayout(): void { super.doLayout(); this.height = 17 * this.scale; this.y += this.height / 2; diff --git a/src/rendering/glyphs/FadeInGlyph.ts b/src/rendering/glyphs/FadeInGlyph.ts index d9e9254d9..c94e873a7 100644 --- a/src/rendering/glyphs/FadeInGlyph.ts +++ b/src/rendering/glyphs/FadeInGlyph.ts @@ -2,12 +2,12 @@ import { ICanvas } from '@src/platform/ICanvas'; import { EffectGlyph } from '@src/rendering/glyphs/EffectGlyph'; export class FadeInGlyph extends EffectGlyph { - public doLayout(): void { + public override doLayout(): void { super.doLayout(); this.height = 17 * this.scale; } - public paint(cx: number, cy: number, canvas: ICanvas): void { + public override paint(cx: number, cy: number, canvas: ICanvas): void { let size: number = 6 * this.scale; let width: number = Math.max(this.width, 14 * this.scale); let offset: number = this.height / 2; diff --git a/src/rendering/glyphs/FermataGlyph.ts b/src/rendering/glyphs/FermataGlyph.ts index 361b30591..111fb5621 100644 --- a/src/rendering/glyphs/FermataGlyph.ts +++ b/src/rendering/glyphs/FermataGlyph.ts @@ -21,12 +21,12 @@ export class FermataGlyph extends MusicFontGlyph { } } - public doLayout(): void { + public override doLayout(): void { this.width = 23 * this.scale; this.height = 12 * this.scale; } - public paint(cx: number, cy: number, canvas: ICanvas): void { + public override paint(cx: number, cy: number, canvas: ICanvas): void { super.paint(cx - this.width / 2, cy + this.height, canvas); } } diff --git a/src/rendering/glyphs/FlagGlyph.ts b/src/rendering/glyphs/FlagGlyph.ts index 4b6a2b213..b4e2d8550 100644 --- a/src/rendering/glyphs/FlagGlyph.ts +++ b/src/rendering/glyphs/FlagGlyph.ts @@ -11,7 +11,7 @@ export class FlagGlyph extends MusicFontGlyph { super(x, y, isGrace ? NoteHeadGlyph.GraceScale : 1, FlagGlyph.getSymbol(duration, direction, isGrace)); } - public doLayout(): void { + public override doLayout(): void { this.width = 0; } diff --git a/src/rendering/glyphs/GhostNoteContainerGlyph.ts b/src/rendering/glyphs/GhostNoteContainerGlyph.ts index 840577a82..ccc81cece 100644 --- a/src/rendering/glyphs/GhostNoteContainerGlyph.ts +++ b/src/rendering/glyphs/GhostNoteContainerGlyph.ts @@ -52,7 +52,7 @@ export class GhostNoteContainerGlyph extends Glyph { return false; } - public doLayout(): void { + public override doLayout(): void { let sr: ScoreBarRenderer = this.renderer as ScoreBarRenderer; this._infos.sort((a, b) => { return a.line - b.line; @@ -79,7 +79,7 @@ export class GhostNoteContainerGlyph extends Glyph { this.width = this._glyphs.length > 0 ? this._glyphs[0].width : 0; } - public paint(cx: number, cy: number, canvas: ICanvas): void { + public override paint(cx: number, cy: number, canvas: ICanvas): void { super.paint(cx, cy, canvas); for (let g of this._glyphs) { g.paint(cx + this.x, cy + this.y, canvas); diff --git a/src/rendering/glyphs/GhostParenthesisGlyph.ts b/src/rendering/glyphs/GhostParenthesisGlyph.ts index 9813adf98..6fae22fcc 100644 --- a/src/rendering/glyphs/GhostParenthesisGlyph.ts +++ b/src/rendering/glyphs/GhostParenthesisGlyph.ts @@ -11,12 +11,12 @@ export class GhostParenthesisGlyph extends Glyph { this._isOpen = isOpen; } - public doLayout(): void { + public override doLayout(): void { super.doLayout(); this.width = GhostParenthesisGlyph.Size * this.scale; } - public paint(cx: number, cy: number, canvas: ICanvas): void { + public override paint(cx: number, cy: number, canvas: ICanvas): void { if (this._isOpen) { TieGlyph.paintTie( canvas, diff --git a/src/rendering/glyphs/GlyphGroup.ts b/src/rendering/glyphs/GlyphGroup.ts index d2d23b68f..dfb177230 100644 --- a/src/rendering/glyphs/GlyphGroup.ts +++ b/src/rendering/glyphs/GlyphGroup.ts @@ -16,7 +16,7 @@ export class GlyphGroup extends Glyph { super(x, y); } - public doLayout(): void { + public override doLayout(): void { if (!this.glyphs || this.glyphs.length === 0) { this.width = 0; return; @@ -41,7 +41,7 @@ export class GlyphGroup extends Glyph { this.glyphs.push(g); } - public paint(cx: number, cy: number, canvas: ICanvas): void { + public override paint(cx: number, cy: number, canvas: ICanvas): void { let glyphs = this.glyphs; if (!glyphs || glyphs.length === 0) { return; diff --git a/src/rendering/glyphs/GroupedEffectGlyph.ts b/src/rendering/glyphs/GroupedEffectGlyph.ts index 85c70c703..4d8ece9e8 100644 --- a/src/rendering/glyphs/GroupedEffectGlyph.ts +++ b/src/rendering/glyphs/GroupedEffectGlyph.ts @@ -26,7 +26,7 @@ export abstract class GroupedEffectGlyph extends EffectGlyph { ); } - public paint(cx: number, cy: number, canvas: ICanvas): void { + public override paint(cx: number, cy: number, canvas: ICanvas): void { // if we are linked with the previous, the first glyph of the group will also render this one. if (this.isLinkedWithPrevious) { return; diff --git a/src/rendering/glyphs/GuitarGolpeGlyph.ts b/src/rendering/glyphs/GuitarGolpeGlyph.ts index 046bc3f80..0e4e2deae 100644 --- a/src/rendering/glyphs/GuitarGolpeGlyph.ts +++ b/src/rendering/glyphs/GuitarGolpeGlyph.ts @@ -8,12 +8,12 @@ export class GuitarGolpeGlyph extends MusicFontGlyph { super(x, y, NoteHeadGlyph.GraceScale, MusicFontSymbol.GuitarGolpe); } - public doLayout(): void { + public override doLayout(): void { this.width = 9 * this.scale; this.height = 10 * this.scale; } - public paint(cx: number, cy: number, canvas: ICanvas): void { + public override paint(cx: number, cy: number, canvas: ICanvas): void { super.paint(cx, cy + this.height, canvas); } } diff --git a/src/rendering/glyphs/LeftHandTapGlyph.ts b/src/rendering/glyphs/LeftHandTapGlyph.ts index e29650ed5..830e51235 100644 --- a/src/rendering/glyphs/LeftHandTapGlyph.ts +++ b/src/rendering/glyphs/LeftHandTapGlyph.ts @@ -9,13 +9,13 @@ export class LeftHandTapGlyph extends EffectGlyph { super(0, 0); } - public doLayout(): void { + public override doLayout(): void { super.doLayout(); const font = this.renderer.resources.effectFont; this.height = font.size + LeftHandTapGlyph.Padding * this.scale; } - public paint(cx: number, cy: number, canvas: ICanvas): void { + public override paint(cx: number, cy: number, canvas: ICanvas): void { let res: RenderingResources = this.renderer.resources; canvas.font = res.effectFont; let old: TextAlign = canvas.textAlign; diff --git a/src/rendering/glyphs/LeftToRightLayoutingGlyphGroup.ts b/src/rendering/glyphs/LeftToRightLayoutingGlyphGroup.ts index 62e4b200b..7565a1c65 100644 --- a/src/rendering/glyphs/LeftToRightLayoutingGlyphGroup.ts +++ b/src/rendering/glyphs/LeftToRightLayoutingGlyphGroup.ts @@ -7,7 +7,7 @@ export class LeftToRightLayoutingGlyphGroup extends GlyphGroup { this.glyphs = []; } - public addGlyph(g: Glyph): void { + public override addGlyph(g: Glyph): void { g.x = this.glyphs!.length === 0 ? 0 diff --git a/src/rendering/glyphs/LineRangedGlyph.ts b/src/rendering/glyphs/LineRangedGlyph.ts index 572c84e43..c427dfb3a 100644 --- a/src/rendering/glyphs/LineRangedGlyph.ts +++ b/src/rendering/glyphs/LineRangedGlyph.ts @@ -15,7 +15,7 @@ export class LineRangedGlyph extends GroupedEffectGlyph { this._label = label; } - public doLayout(): void { + public override doLayout(): void { if (this.renderer.settings.notation.extendLineEffectsToBeatEnd) { this.endPosition = BeatXPosition.EndBeat; this.forceGroupedRendering = true; @@ -24,7 +24,7 @@ export class LineRangedGlyph extends GroupedEffectGlyph { this.height = this.renderer.resources.effectFont.size; } - protected paintNonGrouped(cx: number, cy: number, canvas: ICanvas): void { + protected override paintNonGrouped(cx: number, cy: number, canvas: ICanvas): void { let res: RenderingResources = this.renderer.resources; canvas.font = res.effectFont; let x: TextAlign = canvas.textAlign; diff --git a/src/rendering/glyphs/LyricsGlyph.ts b/src/rendering/glyphs/LyricsGlyph.ts index 0a03654ea..90cf0785c 100644 --- a/src/rendering/glyphs/LyricsGlyph.ts +++ b/src/rendering/glyphs/LyricsGlyph.ts @@ -15,12 +15,12 @@ export class LyricsGlyph extends EffectGlyph { this.textAlign = textAlign; } - public doLayout(): void { + public override doLayout(): void { super.doLayout(); this.height = this.font.size * this._lines.length; } - public paint(cx: number, cy: number, canvas: ICanvas): void { + public override paint(cx: number, cy: number, canvas: ICanvas): void { canvas.font = this.font; let old: TextAlign = canvas.textAlign; canvas.textAlign = this.textAlign; diff --git a/src/rendering/glyphs/MusicFontGlyph.ts b/src/rendering/glyphs/MusicFontGlyph.ts index 960fd37c2..858528110 100644 --- a/src/rendering/glyphs/MusicFontGlyph.ts +++ b/src/rendering/glyphs/MusicFontGlyph.ts @@ -12,7 +12,7 @@ export class MusicFontGlyph extends EffectGlyph { this.symbol = symbol; } - public paint(cx: number, cy: number, canvas: ICanvas): void { + public override paint(cx: number, cy: number, canvas: ICanvas): void { canvas.fillMusicFontSymbol(cx + this.x, cy + this.y, this.glyphScale * this.scale, this.symbol, false); } } diff --git a/src/rendering/glyphs/NoteHeadGlyph.ts b/src/rendering/glyphs/NoteHeadGlyph.ts index cf1b326f4..7f79f43ac 100644 --- a/src/rendering/glyphs/NoteHeadGlyph.ts +++ b/src/rendering/glyphs/NoteHeadGlyph.ts @@ -16,12 +16,12 @@ export class NoteHeadGlyph extends MusicFontGlyph { this._duration = duration; } - public paint(cx: number, cy: number, canvas: ICanvas): void { + public override paint(cx: number, cy: number, canvas: ICanvas): void { let offset: number = this._isGrace ? this.scale : 0; canvas.fillMusicFontSymbol(cx + this.x, cy + this.y + offset, this.glyphScale * this.scale, this.symbol, false); } - public doLayout(): void { + public override doLayout(): void { let scale: number = (this._isGrace ? NoteHeadGlyph.GraceScale : 1) * this.scale; switch (this._duration) { case Duration.QuadrupleWhole: diff --git a/src/rendering/glyphs/NoteNumberGlyph.ts b/src/rendering/glyphs/NoteNumberGlyph.ts index 0f09ac9c9..80f98c040 100644 --- a/src/rendering/glyphs/NoteNumberGlyph.ts +++ b/src/rendering/glyphs/NoteNumberGlyph.ts @@ -24,7 +24,7 @@ export class NoteNumberGlyph extends Glyph { this._note = note; } - public doLayout(): void { + public override doLayout(): void { let n: Note = this._note; let fret: number = n.fret - n.beat.voice.bar.staff.transpositionPitch; if (n.harmonicType === HarmonicType.Natural && n.harmonicValue !== 0) { @@ -91,7 +91,7 @@ export class NoteNumberGlyph extends Glyph { } } - public paint(cx: number, cy: number, canvas: ICanvas): void { + public override paint(cx: number, cy: number, canvas: ICanvas): void { if (this.isEmpty) { return; } diff --git a/src/rendering/glyphs/NoteVibratoGlyph.ts b/src/rendering/glyphs/NoteVibratoGlyph.ts index ce2e3075c..c5be86739 100644 --- a/src/rendering/glyphs/NoteVibratoGlyph.ts +++ b/src/rendering/glyphs/NoteVibratoGlyph.ts @@ -20,7 +20,7 @@ export class NoteVibratoGlyph extends GroupedEffectGlyph { this._partialWaves = partialWaves; } - public doLayout(): void { + public override doLayout(): void { super.doLayout(); let symbolHeight: number = 0; switch (this._type) { diff --git a/src/rendering/glyphs/NumberGlyph.ts b/src/rendering/glyphs/NumberGlyph.ts index 2a859964d..10d33f7d6 100644 --- a/src/rendering/glyphs/NumberGlyph.ts +++ b/src/rendering/glyphs/NumberGlyph.ts @@ -14,7 +14,7 @@ export class NumberGlyph extends GlyphGroup { this._scale = scale; } - public doLayout(): void { + public override doLayout(): void { let i: number = this._number; while (i > 0) { let num: number = i % 10; diff --git a/src/rendering/glyphs/OttavaGlyph.ts b/src/rendering/glyphs/OttavaGlyph.ts index 512707f3a..96b99559d 100644 --- a/src/rendering/glyphs/OttavaGlyph.ts +++ b/src/rendering/glyphs/OttavaGlyph.ts @@ -14,12 +14,12 @@ export class OttavaGlyph extends GroupedEffectGlyph { this._aboveStaff = aboveStaff; } - public doLayout(): void { + public override doLayout(): void { super.doLayout(); this.height = 13 * this.scale; } - protected paintNonGrouped(cx: number, cy: number, canvas: ICanvas): void { + protected override paintNonGrouped(cx: number, cy: number, canvas: ICanvas): void { this.paintOttava(cx, cy, canvas); } diff --git a/src/rendering/glyphs/PercussionNoteHeadGlyph.ts b/src/rendering/glyphs/PercussionNoteHeadGlyph.ts index b0dd35f67..4e0919398 100644 --- a/src/rendering/glyphs/PercussionNoteHeadGlyph.ts +++ b/src/rendering/glyphs/PercussionNoteHeadGlyph.ts @@ -15,7 +15,7 @@ export class PercussionNoteHeadGlyph extends MusicFontGlyph { this._articulation = articulation; } - public paint(cx: number, cy: number, canvas: ICanvas): void { + public override paint(cx: number, cy: number, canvas: ICanvas): void { let offset: number = this._isGrace ? this.scale : 0; canvas.fillMusicFontSymbol(cx + this.x, cy + this.y + offset, this.glyphScale * this.scale, this.symbol, false); @@ -24,7 +24,7 @@ export class PercussionNoteHeadGlyph extends MusicFontGlyph { } } - public doLayout(): void { + public override doLayout(): void { let scale: number = (this._isGrace ? NoteHeadGlyph.GraceScale : 1) * this.scale; switch (this.symbol) { case MusicFontSymbol.NoteheadWhole: diff --git a/src/rendering/glyphs/PickStrokeGlyph.ts b/src/rendering/glyphs/PickStrokeGlyph.ts index 99aa950be..0673b77d8 100644 --- a/src/rendering/glyphs/PickStrokeGlyph.ts +++ b/src/rendering/glyphs/PickStrokeGlyph.ts @@ -10,12 +10,12 @@ export class PickStrokeGlyph extends MusicFontGlyph { super(x, y, NoteHeadGlyph.GraceScale, PickStrokeGlyph.getSymbol(pickStroke)); } - public doLayout(): void { + public override doLayout(): void { this.width = 9 * this.scale; this.height = 13 * this.scale; } - public paint(cx: number, cy: number, canvas: ICanvas): void { + public override paint(cx: number, cy: number, canvas: ICanvas): void { super.paint(cx, cy + this.height, canvas); } diff --git a/src/rendering/glyphs/PictEdgeOfCymbalGlyph.ts b/src/rendering/glyphs/PictEdgeOfCymbalGlyph.ts index 4610c8bb5..2bf7679c6 100644 --- a/src/rendering/glyphs/PictEdgeOfCymbalGlyph.ts +++ b/src/rendering/glyphs/PictEdgeOfCymbalGlyph.ts @@ -7,12 +7,12 @@ export class PictEdgeOfCymbalGlyph extends MusicFontGlyph { super(x, y, 0.5, MusicFontSymbol.PictEdgeOfCymbal); } - public doLayout(): void { + public override doLayout(): void { this.width = 22 * this.scale; this.height = 15 * this.scale; } - public paint(cx: number, cy: number, canvas: ICanvas): void { + public override paint(cx: number, cy: number, canvas: ICanvas): void { super.paint(cx - 3 * this.scale, cy + this.height, canvas); } } diff --git a/src/rendering/glyphs/RepeatCloseGlyph.ts b/src/rendering/glyphs/RepeatCloseGlyph.ts index 93fb4479e..94f8c6735 100644 --- a/src/rendering/glyphs/RepeatCloseGlyph.ts +++ b/src/rendering/glyphs/RepeatCloseGlyph.ts @@ -6,11 +6,11 @@ export class RepeatCloseGlyph extends Glyph { super(x, y); } - public doLayout(): void { + public override doLayout(): void { this.width = 11 * this.scale; } - public paint(cx: number, cy: number, canvas: ICanvas): void { + public override paint(cx: number, cy: number, canvas: ICanvas): void { let blockWidth: number = 4 * this.scale; let top: number = cy + this.y + this.renderer.topPadding; let bottom: number = cy + this.y + this.renderer.height - this.renderer.bottomPadding; diff --git a/src/rendering/glyphs/RepeatCountGlyph.ts b/src/rendering/glyphs/RepeatCountGlyph.ts index d3858cfbb..32708841d 100644 --- a/src/rendering/glyphs/RepeatCountGlyph.ts +++ b/src/rendering/glyphs/RepeatCountGlyph.ts @@ -11,11 +11,11 @@ export class RepeatCountGlyph extends Glyph { this._count = count; } - public doLayout(): void { + public override doLayout(): void { this.width = 0; } - public paint(cx: number, cy: number, canvas: ICanvas): void { + public override paint(cx: number, cy: number, canvas: ICanvas): void { let res: RenderingResources = this.renderer.resources; let oldAlign: TextAlign = canvas.textAlign; canvas.font = res.barNumberFont; diff --git a/src/rendering/glyphs/RepeatOpenGlyph.ts b/src/rendering/glyphs/RepeatOpenGlyph.ts index 819e69adc..79970ca7e 100644 --- a/src/rendering/glyphs/RepeatOpenGlyph.ts +++ b/src/rendering/glyphs/RepeatOpenGlyph.ts @@ -13,11 +13,11 @@ export class RepeatOpenGlyph extends Glyph { this._circleSize = circleSize; } - public doLayout(): void { + public override doLayout(): void { this.width = 13 * this.scale; } - public paint(cx: number, cy: number, canvas: ICanvas): void { + public override paint(cx: number, cy: number, canvas: ICanvas): void { let blockWidth: number = 4 * this.scale; let top: number = cy + this.y + this.renderer.topPadding; let bottom: number = cy + this.y + this.renderer.height - this.renderer.bottomPadding; diff --git a/src/rendering/glyphs/RowContainerGlyph.ts b/src/rendering/glyphs/RowContainerGlyph.ts index 49da572bd..9934315d1 100644 --- a/src/rendering/glyphs/RowContainerGlyph.ts +++ b/src/rendering/glyphs/RowContainerGlyph.ts @@ -14,7 +14,7 @@ export class RowContainerGlyph extends GlyphGroup { this._align = align; } - public doLayout(): void { + public override doLayout(): void { let x: number = 0; let y: number = 0; let padding: number = 2 * RowContainerGlyph.Padding * this.scale; @@ -46,7 +46,7 @@ export class RowContainerGlyph extends GlyphGroup { this.height = y + padding; } - public paint(cx: number, cy: number, canvas: ICanvas): void { + public override paint(cx: number, cy: number, canvas: ICanvas): void { for (let row of this._rows) { row.paint(cx + this.x, cy + this.y + RowContainerGlyph.Padding * this.scale, canvas); } diff --git a/src/rendering/glyphs/RowGlyphContainer.ts b/src/rendering/glyphs/RowGlyphContainer.ts index 5ff2586f9..3b392dcc4 100644 --- a/src/rendering/glyphs/RowGlyphContainer.ts +++ b/src/rendering/glyphs/RowGlyphContainer.ts @@ -12,7 +12,7 @@ export class RowGlyphContainer extends GlyphGroup { this._align = align; } - public doLayout(): void { + public override doLayout(): void { let x: number = 0; switch (this._align) { case TextAlign.Left: diff --git a/src/rendering/glyphs/ScoreBeatGlyph.ts b/src/rendering/glyphs/ScoreBeatGlyph.ts index be2151ce5..35d1c1602 100644 --- a/src/rendering/glyphs/ScoreBeatGlyph.ts +++ b/src/rendering/glyphs/ScoreBeatGlyph.ts @@ -38,21 +38,21 @@ export class ScoreBeatGlyph extends BeatOnNoteGlyphBase { public noteHeads: ScoreNoteChordGlyph | null = null; public restGlyph: ScoreRestGlyph | null = null; - public getNoteX(note: Note, requestedPosition: NoteXPosition): number { + public override getNoteX(note: Note, requestedPosition: NoteXPosition): number { return this.noteHeads ? this.noteHeads.getNoteX(note, requestedPosition) : 0; } - public buildBoundingsLookup(beatBounds: BeatBounds, cx: number, cy: number) { + public override buildBoundingsLookup(beatBounds: BeatBounds, cx: number, cy: number) { if (this.noteHeads) { this.noteHeads.buildBoundingsLookup(beatBounds, cx + this.x, cy + this.y); } } - public getNoteY(note: Note, requestedPosition: NoteYPosition): number { + public override getNoteY(note: Note, requestedPosition: NoteYPosition): number { return this.noteHeads ? this.noteHeads.getNoteY(note, requestedPosition) : 0; } - public updateBeamingHelper(): void { + public override updateBeamingHelper(): void { if (this.noteHeads) { this.noteHeads.updateBeamingHelper(this.container.x + this.x); } else if (this.restGlyph) { @@ -72,13 +72,13 @@ export class ScoreBeatGlyph extends BeatOnNoteGlyphBase { } } - public paint(cx: number, cy: number, canvas: ICanvas): void { + public override paint(cx: number, cy: number, canvas: ICanvas): void { if (!this._skipPaint) { super.paint(cx, cy, canvas); } } - public doLayout(): void { + public override doLayout(): void { // create glyphs let sr: ScoreBarRenderer = this.renderer as ScoreBarRenderer; if (!this.container.beat.isEmpty) { diff --git a/src/rendering/glyphs/ScoreBeatPreNotesGlyph.ts b/src/rendering/glyphs/ScoreBeatPreNotesGlyph.ts index 0f5c3a67f..f9ecb8be0 100644 --- a/src/rendering/glyphs/ScoreBeatPreNotesGlyph.ts +++ b/src/rendering/glyphs/ScoreBeatPreNotesGlyph.ts @@ -23,7 +23,7 @@ export class ScoreBeatPreNotesGlyph extends BeatGlyphBase { public accidentals: AccidentalGroupGlyph | null = null; - public doLayout(): void { + public override doLayout(): void { if (!this.container.beat.isRest) { let accidentals: AccidentalGroupGlyph = new AccidentalGroupGlyph(); accidentals.renderer = this.renderer; @@ -42,7 +42,7 @@ export class ScoreBeatPreNotesGlyph extends BeatGlyphBase { case BendType.Prebend: case BendType.PrebendRelease: preBends.addGlyph( - note.displayValue - ((note.bendPoints[0].value / 2) | 0), + note.displayValue - ((note.bendPoints![0].value / 2) | 0), false ); break; @@ -52,7 +52,7 @@ export class ScoreBeatPreNotesGlyph extends BeatGlyphBase { case WhammyType.PrediveDive: case WhammyType.Predive: this._prebends.addGlyph( - note.displayValue - ((note.beat.whammyBarPoints[0].value / 2) | 0), + note.displayValue - ((note.beat.whammyBarPoints![0].value / 2) | 0), false ); break; diff --git a/src/rendering/glyphs/ScoreBendGlyph.ts b/src/rendering/glyphs/ScoreBendGlyph.ts index f30cdbbb4..3b746c0b3 100644 --- a/src/rendering/glyphs/ScoreBendGlyph.ts +++ b/src/rendering/glyphs/ScoreBendGlyph.ts @@ -43,7 +43,7 @@ export class ScoreBendGlyph extends ScoreHelperNotesBaseGlyph { this._endNoteGlyph = endGlyphs; this.BendNoteHeads.push(endGlyphs); } - let lastBendPoint: BendPoint = note.bendPoints[note.bendPoints.length - 1]; + let lastBendPoint: BendPoint = note.bendPoints![note.bendPoints!.length - 1]; endGlyphs.addGlyph(this.getBendNoteValue(note, lastBendPoint), lastBendPoint.value % 2 !== 0); } break; @@ -57,7 +57,7 @@ export class ScoreBendGlyph extends ScoreHelperNotesBaseGlyph { this._endNoteGlyph = endGlyphs; this.BendNoteHeads.push(endGlyphs); } - let lastBendPoint: BendPoint = note.bendPoints[note.bendPoints.length - 1]; + let lastBendPoint: BendPoint = note.bendPoints![note.bendPoints!.length - 1]; endGlyphs.addGlyph(this.getBendNoteValue(note, lastBendPoint), lastBendPoint.value % 2 !== 0); } } @@ -71,9 +71,9 @@ export class ScoreBendGlyph extends ScoreHelperNotesBaseGlyph { middleGlyphs.renderer = this.renderer; this.BendNoteHeads.push(middleGlyphs); } - let middleBendPoint: BendPoint = note.bendPoints[1]; + let middleBendPoint: BendPoint = note.bendPoints![1]; middleGlyphs.addGlyph( - this.getBendNoteValue(note, note.bendPoints[1]), + this.getBendNoteValue(note, note.bendPoints![1]), middleBendPoint.value % 2 !== 0 ); let endGlyphs = this._endNoteGlyph; @@ -83,14 +83,14 @@ export class ScoreBendGlyph extends ScoreHelperNotesBaseGlyph { this._endNoteGlyph = endGlyphs; this.BendNoteHeads.push(endGlyphs); } - let lastBendPoint: BendPoint = note.bendPoints[note.bendPoints.length - 1]; + let lastBendPoint: BendPoint = note.bendPoints![note.bendPoints!.length - 1]; endGlyphs.addGlyph(this.getBendNoteValue(note, lastBendPoint), lastBendPoint.value % 2 !== 0); } break; } } - public paint(cx: number, cy: number, canvas: ICanvas): void { + public override paint(cx: number, cy: number, canvas: ICanvas): void { // Draw note heads let startNoteRenderer: ScoreBarRenderer = this.renderer.scoreRenderer.layout!.getRendererForBar( this.renderer.staff.staveId, @@ -222,7 +222,7 @@ export class ScoreBendGlyph extends ScoreHelperNotesBaseGlyph { startNoteRenderer.y + startNoteRenderer.getScoreY( startNoteRenderer.accidentalHelper.getNoteLineForValue( - note.displayValue - ((note.bendPoints[0].value / 2) | 0), + note.displayValue - ((note.bendPoints![0].value / 2) | 0), false ) ) + @@ -246,7 +246,7 @@ export class ScoreBendGlyph extends ScoreHelperNotesBaseGlyph { let endY: number = 0; switch (note.bendType) { case BendType.Bend: - endValue = this.getBendNoteValue(note, note.bendPoints[note.bendPoints.length - 1]); + endValue = this.getBendNoteValue(note, note.bendPoints![note.bendPoints!.length - 1]); endY = this._endNoteGlyph!.getNoteValueY(endValue) + heightOffset; this.drawBendSlur( canvas, @@ -260,7 +260,7 @@ export class ScoreBendGlyph extends ScoreHelperNotesBaseGlyph { ); break; case BendType.BendRelease: - let middleValue: number = this.getBendNoteValue(note, note.bendPoints[1]); + let middleValue: number = this.getBendNoteValue(note, note.bendPoints![1]); let middleY: number = this._middleNoteGlyph!.getNoteValueY(middleValue) + heightOffset; this.drawBendSlur( canvas, @@ -272,7 +272,7 @@ export class ScoreBendGlyph extends ScoreHelperNotesBaseGlyph { this.scale, slurText ); - endValue = this.getBendNoteValue(note, note.bendPoints[note.bendPoints.length - 1]); + endValue = this.getBendNoteValue(note, note.bendPoints![note.bendPoints!.length - 1]); endY = this._endNoteGlyph!.getNoteValueY(endValue) + heightOffset; this.drawBendSlur( canvas, @@ -287,7 +287,7 @@ export class ScoreBendGlyph extends ScoreHelperNotesBaseGlyph { break; case BendType.Release: if (this.BendNoteHeads.length > 0) { - endValue = this.getBendNoteValue(note, note.bendPoints[note.bendPoints.length - 1]); + endValue = this.getBendNoteValue(note, note.bendPoints![note.bendPoints!.length - 1]); endY = this.BendNoteHeads[0].getNoteValueY(endValue) + heightOffset; this.drawBendSlur( canvas, @@ -313,7 +313,7 @@ export class ScoreBendGlyph extends ScoreHelperNotesBaseGlyph { startNoteRenderer.y + startNoteRenderer.getScoreY( startNoteRenderer.accidentalHelper.getNoteLineForValue( - note.displayValue - ((note.bendPoints[0].value / 2) | 0), + note.displayValue - ((note.bendPoints![0].value / 2) | 0), false ) ) + @@ -328,7 +328,7 @@ export class ScoreBendGlyph extends ScoreHelperNotesBaseGlyph { this.scale ); if (this.BendNoteHeads.length > 0) { - endValue = this.getBendNoteValue(note, note.bendPoints[note.bendPoints.length - 1]); + endValue = this.getBendNoteValue(note, note.bendPoints![note.bendPoints!.length - 1]); endY = this.BendNoteHeads[0].getNoteValueY(endValue) + heightOffset; this.drawBendSlur( canvas, diff --git a/src/rendering/glyphs/ScoreBrushGlyph.ts b/src/rendering/glyphs/ScoreBrushGlyph.ts index 97ddac879..99dd596ac 100644 --- a/src/rendering/glyphs/ScoreBrushGlyph.ts +++ b/src/rendering/glyphs/ScoreBrushGlyph.ts @@ -15,11 +15,11 @@ export class ScoreBrushGlyph extends Glyph { this._beat = beat; } - public doLayout(): void { + public override doLayout(): void { this.width = 10 * this.scale; } - public paint(cx: number, cy: number, canvas: ICanvas): void { + public override paint(cx: number, cy: number, canvas: ICanvas): void { let scoreBarRenderer: ScoreBarRenderer = this.renderer as ScoreBarRenderer; let lineSize: number = scoreBarRenderer.lineOffset; let startY: number = cy + this.y + (scoreBarRenderer.getNoteY(this._beat.maxNote!, NoteYPosition.Bottom) - lineSize); diff --git a/src/rendering/glyphs/ScoreHelperNotesBaseGlyph.ts b/src/rendering/glyphs/ScoreHelperNotesBaseGlyph.ts index bd717a19d..b8be64a1c 100644 --- a/src/rendering/glyphs/ScoreHelperNotesBaseGlyph.ts +++ b/src/rendering/glyphs/ScoreHelperNotesBaseGlyph.ts @@ -23,7 +23,7 @@ export class ScoreHelperNotesBaseGlyph extends Glyph { TieGlyph.drawBendSlur(canvas, x1, y1, x2, y2, down, scale, slurText); } - public doLayout(): void { + public override doLayout(): void { super.doLayout(); this.width = 0; for (let noteHeads of this.BendNoteHeads) { diff --git a/src/rendering/glyphs/ScoreLegatoGlyph.ts b/src/rendering/glyphs/ScoreLegatoGlyph.ts index 19e255670..4d46b79bb 100644 --- a/src/rendering/glyphs/ScoreLegatoGlyph.ts +++ b/src/rendering/glyphs/ScoreLegatoGlyph.ts @@ -12,11 +12,11 @@ export class ScoreLegatoGlyph extends TieGlyph { super(startBeat, endBeat, forEnd); } - public doLayout(): void { + public override doLayout(): void { super.doLayout(); } - protected getBeamDirection(beat: Beat, noteRenderer: BarRendererBase): BeamDirection { + protected override getBeamDirection(beat: Beat, noteRenderer: BarRendererBase): BeamDirection { if (beat.isRest) { return BeamDirection.Up; } @@ -29,7 +29,7 @@ export class ScoreLegatoGlyph extends TieGlyph { } } - protected getStartY(): number { + protected override getStartY(): number { if (this.startBeat!.isRest) { // below all lines return (this.startNoteRenderer as ScoreBarRenderer).getScoreY(9); @@ -43,7 +43,7 @@ export class ScoreLegatoGlyph extends TieGlyph { } } - protected getEndY(): number { + protected override getEndY(): number { const endNoteScoreRenderer = this.endNoteRenderer as ScoreBarRenderer; if (this.endBeat!.isRest) { switch (this.tieDirection) { @@ -89,11 +89,11 @@ export class ScoreLegatoGlyph extends TieGlyph { } } - protected getStartX(): number { + protected override getStartX(): number { return this.startNoteRenderer!.getBeatX(this.startBeat!, BeatXPosition.MiddleNotes); } - protected getEndX(): number { + protected override getEndX(): number { const endBeamDirection = (this.endNoteRenderer as ScoreBarRenderer).getBeatDirection(this.endBeat!); return this.endNoteRenderer!.getBeatX( this.endBeat!, diff --git a/src/rendering/glyphs/ScoreNoteChordGlyph.ts b/src/rendering/glyphs/ScoreNoteChordGlyph.ts index 8d654fa0b..bebb9df18 100644 --- a/src/rendering/glyphs/ScoreNoteChordGlyph.ts +++ b/src/rendering/glyphs/ScoreNoteChordGlyph.ts @@ -97,7 +97,7 @@ export class ScoreNoteChordGlyph extends ScoreNoteChordGlyphBase { } } - public doLayout(): void { + public override doLayout(): void { super.doLayout(); let direction: BeamDirection = this.direction; for (const effect of this.aboveBeatEffects.values()) { @@ -150,7 +150,7 @@ export class ScoreNoteChordGlyph extends ScoreNoteChordGlyphBase { } } - public paint(cx: number, cy: number, canvas: ICanvas): void { + public override paint(cx: number, cy: number, canvas: ICanvas): void { // TODO: this method seems to be quite heavy according to the profiler, why? let scoreRenderer: ScoreBarRenderer = this.renderer as ScoreBarRenderer; // diff --git a/src/rendering/glyphs/ScoreNoteChordGlyphBase.ts b/src/rendering/glyphs/ScoreNoteChordGlyphBase.ts index 6f00bcf73..36351f360 100644 --- a/src/rendering/glyphs/ScoreNoteChordGlyphBase.ts +++ b/src/rendering/glyphs/ScoreNoteChordGlyphBase.ts @@ -43,7 +43,7 @@ export abstract class ScoreNoteChordGlyphBase extends Glyph { return !!this.maxNote && this.maxNote.line > 8; } - public doLayout(): void { + public override doLayout(): void { this._infos.sort((a, b) => { return b.line - a.line; }); @@ -101,7 +101,7 @@ export abstract class ScoreNoteChordGlyphBase extends Glyph { this.width = w; } - public paint(cx: number, cy: number, canvas: ICanvas): void { + public override paint(cx: number, cy: number, canvas: ICanvas): void { cx += this.x; cy += this.y; // TODO: this method seems to be quite heavy according to the profiler, why? diff --git a/src/rendering/glyphs/ScoreRestGlyph.ts b/src/rendering/glyphs/ScoreRestGlyph.ts index dd7c52470..6fac3b44c 100644 --- a/src/rendering/glyphs/ScoreRestGlyph.ts +++ b/src/rendering/glyphs/ScoreRestGlyph.ts @@ -62,7 +62,7 @@ export class ScoreRestGlyph extends MusicFontGlyph { return 10; } - public doLayout(): void { + public override doLayout(): void { this.width = ScoreRestGlyph.getSize(this._duration) * this.scale; } diff --git a/src/rendering/glyphs/ScoreSlideLineGlyph.ts b/src/rendering/glyphs/ScoreSlideLineGlyph.ts index 608f40232..7807159f0 100644 --- a/src/rendering/glyphs/ScoreSlideLineGlyph.ts +++ b/src/rendering/glyphs/ScoreSlideLineGlyph.ts @@ -26,11 +26,11 @@ export class ScoreSlideLineGlyph extends Glyph { this._parent = parent; } - public doLayout(): void { + public override doLayout(): void { this.width = 0; } - public paint(cx: number, cy: number, canvas: ICanvas): void { + public override paint(cx: number, cy: number, canvas: ICanvas): void { this.paintSlideIn(cx, cy, canvas); this.drawSlideOut(cx, cy, canvas); } diff --git a/src/rendering/glyphs/ScoreSlurGlyph.ts b/src/rendering/glyphs/ScoreSlurGlyph.ts index e56b737c1..3879c2cfe 100644 --- a/src/rendering/glyphs/ScoreSlurGlyph.ts +++ b/src/rendering/glyphs/ScoreSlurGlyph.ts @@ -16,11 +16,11 @@ export class ScoreSlurGlyph extends ScoreLegatoGlyph { this._endNote = endNote; } - protected getTieHeight(startX: number, startY: number, endX: number, endY: number): number { + protected override getTieHeight(startX: number, startY: number, endX: number, endY: number): number { return Math.log2(endX - startX + 1) * this.renderer.settings.notation.slurHeight; } - protected getStartY(): number { + protected override getStartY(): number { if (this.isStartCentered()) { switch (this.tieDirection) { case BeamDirection.Up: @@ -34,7 +34,7 @@ export class ScoreSlurGlyph extends ScoreLegatoGlyph { return this.startNoteRenderer!.getNoteY(this._startNote, NoteYPosition.Center); } - protected getEndY(): number { + protected override getEndY(): number { if (this.isEndCentered()) { if (this.isEndOnStem()) { switch (this.tieDirection) { @@ -78,13 +78,13 @@ export class ScoreSlurGlyph extends ScoreLegatoGlyph { return startBeamDirection !== endBeamDirection && this.startBeat!.graceType === GraceType.None; } - protected getStartX(): number { + protected override getStartX(): number { return this.isStartCentered() ? this.startNoteRenderer!.getBeatX(this._startNote.beat, BeatXPosition.MiddleNotes) : this.startNoteRenderer!.getNoteX(this._startNote, NoteXPosition.Right); } - protected getEndX(): number { + protected override getEndX(): number { if (this.isEndCentered()) { if (this.isEndOnStem()) { return this.endNoteRenderer!.getBeatX(this._endNote.beat, BeatXPosition.Stem); diff --git a/src/rendering/glyphs/ScoreTieGlyph.ts b/src/rendering/glyphs/ScoreTieGlyph.ts index 195371ee2..4ac97223e 100644 --- a/src/rendering/glyphs/ScoreTieGlyph.ts +++ b/src/rendering/glyphs/ScoreTieGlyph.ts @@ -16,15 +16,15 @@ export class ScoreTieGlyph extends TieGlyph { this.endNote = endNote; } - protected shouldDrawBendSlur() { + protected override shouldDrawBendSlur() { return this.renderer.settings.notation.extendBendArrowsOnTiedNotes && !!this.startNote.bendOrigin && this.startNote.isTieOrigin; } - public doLayout(): void { + public override doLayout(): void { super.doLayout(); } - protected getBeamDirection(beat: Beat, noteRenderer: BarRendererBase): BeamDirection { + protected override getBeamDirection(beat: Beat, noteRenderer: BarRendererBase): BeamDirection { // invert direction (if stems go up, ties go down to not cross them) switch ((noteRenderer as ScoreBarRenderer).getBeatDirection(beat)) { case BeamDirection.Up: @@ -34,7 +34,7 @@ export class ScoreTieGlyph extends TieGlyph { } } - protected getStartY(): number { + protected override getStartY(): number { if (this.startBeat!.isRest) { // below all lines return (this.startNoteRenderer as ScoreBarRenderer).getScoreY(9); @@ -48,7 +48,7 @@ export class ScoreTieGlyph extends TieGlyph { } } - protected getEndY(): number { + protected override getEndY(): number { const endNoteScoreRenderer = this.endNoteRenderer as ScoreBarRenderer; if (this.endBeat!.isRest) { switch (this.tieDirection) { @@ -67,11 +67,11 @@ export class ScoreTieGlyph extends TieGlyph { } } - protected getStartX(): number { + protected override getStartX(): number { return this.startNoteRenderer!.getBeatX(this.startNote.beat, BeatXPosition.PostNotes); } - protected getEndX(): number { + protected override getEndX(): number { return this.endNoteRenderer!.getBeatX(this.endNote.beat, BeatXPosition.PreNotes); } } diff --git a/src/rendering/glyphs/ScoreWhammyBarGlyph.ts b/src/rendering/glyphs/ScoreWhammyBarGlyph.ts index cc5292591..dfb134dc1 100644 --- a/src/rendering/glyphs/ScoreWhammyBarGlyph.ts +++ b/src/rendering/glyphs/ScoreWhammyBarGlyph.ts @@ -27,7 +27,7 @@ export class ScoreWhammyBarGlyph extends ScoreHelperNotesBaseGlyph { this._beat = beat; } - public doLayout(): void { + public override doLayout(): void { let whammyMode: NotationMode = this.renderer.settings.notation.notationMode; switch (this._beat.whammyBarType) { case WhammyType.None: @@ -39,7 +39,7 @@ export class ScoreWhammyBarGlyph extends ScoreHelperNotesBaseGlyph { { let endGlyphs: BendNoteHeadGroupGlyph = new BendNoteHeadGroupGlyph(this._beat, false); endGlyphs.renderer = this.renderer; - let lastWhammyPoint: BendPoint = this._beat.whammyBarPoints[this._beat.whammyBarPoints.length - 1]; + let lastWhammyPoint: BendPoint = this._beat.whammyBarPoints![this._beat.whammyBarPoints!.length - 1]; for (let note of this._beat.notes) { if (!note.isTieOrigin) { endGlyphs.addGlyph( @@ -64,10 +64,10 @@ export class ScoreWhammyBarGlyph extends ScoreHelperNotesBaseGlyph { let middleGlyphs: BendNoteHeadGroupGlyph = new BendNoteHeadGroupGlyph(this._beat, false); middleGlyphs.renderer = this.renderer; if (this.renderer.settings.notation.notationMode === NotationMode.GuitarPro) { - let middleBendPoint: BendPoint = this._beat.whammyBarPoints[1]; + let middleBendPoint: BendPoint = this._beat.whammyBarPoints![1]; for (let note of this._beat.notes) { middleGlyphs.addGlyph( - this.getBendNoteValue(note, this._beat.whammyBarPoints[1]), + this.getBendNoteValue(note, this._beat.whammyBarPoints![1]), middleBendPoint.value % 2 !== 0 ); } @@ -77,8 +77,8 @@ export class ScoreWhammyBarGlyph extends ScoreHelperNotesBaseGlyph { let endGlyphs: BendNoteHeadGroupGlyph = new BendNoteHeadGroupGlyph(this._beat, false); endGlyphs.renderer = this.renderer; if (this.renderer.settings.notation.notationMode === NotationMode.GuitarPro) { - let lastBendPoint: BendPoint = this._beat.whammyBarPoints[ - this._beat.whammyBarPoints.length - 1 + let lastBendPoint: BendPoint = this._beat.whammyBarPoints![ + this._beat.whammyBarPoints!.length - 1 ]; for (let note of this._beat.notes) { endGlyphs.addGlyph( @@ -98,7 +98,7 @@ export class ScoreWhammyBarGlyph extends ScoreHelperNotesBaseGlyph { super.doLayout(); } - public paint(cx: number, cy: number, canvas: ICanvas): void { + public override paint(cx: number, cy: number, canvas: ICanvas): void { let beat: Beat = this._beat; switch (beat.whammyBarType) { case WhammyType.None: @@ -154,8 +154,8 @@ export class ScoreWhammyBarGlyph extends ScoreHelperNotesBaseGlyph { if (direction === BeamDirection.Up) { heightOffset = -heightOffset; } - let endValue: number = beat.whammyBarPoints.length > 0 - ? this.getBendNoteValue(note, beat.whammyBarPoints[beat.whammyBarPoints.length - 1]) + let endValue: number = beat.whammyBarPoints!.length > 0 + ? this.getBendNoteValue(note, beat.whammyBarPoints![beat.whammyBarPoints!.length - 1]) : 0; let endY: number = 0; let bendTie: boolean = false; @@ -248,14 +248,14 @@ export class ScoreWhammyBarGlyph extends ScoreHelperNotesBaseGlyph { 2 * this.scale; let middleX: number = (simpleStartX + simpleEndX) / 2; let text: string = ( - ((this._beat.whammyBarPoints[1].value - this._beat.whammyBarPoints[0].value) / 4) | + ((this._beat.whammyBarPoints![1].value - this._beat.whammyBarPoints![0].value) / 4) | 0 ).toString(); canvas.font = this.renderer.resources.tablatureFont; canvas.fillText(text, middleX, cy + this.y); let simpleStartY: number = cy + this.y + canvas.font.size + 2 * this.scale; let simpleEndY: number = simpleStartY + ScoreWhammyBarGlyph.SimpleDipHeight * this.scale; - if (this._beat.whammyBarPoints[1].value > this._beat.whammyBarPoints[0].value) { + if (this._beat.whammyBarPoints![1].value > this._beat.whammyBarPoints![0].value) { canvas.moveTo(simpleStartX, simpleEndY); canvas.lineTo(middleX, simpleStartY); canvas.lineTo(simpleEndX, simpleEndY); @@ -284,7 +284,7 @@ export class ScoreWhammyBarGlyph extends ScoreHelperNotesBaseGlyph { this.BendNoteHeads[0].x = middleX - this.BendNoteHeads[0].noteHeadOffset; this.BendNoteHeads[0].y = cy + startNoteRenderer.y; this.BendNoteHeads[0].paint(0, 0, canvas); - let middleValue: number = this.getBendNoteValue(note, beat.whammyBarPoints[1]); + let middleValue: number = this.getBendNoteValue(note, beat.whammyBarPoints![1]); let middleY: number = this.BendNoteHeads[0].getNoteValueY(middleValue) + heightOffset; this.drawBendSlur( canvas, @@ -323,7 +323,7 @@ export class ScoreWhammyBarGlyph extends ScoreHelperNotesBaseGlyph { startNoteRenderer.y + startNoteRenderer.getScoreY( startNoteRenderer.accidentalHelper.getNoteLineForValue( - note.displayValue - ((note.beat.whammyBarPoints[0].value / 2) | 0), + note.displayValue - ((note.beat.whammyBarPoints![0].value / 2) | 0), false ) ) + diff --git a/src/rendering/glyphs/TabBeatContainerGlyph.ts b/src/rendering/glyphs/TabBeatContainerGlyph.ts index aa741e324..55914dc21 100644 --- a/src/rendering/glyphs/TabBeatContainerGlyph.ts +++ b/src/rendering/glyphs/TabBeatContainerGlyph.ts @@ -18,7 +18,7 @@ export class TabBeatContainerGlyph extends BeatContainerGlyph { super(beat, voiceContainer); } - public doLayout(): void { + public override doLayout(): void { this._effectSlurs = []; super.doLayout(); if (this._bend) { @@ -28,7 +28,7 @@ export class TabBeatContainerGlyph extends BeatContainerGlyph { } } - protected createTies(n: Note): void { + protected override createTies(n: Note): void { if (!n.isVisible) { return; } diff --git a/src/rendering/glyphs/TabBeatGlyph.ts b/src/rendering/glyphs/TabBeatGlyph.ts index bdc1b534c..847baf082 100644 --- a/src/rendering/glyphs/TabBeatGlyph.ts +++ b/src/rendering/glyphs/TabBeatGlyph.ts @@ -19,21 +19,21 @@ export class TabBeatGlyph extends BeatOnNoteGlyphBase { public noteNumbers: TabNoteChordGlyph | null = null; public restGlyph: TabRestGlyph | null = null; - public getNoteX(note: Note, requestedPosition: NoteXPosition): number { + public override getNoteX(note: Note, requestedPosition: NoteXPosition): number { return this.noteNumbers ? this.noteNumbers.getNoteX(note, requestedPosition) : 0; } - public getNoteY(note: Note, requestedPosition: NoteYPosition): number { + public override getNoteY(note: Note, requestedPosition: NoteYPosition): number { return this.noteNumbers ? this.noteNumbers.getNoteY(note, requestedPosition) : 0; } - public buildBoundingsLookup(beatBounds:BeatBounds, cx:number, cy:number) { + public override buildBoundingsLookup(beatBounds:BeatBounds, cx:number, cy:number) { if(this.noteNumbers) { this.noteNumbers.buildBoundingsLookup(beatBounds, cx + this.x, cy + this.y); } } - public doLayout(): void { + public override doLayout(): void { let tabRenderer: TabBarRenderer = this.renderer as TabBarRenderer; if (!this.container.beat.isRest) { // @@ -136,7 +136,7 @@ export class TabBeatGlyph extends BeatOnNoteGlyphBase { } } - public updateBeamingHelper(): void { + public override updateBeamingHelper(): void { if (!this.container.beat.isRest) { this.noteNumbers!.updateBeamingHelper(this.container.x + this.x); } else { diff --git a/src/rendering/glyphs/TabBeatPreNotesGlyph.ts b/src/rendering/glyphs/TabBeatPreNotesGlyph.ts index c5bee21f0..7ed32ed93 100644 --- a/src/rendering/glyphs/TabBeatPreNotesGlyph.ts +++ b/src/rendering/glyphs/TabBeatPreNotesGlyph.ts @@ -4,7 +4,7 @@ import { SpacingGlyph } from '@src/rendering/glyphs/SpacingGlyph'; import { TabBrushGlyph } from '@src/rendering/glyphs/TabBrushGlyph'; export class TabBeatPreNotesGlyph extends BeatGlyphBase { - public doLayout(): void { + public override doLayout(): void { if (this.container.beat.brushType !== BrushType.None && !this.container.beat.isRest) { this.addGlyph(new TabBrushGlyph(this.container.beat)); this.addGlyph(new SpacingGlyph(0, 0, 4 * this.scale)); diff --git a/src/rendering/glyphs/TabBendGlyph.ts b/src/rendering/glyphs/TabBendGlyph.ts index be1839676..13f303869 100644 --- a/src/rendering/glyphs/TabBendGlyph.ts +++ b/src/rendering/glyphs/TabBendGlyph.ts @@ -122,7 +122,7 @@ export class TabBendGlyph extends Glyph { } } - public doLayout(): void { + public override doLayout(): void { super.doLayout(); let bendHeight: number = this._maxBendValue * TabBendGlyph.BendValueHeight * this.scale; this.renderer.registerOverflowTop(bendHeight); @@ -182,14 +182,14 @@ export class TabBendGlyph extends Glyph { // though it might not be 100% correct from timing perspective. switch (note.bendType) { case BendType.Custom: - for (let bendPoint of note.bendPoints) { + for (let bendPoint of note.bendPoints!) { renderingPoints.push(new TabBendRenderPoint(bendPoint.offset, bendPoint.value)); } break; case BendType.BendRelease: - renderingPoints.push(new TabBendRenderPoint(0, note.bendPoints[0].value)); - renderingPoints.push(new TabBendRenderPoint((BendPoint.MaxPosition / 2) | 0, note.bendPoints[1].value)); - renderingPoints.push(new TabBendRenderPoint(BendPoint.MaxPosition, note.bendPoints[3].value)); + renderingPoints.push(new TabBendRenderPoint(0, note.bendPoints![0].value)); + renderingPoints.push(new TabBendRenderPoint((BendPoint.MaxPosition / 2) | 0, note.bendPoints![1].value)); + renderingPoints.push(new TabBendRenderPoint(BendPoint.MaxPosition, note.bendPoints![3].value)); break; case BendType.Bend: case BendType.Hold: @@ -197,14 +197,14 @@ export class TabBendGlyph extends Glyph { case BendType.PrebendBend: case BendType.PrebendRelease: case BendType.Release: - renderingPoints.push(new TabBendRenderPoint(0, note.bendPoints[0].value)); - renderingPoints.push(new TabBendRenderPoint(BendPoint.MaxPosition, note.bendPoints[1].value)); + renderingPoints.push(new TabBendRenderPoint(0, note.bendPoints![0].value)); + renderingPoints.push(new TabBendRenderPoint(BendPoint.MaxPosition, note.bendPoints![1].value)); break; } return renderingPoints; } - public paint(cx: number, cy: number, canvas: ICanvas): void { + public override paint(cx: number, cy: number, canvas: ICanvas): void { let color: Color = canvas.color; if (this._notes.length > 1) { canvas.color = this.renderer.resources.secondaryGlyphColor; diff --git a/src/rendering/glyphs/TabBrushGlyph.ts b/src/rendering/glyphs/TabBrushGlyph.ts index d846bffb1..99a10e6fa 100644 --- a/src/rendering/glyphs/TabBrushGlyph.ts +++ b/src/rendering/glyphs/TabBrushGlyph.ts @@ -15,11 +15,11 @@ export class TabBrushGlyph extends Glyph { this._beat = beat; } - public doLayout(): void { + public override doLayout(): void { this.width = 10 * this.scale; } - public paint(cx: number, cy: number, canvas: ICanvas): void { + public override paint(cx: number, cy: number, canvas: ICanvas): void { let tabBarRenderer: TabBarRenderer = this.renderer as TabBarRenderer; let startY: number = cy + this.x + (tabBarRenderer.getNoteY(this._beat.maxNote!, NoteYPosition.Top)); diff --git a/src/rendering/glyphs/TabClefGlyph.ts b/src/rendering/glyphs/TabClefGlyph.ts index 096f9ce7a..d20f12934 100644 --- a/src/rendering/glyphs/TabClefGlyph.ts +++ b/src/rendering/glyphs/TabClefGlyph.ts @@ -7,11 +7,11 @@ export class TabClefGlyph extends Glyph { super(x, y); } - public doLayout(): void { + public override doLayout(): void { this.width = 28 * this.scale; } - public paint(cx: number, cy: number, canvas: ICanvas): void { + public override paint(cx: number, cy: number, canvas: ICanvas): void { let strings: number = this.renderer.bar.staff.tuning.length; let symbol: MusicFontSymbol = strings <= 4 ? MusicFontSymbol.FourStringTabClef : MusicFontSymbol.SixStringTabClef; let scale: number = strings <= 4 ? strings / 4.5 : strings / 6.5; diff --git a/src/rendering/glyphs/TabNoteChordGlyph.ts b/src/rendering/glyphs/TabNoteChordGlyph.ts index ef3d8f17e..2e2dc81bb 100644 --- a/src/rendering/glyphs/TabNoteChordGlyph.ts +++ b/src/rendering/glyphs/TabNoteChordGlyph.ts @@ -73,7 +73,7 @@ export class TabNoteChordGlyph extends Glyph { return 0; } - public doLayout(): void { + public override doLayout(): void { let w: number = 0; let noteStringWidth: number = 0; for (let i: number = 0, j: number = this._notes.length; i < j; i++) { @@ -110,7 +110,7 @@ export class TabNoteChordGlyph extends Glyph { } } - public paint(cx: number, cy: number, canvas: ICanvas): void { + public override paint(cx: number, cy: number, canvas: ICanvas): void { cx += this.x; cy += this.y; let res: RenderingResources = this.renderer.resources; diff --git a/src/rendering/glyphs/TabRestGlyph.ts b/src/rendering/glyphs/TabRestGlyph.ts index 1d01c9a08..856d01b2a 100644 --- a/src/rendering/glyphs/TabRestGlyph.ts +++ b/src/rendering/glyphs/TabRestGlyph.ts @@ -15,7 +15,7 @@ export class TabRestGlyph extends MusicFontGlyph { this._duration = duration; } - public doLayout(): void { + public override doLayout(): void { if (this._isVisibleRest) { this.width = ScoreRestGlyph.getSize(this._duration) * this.scale; } else { @@ -33,7 +33,7 @@ export class TabRestGlyph extends MusicFontGlyph { } } - public paint(cx: number, cy: number, canvas: ICanvas): void { + public override paint(cx: number, cy: number, canvas: ICanvas): void { if (this._isVisibleRest) { super.paint(cx, cy, canvas); } diff --git a/src/rendering/glyphs/TabSlideLineGlyph.ts b/src/rendering/glyphs/TabSlideLineGlyph.ts index 806b23fb2..44399ae98 100644 --- a/src/rendering/glyphs/TabSlideLineGlyph.ts +++ b/src/rendering/glyphs/TabSlideLineGlyph.ts @@ -24,11 +24,11 @@ export class TabSlideLineGlyph extends Glyph { this._parent = parent; } - public doLayout(): void { + public override doLayout(): void { this.width = 0; } - public paint(cx: number, cy: number, canvas: ICanvas): void { + public override paint(cx: number, cy: number, canvas: ICanvas): void { this.paintSlideIn(cx, cy, canvas); this.paintSlideOut(cx, cy, canvas); } diff --git a/src/rendering/glyphs/TabSlurGlyph.ts b/src/rendering/glyphs/TabSlurGlyph.ts index 5f66be074..7460f1642 100644 --- a/src/rendering/glyphs/TabSlurGlyph.ts +++ b/src/rendering/glyphs/TabSlurGlyph.ts @@ -15,7 +15,7 @@ export class TabSlurGlyph extends TabTieGlyph { this._forSlide = forSlide; } - protected getTieHeight(startX: number, startY: number, endX: number, endY: number): number { + protected override getTieHeight(startX: number, startY: number, endX: number, endY: number): number { return Math.log(endX - startX + 1) * this.renderer.settings.notation.slurHeight; } @@ -64,7 +64,7 @@ export class TabSlurGlyph extends TabTieGlyph { return true; } - public paint(cx: number, cy: number, canvas: ICanvas): void { + public override paint(cx: number, cy: number, canvas: ICanvas): void { let startNoteRenderer: BarRendererBase = this.renderer.scoreRenderer.layout!.getRendererForBar( this.renderer.staff.staveId, this.startBeat!.voice.bar diff --git a/src/rendering/glyphs/TabTieGlyph.ts b/src/rendering/glyphs/TabTieGlyph.ts index 77b79ad60..3a7e1270b 100644 --- a/src/rendering/glyphs/TabTieGlyph.ts +++ b/src/rendering/glyphs/TabTieGlyph.ts @@ -14,14 +14,14 @@ export class TabTieGlyph extends TieGlyph { this.endNote = endNote; } - protected getTieHeight(startX: number, startY: number, endX: number, endY: number): number { + protected override getTieHeight(startX: number, startY: number, endX: number, endY: number): number { if(this.startNote === this.endNote) { return 15; } return super.getTieHeight(startX, startY, endX, endY); } - protected getBeamDirection(beat: Beat, noteRenderer: BarRendererBase): BeamDirection { + protected override getBeamDirection(beat: Beat, noteRenderer: BarRendererBase): BeamDirection { if(this.startNote === this.endNote) { return BeamDirection.Up; } @@ -32,7 +32,7 @@ export class TabTieGlyph extends TieGlyph { return note.string > 3 ? BeamDirection.Up : BeamDirection.Down; } - protected getStartY(): number { + protected override getStartY(): number { if(this.startNote === this.endNote) { return this.startNoteRenderer!.getNoteY(this.startNote, NoteYPosition.Center); } @@ -43,18 +43,18 @@ export class TabTieGlyph extends TieGlyph { return this.startNoteRenderer!.getNoteY(this.startNote, NoteYPosition.Bottom); } - protected getEndY(): number { + protected override getEndY(): number { return this.getStartY(); } - protected getStartX(): number { + protected override getStartX(): number { if(this.startNote === this.endNote) { return this.getEndX() - 20 * this.scale; } return this.startNoteRenderer!.getNoteX(this.startNote, NoteXPosition.Center); } - protected getEndX(): number { + protected override getEndX(): number { if(this.startNote === this.endNote) { return this.endNoteRenderer!.getNoteX(this.endNote, NoteXPosition.Left); } diff --git a/src/rendering/glyphs/TabWhammyBarGlyph.ts b/src/rendering/glyphs/TabWhammyBarGlyph.ts index e883bceb1..e67087d50 100644 --- a/src/rendering/glyphs/TabWhammyBarGlyph.ts +++ b/src/rendering/glyphs/TabWhammyBarGlyph.ts @@ -28,7 +28,7 @@ export class TabWhammyBarGlyph extends Glyph { private createRenderingPoints(beat: Beat): BendPoint[] { // advanced rendering if (beat.whammyBarType === WhammyType.Custom) { - return beat.whammyBarPoints; + return beat.whammyBarPoints!; } let renderingPoints: BendPoint[] = []; // Guitar Pro Rendering Note: @@ -39,21 +39,21 @@ export class TabWhammyBarGlyph extends Glyph { case WhammyType.Hold: case WhammyType.PrediveDive: case WhammyType.Predive: - renderingPoints.push(new BendPoint(0, beat.whammyBarPoints[0].value)); - renderingPoints.push(new BendPoint(BendPoint.MaxPosition, beat.whammyBarPoints[1].value)); + renderingPoints.push(new BendPoint(0, beat.whammyBarPoints![0].value)); + renderingPoints.push(new BendPoint(BendPoint.MaxPosition, beat.whammyBarPoints![1].value)); break; case WhammyType.Dip: - renderingPoints.push(new BendPoint(0, beat.whammyBarPoints[0].value)); - renderingPoints.push(new BendPoint((BendPoint.MaxPosition / 2) | 0, beat.whammyBarPoints[1].value)); + renderingPoints.push(new BendPoint(0, beat.whammyBarPoints![0].value)); + renderingPoints.push(new BendPoint((BendPoint.MaxPosition / 2) | 0, beat.whammyBarPoints![1].value)); renderingPoints.push( - new BendPoint(BendPoint.MaxPosition, beat.whammyBarPoints[beat.whammyBarPoints.length - 1].value) + new BendPoint(BendPoint.MaxPosition, beat.whammyBarPoints![beat.whammyBarPoints!.length - 1].value) ); break; } return renderingPoints; } - public doLayout(): void { + public override doLayout(): void { super.doLayout(); this._isSimpleDip = this.renderer.settings.notation.notationMode === NotationMode.SongBook && @@ -75,7 +75,7 @@ export class TabWhammyBarGlyph extends Glyph { let topOffset: number = maxValue!.value > 0 ? Math.abs(this.getOffset(maxValue!.value)) : 0; if ( topOffset > 0 || - this._beat.whammyBarPoints[0].value !== 0 || + this._beat.whammyBarPoints![0].value !== 0 || this.renderer.settings.notation.isNotationElementVisible(NotationElement.ZerosOnDiveWhammys) ) { topOffset += this.renderer.resources.tablatureFont.size * 2; @@ -104,7 +104,7 @@ export class TabWhammyBarGlyph extends Glyph { return offset; } - public paint(cx: number, cy: number, canvas: ICanvas): void { + public override paint(cx: number, cy: number, canvas: ICanvas): void { let startNoteRenderer: BarRendererBase = this.renderer; let endBeat: Beat | null = this._beat.nextBeat; let endNoteRenderer: TabBarRenderer | null = null; diff --git a/src/rendering/glyphs/TempoGlyph.ts b/src/rendering/glyphs/TempoGlyph.ts index 00d7811fc..a7d7ca02f 100644 --- a/src/rendering/glyphs/TempoGlyph.ts +++ b/src/rendering/glyphs/TempoGlyph.ts @@ -12,12 +12,12 @@ export class TempoGlyph extends EffectGlyph { this._tempo = tempo; } - public doLayout(): void { + public override doLayout(): void { super.doLayout(); this.height = 25 * this.scale; } - public paint(cx: number, cy: number, canvas: ICanvas): void { + public override paint(cx: number, cy: number, canvas: ICanvas): void { let res: RenderingResources = this.renderer.resources; canvas.font = res.markerFont; canvas.fillMusicFontSymbol( diff --git a/src/rendering/glyphs/TextGlyph.ts b/src/rendering/glyphs/TextGlyph.ts index ce8eb972a..1776fc629 100644 --- a/src/rendering/glyphs/TextGlyph.ts +++ b/src/rendering/glyphs/TextGlyph.ts @@ -15,12 +15,12 @@ export class TextGlyph extends EffectGlyph { this.textAlign = textAlign; } - public doLayout(): void { + public override doLayout(): void { super.doLayout(); this.height = this.font.size * this._lines.length; } - public paint(cx: number, cy: number, canvas: ICanvas): void { + public override paint(cx: number, cy: number, canvas: ICanvas): void { let color = canvas.color ; canvas.color = color; canvas.font = this.font; diff --git a/src/rendering/glyphs/TieGlyph.ts b/src/rendering/glyphs/TieGlyph.ts index 8d88f6a66..ecd9a90fb 100644 --- a/src/rendering/glyphs/TieGlyph.ts +++ b/src/rendering/glyphs/TieGlyph.ts @@ -21,11 +21,11 @@ export class TieGlyph extends Glyph { this.forEnd = forEnd; } - public doLayout(): void { + public override doLayout(): void { this.width = 0; } - public paint(cx: number, cy: number, canvas: ICanvas): void { + public override paint(cx: number, cy: number, canvas: ICanvas): void { if (!this.endBeat) { return; } diff --git a/src/rendering/glyphs/TimeSignatureGlyph.ts b/src/rendering/glyphs/TimeSignatureGlyph.ts index 6d3260b5f..d59385b56 100644 --- a/src/rendering/glyphs/TimeSignatureGlyph.ts +++ b/src/rendering/glyphs/TimeSignatureGlyph.ts @@ -19,7 +19,7 @@ export abstract class TimeSignatureGlyph extends GlyphGroup { protected abstract get commonScale(): number; protected abstract get numberScale(): number; - public doLayout(): void { + public override doLayout(): void { if (this._isCommon && this._numerator === 2 && this._denominator === 2) { let common: MusicFontGlyph = new MusicFontGlyph( 0, diff --git a/src/rendering/glyphs/TremoloPickingGlyph.ts b/src/rendering/glyphs/TremoloPickingGlyph.ts index f295cc39a..0b6c86e22 100644 --- a/src/rendering/glyphs/TremoloPickingGlyph.ts +++ b/src/rendering/glyphs/TremoloPickingGlyph.ts @@ -7,7 +7,7 @@ export class TremoloPickingGlyph extends MusicFontGlyph { super(x, y, 1, TremoloPickingGlyph.getSymbol(duration)); } - public doLayout(): void { + public override doLayout(): void { this.width = 12 * this.scale; } diff --git a/src/rendering/glyphs/TrillGlyph.ts b/src/rendering/glyphs/TrillGlyph.ts index cf92c7f31..20957716f 100644 --- a/src/rendering/glyphs/TrillGlyph.ts +++ b/src/rendering/glyphs/TrillGlyph.ts @@ -8,12 +8,12 @@ export class TrillGlyph extends EffectGlyph { super(x, y); } - public doLayout(): void { + public override doLayout(): void { super.doLayout(); this.height = 20 * this.scale; } - public paint(cx: number, cy: number, canvas: ICanvas): void { + public override paint(cx: number, cy: number, canvas: ICanvas): void { let res: RenderingResources = this.renderer.resources; canvas.font = res.markerFont; let textw: number = canvas.measureText('tr'); diff --git a/src/rendering/glyphs/TripletFeelGlyph.ts b/src/rendering/glyphs/TripletFeelGlyph.ts index 8b6651154..be6b7839e 100644 --- a/src/rendering/glyphs/TripletFeelGlyph.ts +++ b/src/rendering/glyphs/TripletFeelGlyph.ts @@ -25,12 +25,12 @@ export class TripletFeelGlyph extends EffectGlyph { this._tripletFeel = tripletFeel; } - public doLayout(): void { + public override doLayout(): void { super.doLayout(); this.height = 25 * this.scale; } - public paint(cx: number, cy: number, canvas: ICanvas): void { + public override paint(cx: number, cy: number, canvas: ICanvas): void { cx += this.x; cy += this.y; let noteY: number = cy + this.height * NoteHeadGlyph.GraceScale; diff --git a/src/rendering/glyphs/TuningGlyph.ts b/src/rendering/glyphs/TuningGlyph.ts index 9c8a43199..13424e682 100644 --- a/src/rendering/glyphs/TuningGlyph.ts +++ b/src/rendering/glyphs/TuningGlyph.ts @@ -16,7 +16,7 @@ export class TuningGlyph extends GlyphGroup { this.glyphs = []; } - public doLayout() { + public override doLayout() { if (this.glyphs!.length > 0) { return; } diff --git a/src/rendering/glyphs/VoiceContainerGlyph.ts b/src/rendering/glyphs/VoiceContainerGlyph.ts index a135e3d0d..6eabc93ec 100644 --- a/src/rendering/glyphs/VoiceContainerGlyph.ts +++ b/src/rendering/glyphs/VoiceContainerGlyph.ts @@ -119,7 +119,7 @@ export class VoiceContainerGlyph extends GlyphGroup { this.scaleToForce(Math.max(this.renderer.settings.display.stretchForce, info.minStretchForce)); } - public addGlyph(g: Glyph): void { + public override addGlyph(g: Glyph): void { let bg: BeatContainerGlyph = g as BeatContainerGlyph; g.x = this.beatGlyphs.length === 0 @@ -134,10 +134,10 @@ export class VoiceContainerGlyph extends GlyphGroup { } } - public doLayout(): void { + public override doLayout(): void { } - public paint(cx: number, cy: number, canvas: ICanvas): void { + public override paint(cx: number, cy: number, canvas: ICanvas): void { // canvas.color = Color.random(); // canvas.strokeRect(cx + this.x, cy + this.y, this.width, this.renderer.height); canvas.color = diff --git a/src/rendering/layout/HorizontalScreenLayout.ts b/src/rendering/layout/HorizontalScreenLayout.ts index d6bdcd6f4..924d5f3d5 100644 --- a/src/rendering/layout/HorizontalScreenLayout.ts +++ b/src/rendering/layout/HorizontalScreenLayout.ts @@ -33,10 +33,6 @@ export class HorizontalScreenLayout extends ScoreLayout { return false; } - public get padding(): number[] { - return this._pagePadding!; - } - public get firstBarX(): number{ let x= this._pagePadding![0]; if(this._group) { diff --git a/src/rendering/layout/PageViewLayout.ts b/src/rendering/layout/PageViewLayout.ts index 7a09eef78..1acdf322c 100644 --- a/src/rendering/layout/PageViewLayout.ts +++ b/src/rendering/layout/PageViewLayout.ts @@ -73,10 +73,6 @@ export class PageViewLayout extends ScoreLayout { return true; } - public get padding(): number[] { - return this._pagePadding!; - } - public get firstBarX(): number { let x = this._pagePadding![0]; if (this._groups.length > 0) { diff --git a/src/rendering/layout/ScoreLayout.ts b/src/rendering/layout/ScoreLayout.ts index 7d1e2a178..982b84dcb 100644 --- a/src/rendering/layout/ScoreLayout.ts +++ b/src/rendering/layout/ScoreLayout.ts @@ -50,7 +50,6 @@ export abstract class ScoreLayout { this.renderer = renderer; } - public abstract get padding(): number[]; public abstract get firstBarX(): number; public abstract get supportsResize(): boolean; @@ -191,11 +190,14 @@ export abstract class ScoreLayout { let chords: Map<string, Chord> = new Map<string, Chord>(); for (let track of this.renderer.tracks!) { for (let staff of track.staves) { - for (const [chordId, chord] of staff.chords) { - if (!chords.has(chordId)) { - if (chord.showDiagram) { - chords.set(chordId, chord); - this.chordDiagrams!.addChord(chord); + const sc = staff.chords; + if (sc) { + for (const [chordId, chord] of sc) { + if (!chords.has(chordId)) { + if (chord.showDiagram) { + chords.set(chordId, chord); + this.chordDiagrams!.addChord(chord); + } } } } diff --git a/src/xml/XmlDocument.ts b/src/xml/XmlDocument.ts index 769b95986..e4d3d1667 100644 --- a/src/xml/XmlDocument.ts +++ b/src/xml/XmlDocument.ts @@ -35,7 +35,7 @@ export class XmlDocument extends XmlNode { XmlParser.parse(xml, 0, this); } - public toString() { + public override toString() { return this.toFormattedString(); } diff --git a/test/audio/FlatMidiEventGenerator.ts b/test/audio/FlatMidiEventGenerator.ts index 9c1a4eefd..f2c90ee5b 100644 --- a/test/audio/FlatMidiEventGenerator.ts +++ b/test/audio/FlatMidiEventGenerator.ts @@ -101,11 +101,11 @@ export class TempoEvent extends FlatMidiEvent { this.tempo = tempo; } - public toString(): string { + public override toString(): string { return `Tempo: ${super.toString()} Tempo[${this.tempo}]`; } - public equals(obj: unknown): boolean { + public override equals(obj: unknown): boolean { if (!super.equals(obj)) { return false; } @@ -128,11 +128,11 @@ export class TimeSignatureEvent extends FlatMidiEvent { this.denominator = denominator; } - public toString(): string { + public override toString(): string { return `TimeSignature: ${super.toString()} Numerator[${this.numerator}] Denominator[${this.denominator}]`; } - public equals(obj: unknown): boolean { + public override equals(obj: unknown): boolean { if (!super.equals(obj)) { return false; } @@ -153,11 +153,11 @@ export class TrackMidiEvent extends FlatMidiEvent { this.track = track; } - public toString(): string { + public override toString(): string { return `${super.toString()} Track[${this.track}]`; } - public equals(obj: unknown): boolean { + public override equals(obj: unknown): boolean { if (!super.equals(obj)) { return false; } @@ -175,7 +175,7 @@ export class TrackEndEvent extends TrackMidiEvent { super(tick, track); } - public toString(): string { + public override toString(): string { return 'End of Track ' + super.toString(); } } @@ -188,11 +188,11 @@ export class ChannelMidiEvent extends TrackMidiEvent { this.channel = channel; } - public toString(): string { + public override toString(): string { return `${super.toString()} Channel[${this.channel}]`; } - public equals(obj: unknown): boolean { + public override equals(obj: unknown): boolean { if (!super.equals(obj)) { return false; } @@ -215,11 +215,11 @@ export class ControlChangeEvent extends ChannelMidiEvent { this.value = value; } - public toString(): string { + public override toString(): string { return `ControlChange: ${super.toString()} Controller[${this.controller}] Value[${this.value}]`; } - public equals(obj: unknown): boolean { + public override equals(obj: unknown): boolean { if (!super.equals(obj)) { return false; } @@ -237,11 +237,11 @@ export class RestEvent extends ChannelMidiEvent { super(tick, track, channel); } - public toString(): string { + public override toString(): string { return `Rest: ${super.toString()}`; } - public equals(obj: unknown): boolean { + public override equals(obj: unknown): boolean { if (!super.equals(obj)) { return false; } @@ -261,11 +261,11 @@ export class ProgramChangeEvent extends ChannelMidiEvent { this.program = program; } - public toString(): string { + public override toString(): string { return `ProgramChange: ${super.toString()} Program[${this.program}]`; } - public equals(obj: unknown): boolean { + public override equals(obj: unknown): boolean { if (!super.equals(obj)) { return false; } @@ -297,11 +297,11 @@ export class NoteEvent extends ChannelMidiEvent { this.dynamicValue = dynamicValue; } - public toString(): string { + public override toString(): string { return `Note: ${super.toString()} Length[${this.length}] Key[${this.key}] Dynamic[${this.dynamicValue}]`; } - public equals(obj: unknown): boolean { + public override equals(obj: unknown): boolean { if (!super.equals(obj)) { return false; } @@ -322,11 +322,11 @@ export class BendEvent extends ChannelMidiEvent { this.value = value; } - public toString(): string { + public override toString(): string { return `Bend: ${super.toString()} Value[${this.value}]`; } - public equals(obj: unknown): boolean { + public override equals(obj: unknown): boolean { if (!super.equals(obj)) { return false; } @@ -348,11 +348,11 @@ export class NoteBendEvent extends ChannelMidiEvent { this.value = value; } - public toString(): string { + public override toString(): string { return `NoteBend: ${super.toString()} Key[${this.key}] Value[${this.value}]`; } - public equals(obj: unknown): boolean { + public override equals(obj: unknown): boolean { if (!super.equals(obj)) { return false; } diff --git a/test/exporter/Gp7Exporter.test.ts b/test/exporter/Gp7Exporter.test.ts index 5b0f551ea..eb1245645 100644 --- a/test/exporter/Gp7Exporter.test.ts +++ b/test/exporter/Gp7Exporter.test.ts @@ -99,17 +99,17 @@ describe('Gp7ExporterTest', () => { it('gp5-to-gp7', async () => { await testRoundTripEqual(`conversion/full-song.gp5`, [ - 'accidentalMode', // gets upgraded from default - 'percussionArticulations', // gets added + 'accidentalmode', // gets upgraded from default + 'percussionarticulations', // gets added 'automations' // volume automations are not yet supported in gpif ]); }); it('gp6-to-gp7', async () => { await testRoundTripEqual(`conversion/full-song.gpx`, [ - 'accidentalMode', // gets upgraded from default - 'percussionArticulations', // gets added - 'percussionArticulation', // gets added + 'accidentalmode', // gets upgraded from default + 'percussionarticulations', // gets added + 'percussionarticulation', // gets added ]); }); @@ -138,6 +138,6 @@ describe('Gp7ExporterTest', () => { const expectedJson = JsonConverter.scoreToJsObject(expected); const actualJson = JsonConverter.scoreToJsObject(actual) - ComparisonHelpers.expectJsonEqual(expectedJson, actualJson, '<alphatex>', ['accidentalMode']); + ComparisonHelpers.expectJsonEqual(expectedJson, actualJson, '<alphatex>', ['accidentalmode']); }); }); diff --git a/test/importer/Gp7Importer.test.ts b/test/importer/Gp7Importer.test.ts index 5183c781f..5cd2375d5 100644 --- a/test/importer/Gp7Importer.test.ts +++ b/test/importer/Gp7Importer.test.ts @@ -95,27 +95,27 @@ describe('Gp7ImporterTest', () => { const reader = await prepareGp7ImporterWithFile('guitarpro7/bends.gp'); let score: Score = reader.readScore(); expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendType).toEqual(BendType.Bend); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints.length).toEqual(2); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints[0].offset).toEqual(0); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints[0].value).toEqual(0); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints[1].offset).toEqual(60); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints[1].value).toEqual(4); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints!.length).toEqual(2); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints![0].offset).toEqual(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints![0].value).toEqual(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints![1].offset).toEqual(60); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints![1].value).toEqual(4); expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendType).toEqual(BendType.Bend); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints.length).toEqual(2); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints[0].offset).toEqual(0); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints[0].value).toEqual(0); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints[1].offset).toEqual(60); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints[1].value).toEqual(4); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints!.length).toEqual(2); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![0].offset).toEqual(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![0].value).toEqual(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![1].offset).toEqual(60); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![1].value).toEqual(4); expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendType).toEqual(BendType.BendRelease); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints.length).toEqual(4); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints[0].offset).toEqual(0); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints[0].value).toEqual(0); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints[1].offset).toEqual(30); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints[1].value).toEqual(12); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints[2].offset).toEqual(30); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints[2].value).toEqual(12); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints[3].offset).toEqual(60); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints[3].value).toEqual(6); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints!.length).toEqual(4); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints![0].offset).toEqual(0); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints![0].value).toEqual(0); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints![1].offset).toEqual(30); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints![1].value).toEqual(12); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints![2].offset).toEqual(30); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints![2].value).toEqual(12); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints![3].offset).toEqual(60); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints![3].value).toEqual(6); }); it('bends-advanced', async () => { @@ -127,203 +127,203 @@ describe('Gp7ImporterTest', () => { // // Bar 1 let note: Note = score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0]; expect(note.bendType).toEqual(BendType.Bend); - expect(note.bendPoints.length).toEqual(2); - expect(note.bendPoints[0].offset).toBeCloseTo(0); - expect(note.bendPoints[0].value).toEqual(0); - expect(note.bendPoints[1].offset).toBeCloseTo(15); - expect(note.bendPoints[1].value).toEqual(4); + expect(note.bendPoints!.length).toEqual(2); + expect(note.bendPoints![0].offset).toBeCloseTo(0); + expect(note.bendPoints![0].value).toEqual(0); + expect(note.bendPoints![1].offset).toBeCloseTo(15); + expect(note.bendPoints![1].value).toEqual(4); note = score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0]; expect(note.bendType).toEqual(BendType.BendRelease); - expect(note.bendPoints.length).toEqual(4); - expect(note.bendPoints[0].offset).toBeCloseTo(0); - expect(note.bendPoints[0].value).toEqual(0); - expect(note.bendPoints[1].offset).toBeCloseTo(10.2); - expect(note.bendPoints[1].value).toEqual(4); - expect(note.bendPoints[2].offset).toBeCloseTo(20.4); - expect(note.bendPoints[2].value).toEqual(4); - expect(note.bendPoints[3].offset).toBeCloseTo(30); - expect(note.bendPoints[3].value).toEqual(0); + expect(note.bendPoints!.length).toEqual(4); + expect(note.bendPoints![0].offset).toBeCloseTo(0); + expect(note.bendPoints![0].value).toEqual(0); + expect(note.bendPoints![1].offset).toBeCloseTo(10.2); + expect(note.bendPoints![1].value).toEqual(4); + expect(note.bendPoints![2].offset).toBeCloseTo(20.4); + expect(note.bendPoints![2].value).toEqual(4); + expect(note.bendPoints![3].offset).toBeCloseTo(30); + expect(note.bendPoints![3].value).toEqual(0); // // Bar 2 note = score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0]; expect(note.bendType).toEqual(BendType.Bend); - expect(note.bendPoints.length).toEqual(2); - expect(note.bendPoints[0].offset).toBeCloseTo(0); - expect(note.bendPoints[0].value).toEqual(0); - expect(note.bendPoints[1].offset).toBeCloseTo(59.4); - expect(note.bendPoints[1].value).toEqual(4); + expect(note.bendPoints!.length).toEqual(2); + expect(note.bendPoints![0].offset).toBeCloseTo(0); + expect(note.bendPoints![0].value).toEqual(0); + expect(note.bendPoints![1].offset).toBeCloseTo(59.4); + expect(note.bendPoints![1].value).toEqual(4); note = score.tracks[0].staves[0].bars[1].voices[0].beats[1].notes[0]; expect(note.bendType).toEqual(BendType.BendRelease); - expect(note.bendPoints.length).toEqual(4); - expect(note.bendPoints[0].offset).toBeCloseTo(0); - expect(note.bendPoints[0].value).toEqual(0); - expect(note.bendPoints[1].offset).toBeCloseTo(10.2); - expect(note.bendPoints[1].value).toEqual(4); - expect(note.bendPoints[2].offset).toBeCloseTo(45.6); - expect(note.bendPoints[2].value).toEqual(4); - expect(note.bendPoints[3].offset).toBeCloseTo(59.4); - expect(note.bendPoints[3].value).toEqual(0); + expect(note.bendPoints!.length).toEqual(4); + expect(note.bendPoints![0].offset).toBeCloseTo(0); + expect(note.bendPoints![0].value).toEqual(0); + expect(note.bendPoints![1].offset).toBeCloseTo(10.2); + expect(note.bendPoints![1].value).toEqual(4); + expect(note.bendPoints![2].offset).toBeCloseTo(45.6); + expect(note.bendPoints![2].value).toEqual(4); + expect(note.bendPoints![3].offset).toBeCloseTo(59.4); + expect(note.bendPoints![3].value).toEqual(0); // // Bar 3 note = score.tracks[0].staves[0].bars[2].voices[0].beats[0].notes[0]; expect(note.bendType).toEqual(BendType.Prebend); - expect(note.bendPoints.length).toEqual(2); - expect(note.bendPoints[0].offset).toBeCloseTo(0); - expect(note.bendPoints[0].value).toEqual(4); - expect(note.bendPoints[1].offset).toBeCloseTo(60); - expect(note.bendPoints[1].value).toEqual(4); + expect(note.bendPoints!.length).toEqual(2); + expect(note.bendPoints![0].offset).toBeCloseTo(0); + expect(note.bendPoints![0].value).toEqual(4); + expect(note.bendPoints![1].offset).toBeCloseTo(60); + expect(note.bendPoints![1].value).toEqual(4); note = score.tracks[0].staves[0].bars[2].voices[0].beats[1].notes[0]; expect(note.bendType).toEqual(BendType.PrebendBend); - expect(note.bendPoints.length).toEqual(2); - expect(note.bendPoints[0].offset).toBeCloseTo(0); - expect(note.bendPoints[0].value).toEqual(4); - expect(note.bendPoints[1].offset).toBeCloseTo(15); - expect(note.bendPoints[1].value).toEqual(6); + expect(note.bendPoints!.length).toEqual(2); + expect(note.bendPoints![0].offset).toBeCloseTo(0); + expect(note.bendPoints![0].value).toEqual(4); + expect(note.bendPoints![1].offset).toBeCloseTo(15); + expect(note.bendPoints![1].value).toEqual(6); // // Bar 4 note = score.tracks[0].staves[0].bars[3].voices[0].beats[0].notes[0]; expect(note.bendType).toEqual(BendType.PrebendRelease); - expect(note.bendPoints.length).toEqual(2); - expect(note.bendPoints[0].offset).toBeCloseTo(0); - expect(note.bendPoints[0].value).toEqual(4); - expect(note.bendPoints[1].offset).toBeCloseTo(15); - expect(note.bendPoints[1].value).toEqual(0); + expect(note.bendPoints!.length).toEqual(2); + expect(note.bendPoints![0].offset).toBeCloseTo(0); + expect(note.bendPoints![0].value).toEqual(4); + expect(note.bendPoints![1].offset).toBeCloseTo(15); + expect(note.bendPoints![1].value).toEqual(0); // // Bar 5 note = score.tracks[0].staves[0].bars[4].voices[0].beats[0].notes[0]; expect(note.bendType).toEqual(BendType.Bend); - expect(note.bendPoints.length).toEqual(2); - expect(note.bendPoints[0].offset).toBeCloseTo(0); - expect(note.bendPoints[0].value).toEqual(0); - expect(note.bendPoints[1].offset).toBeCloseTo(14.4); - expect(note.bendPoints[1].value).toEqual(8); + expect(note.bendPoints!.length).toEqual(2); + expect(note.bendPoints![0].offset).toBeCloseTo(0); + expect(note.bendPoints![0].value).toEqual(0); + expect(note.bendPoints![1].offset).toBeCloseTo(14.4); + expect(note.bendPoints![1].value).toEqual(8); note = score.tracks[0].staves[0].bars[4].voices[0].beats[1].notes[0]; expect(note.bendType).toEqual(BendType.BendRelease); - expect(note.bendPoints.length).toEqual(4); - expect(note.bendPoints[0].offset).toBeCloseTo(0); - expect(note.bendPoints[0].value).toEqual(0); - expect(note.bendPoints[1].offset).toBeCloseTo(9); - expect(note.bendPoints[1].value).toEqual(8); - expect(note.bendPoints[2].offset).toBeCloseTo(20.4); - expect(note.bendPoints[2].value).toEqual(8); - expect(note.bendPoints[3].offset).toBeCloseTo(31.2); - expect(note.bendPoints[3].value).toEqual(4); + expect(note.bendPoints!.length).toEqual(4); + expect(note.bendPoints![0].offset).toBeCloseTo(0); + expect(note.bendPoints![0].value).toEqual(0); + expect(note.bendPoints![1].offset).toBeCloseTo(9); + expect(note.bendPoints![1].value).toEqual(8); + expect(note.bendPoints![2].offset).toBeCloseTo(20.4); + expect(note.bendPoints![2].value).toEqual(8); + expect(note.bendPoints![3].offset).toBeCloseTo(31.2); + expect(note.bendPoints![3].value).toEqual(4); // // Bar 6 note = score.tracks[0].staves[0].bars[5].voices[0].beats[0].notes[0]; expect(note.bendType).toEqual(BendType.Prebend); - expect(note.bendPoints.length).toEqual(2); - expect(note.bendPoints[0].offset).toBeCloseTo(0); - expect(note.bendPoints[0].value).toEqual(8); - expect(note.bendPoints[1].offset).toBeCloseTo(60); - expect(note.bendPoints[1].value).toEqual(8); + expect(note.bendPoints!.length).toEqual(2); + expect(note.bendPoints![0].offset).toBeCloseTo(0); + expect(note.bendPoints![0].value).toEqual(8); + expect(note.bendPoints![1].offset).toBeCloseTo(60); + expect(note.bendPoints![1].value).toEqual(8); note = score.tracks[0].staves[0].bars[5].voices[0].beats[1].notes[0]; expect(note.bendType).toEqual(BendType.PrebendBend); - expect(note.bendPoints.length).toEqual(2); - expect(note.bendPoints[0].offset).toBeCloseTo(0); - expect(note.bendPoints[0].value).toEqual(8); - expect(note.bendPoints[1].offset).toBeCloseTo(16.2); - expect(note.bendPoints[1].value).toEqual(12); + expect(note.bendPoints!.length).toEqual(2); + expect(note.bendPoints![0].offset).toBeCloseTo(0); + expect(note.bendPoints![0].value).toEqual(8); + expect(note.bendPoints![1].offset).toBeCloseTo(16.2); + expect(note.bendPoints![1].value).toEqual(12); // // Bar 7 note = score.tracks[0].staves[0].bars[6].voices[0].beats[0].notes[0]; expect(note.bendType).toEqual(BendType.PrebendRelease); - expect(note.bendPoints.length).toEqual(2); - expect(note.bendPoints[0].offset).toBeCloseTo(0); - expect(note.bendPoints[0].value).toEqual(8); - expect(note.bendPoints[1].offset).toBeCloseTo(14.4); - expect(note.bendPoints[1].value).toEqual(4); + expect(note.bendPoints!.length).toEqual(2); + expect(note.bendPoints![0].offset).toBeCloseTo(0); + expect(note.bendPoints![0].value).toEqual(8); + expect(note.bendPoints![1].offset).toBeCloseTo(14.4); + expect(note.bendPoints![1].value).toEqual(4); // // Bar 8 note = score.tracks[0].staves[0].bars[7].voices[0].beats[0].notes[0]; expect(note.bendType).toEqual(BendType.Bend); - expect(note.bendPoints.length).toEqual(2); - expect(note.bendPoints[0].offset).toBeCloseTo(0); - expect(note.bendPoints[0].value).toEqual(0); - expect(note.bendPoints[1].offset).toBeCloseTo(15); - expect(note.bendPoints[1].value).toEqual(4); + expect(note.bendPoints!.length).toEqual(2); + expect(note.bendPoints![0].offset).toBeCloseTo(0); + expect(note.bendPoints![0].value).toEqual(0); + expect(note.bendPoints![1].offset).toBeCloseTo(15); + expect(note.bendPoints![1].value).toEqual(4); // // Bar 9 note = score.tracks[0].staves[0].bars[8].voices[0].beats[0].notes[0]; expect(note.bendType).toEqual(BendType.BendRelease); - expect(note.bendPoints.length).toEqual(4); - expect(note.bendPoints[0].offset).toBeCloseTo(0); - expect(note.bendPoints[0].value).toEqual(0); - expect(note.bendPoints[1].offset).toBeCloseTo(10.2); - expect(note.bendPoints[1].value).toEqual(4); - expect(note.bendPoints[2].offset).toBeCloseTo(20.4); - expect(note.bendPoints[2].value).toEqual(4); - expect(note.bendPoints[3].offset).toBeCloseTo(30); - expect(note.bendPoints[3].value).toEqual(0); + expect(note.bendPoints!.length).toEqual(4); + expect(note.bendPoints![0].offset).toBeCloseTo(0); + expect(note.bendPoints![0].value).toEqual(0); + expect(note.bendPoints![1].offset).toBeCloseTo(10.2); + expect(note.bendPoints![1].value).toEqual(4); + expect(note.bendPoints![2].offset).toBeCloseTo(20.4); + expect(note.bendPoints![2].value).toEqual(4); + expect(note.bendPoints![3].offset).toBeCloseTo(30); + expect(note.bendPoints![3].value).toEqual(0); // Combined Bends // // Bar 10 note = score.tracks[0].staves[0].bars[9].voices[0].beats[0].notes[0]; expect(note.bendType).toEqual(BendType.Bend); - expect(note.bendPoints.length).toEqual(2); - expect(note.bendPoints[0].offset).toBeCloseTo(0); - expect(note.bendPoints[0].value).toEqual(0); - expect(note.bendPoints[1].offset).toBeCloseTo(15); - expect(note.bendPoints[1].value).toEqual(4); + expect(note.bendPoints!.length).toEqual(2); + expect(note.bendPoints![0].offset).toBeCloseTo(0); + expect(note.bendPoints![0].value).toEqual(0); + expect(note.bendPoints![1].offset).toBeCloseTo(15); + expect(note.bendPoints![1].value).toEqual(4); note = score.tracks[0].staves[0].bars[9].voices[0].beats[1].notes[0]; expect(note.bendType).toEqual(BendType.Release); expect(note.isContinuedBend).toBe(true); - expect(note.bendPoints.length).toEqual(2); - expect(note.bendPoints[0].offset).toBeCloseTo(0); - expect(note.bendPoints[0].value).toEqual(4); - expect(note.bendPoints[1].offset).toBeCloseTo(15); - expect(note.bendPoints[1].value).toEqual(0); + expect(note.bendPoints!.length).toEqual(2); + expect(note.bendPoints![0].offset).toBeCloseTo(0); + expect(note.bendPoints![0].value).toEqual(4); + expect(note.bendPoints![1].offset).toBeCloseTo(15); + expect(note.bendPoints![1].value).toEqual(0); note = score.tracks[0].staves[0].bars[9].voices[0].beats[2].notes[0]; expect(note.bendType).toEqual(BendType.Bend); expect(note.isContinuedBend).toBe(false); - expect(note.bendPoints.length).toEqual(2); - expect(note.bendPoints[0].offset).toBeCloseTo(0); - expect(note.bendPoints[0].value).toEqual(0); - expect(note.bendPoints[1].offset).toBeCloseTo(15); - expect(note.bendPoints[1].value).toEqual(4); + expect(note.bendPoints!.length).toEqual(2); + expect(note.bendPoints![0].offset).toBeCloseTo(0); + expect(note.bendPoints![0].value).toEqual(0); + expect(note.bendPoints![1].offset).toBeCloseTo(15); + expect(note.bendPoints![1].value).toEqual(4); // // Bar 11 note = score.tracks[0].staves[0].bars[10].voices[0].beats[0].notes[0]; expect(note.bendType).toEqual(BendType.Bend); - expect(note.bendPoints.length).toEqual(2); - expect(note.bendPoints[0].offset).toBeCloseTo(0); - expect(note.bendPoints[0].value).toEqual(0); - expect(note.bendPoints[1].offset).toBeCloseTo(15); - expect(note.bendPoints[1].value).toEqual(4); + expect(note.bendPoints!.length).toEqual(2); + expect(note.bendPoints![0].offset).toBeCloseTo(0); + expect(note.bendPoints![0].value).toEqual(0); + expect(note.bendPoints![1].offset).toBeCloseTo(15); + expect(note.bendPoints![1].value).toEqual(4); note = score.tracks[0].staves[0].bars[10].voices[0].beats[1].notes[0]; expect(note.bendType).toEqual(BendType.Bend); expect(note.isContinuedBend).toBe(true); - expect(note.bendPoints.length).toEqual(2); - expect(note.bendPoints[0].offset).toBeCloseTo(0); - expect(note.bendPoints[0].value).toEqual(4); - expect(note.bendPoints[1].offset).toBeCloseTo(15); - expect(note.bendPoints[1].value).toEqual(8); + expect(note.bendPoints!.length).toEqual(2); + expect(note.bendPoints![0].offset).toBeCloseTo(0); + expect(note.bendPoints![0].value).toEqual(4); + expect(note.bendPoints![1].offset).toBeCloseTo(15); + expect(note.bendPoints![1].value).toEqual(8); note = score.tracks[0].staves[0].bars[10].voices[0].beats[2].notes[0]; expect(note.bendType).toEqual(BendType.Release); expect(note.isContinuedBend).toBe(true); - expect(note.bendPoints.length).toEqual(2); - expect(note.bendPoints[0].offset).toBeCloseTo(0); - expect(note.bendPoints[0].value).toEqual(8); - expect(note.bendPoints[1].offset).toBeCloseTo(15); - expect(note.bendPoints[1].value).toEqual(4); + expect(note.bendPoints!.length).toEqual(2); + expect(note.bendPoints![0].offset).toBeCloseTo(0); + expect(note.bendPoints![0].value).toEqual(8); + expect(note.bendPoints![1].offset).toBeCloseTo(15); + expect(note.bendPoints![1].value).toEqual(4); note = score.tracks[0].staves[0].bars[10].voices[0].beats[3].notes[0]; expect(note.bendType).toEqual(BendType.Release); expect(note.isContinuedBend).toBe(true); - expect(note.bendPoints.length).toEqual(2); - expect(note.bendPoints[0].offset).toBeCloseTo(0); - expect(note.bendPoints[0].value).toEqual(4); - expect(note.bendPoints[1].offset).toBeCloseTo(15); - expect(note.bendPoints[1].value).toEqual(0); + expect(note.bendPoints!.length).toEqual(2); + expect(note.bendPoints![0].offset).toBeCloseTo(0); + expect(note.bendPoints![0].value).toEqual(4); + expect(note.bendPoints![1].offset).toBeCloseTo(15); + expect(note.bendPoints![1].value).toEqual(0); // Grace Bends @@ -331,70 +331,70 @@ describe('Gp7ImporterTest', () => { note = score.tracks[0].staves[0].bars[11].voices[0].beats[0].notes[0]; expect(note.beat.graceType).toEqual(GraceType.BeforeBeat); expect(note.bendType).toEqual(BendType.Bend); - expect(note.bendPoints.length).toEqual(2); - expect(note.bendPoints[0].offset).toBeCloseTo(0); - expect(note.bendPoints[0].value).toEqual(0); - expect(note.bendPoints[1].offset).toBeCloseTo(15); - expect(note.bendPoints[1].value).toEqual(4); + expect(note.bendPoints!.length).toEqual(2); + expect(note.bendPoints![0].offset).toBeCloseTo(0); + expect(note.bendPoints![0].value).toEqual(0); + expect(note.bendPoints![1].offset).toBeCloseTo(15); + expect(note.bendPoints![1].value).toEqual(4); // // Bar 13 note = score.tracks[0].staves[0].bars[12].voices[0].beats[0].notes[0]; expect(note.beat.graceType).toEqual(GraceType.BeforeBeat); expect(note.bendType).toEqual(BendType.Bend); - expect(note.bendPoints.length).toEqual(2); - expect(note.bendPoints[0].offset).toBeCloseTo(0); - expect(note.bendPoints[0].value).toEqual(0); - expect(note.bendPoints[1].offset).toBeCloseTo(15); - expect(note.bendPoints[1].value).toEqual(4); + expect(note.bendPoints!.length).toEqual(2); + expect(note.bendPoints![0].offset).toBeCloseTo(0); + expect(note.bendPoints![0].value).toEqual(0); + expect(note.bendPoints![1].offset).toBeCloseTo(15); + expect(note.bendPoints![1].value).toEqual(4); note = score.tracks[0].staves[0].bars[12].voices[0].beats[1].notes[0]; expect(note.isContinuedBend).toBe(true); expect(note.bendType).toEqual(BendType.Hold); - expect(note.bendPoints.length).toEqual(2); - expect(note.bendPoints[0].offset).toBeCloseTo(0); - expect(note.bendPoints[0].value).toEqual(4); - expect(note.bendPoints[1].offset).toBeCloseTo(60); - expect(note.bendPoints[1].value).toEqual(4); + expect(note.bendPoints!.length).toEqual(2); + expect(note.bendPoints![0].offset).toBeCloseTo(0); + expect(note.bendPoints![0].value).toEqual(4); + expect(note.bendPoints![1].offset).toBeCloseTo(60); + expect(note.bendPoints![1].value).toEqual(4); // // Bar 14 note = score.tracks[0].staves[0].bars[13].voices[0].beats[0].notes[0]; expect(note.beat.graceType).toEqual(GraceType.OnBeat); expect(note.bendType).toEqual(BendType.Bend); - expect(note.bendPoints.length).toEqual(2); - expect(note.bendPoints[0].offset).toBeCloseTo(0); - expect(note.bendPoints[0].value).toEqual(0); - expect(note.bendPoints[1].offset).toBeCloseTo(18); - expect(note.bendPoints[1].value).toEqual(1); + expect(note.bendPoints!.length).toEqual(2); + expect(note.bendPoints![0].offset).toBeCloseTo(0); + expect(note.bendPoints![0].value).toEqual(0); + expect(note.bendPoints![1].offset).toBeCloseTo(18); + expect(note.bendPoints![1].value).toEqual(1); note = score.tracks[0].staves[0].bars[13].voices[0].beats[1].notes[0]; expect(note.isContinuedBend).toBe(true); expect(note.bendType).toEqual(BendType.Hold); - expect(note.bendPoints.length).toEqual(2); - expect(note.bendPoints[0].offset).toBeCloseTo(0); - expect(note.bendPoints[0].value).toEqual(1); - expect(note.bendPoints[1].offset).toBeCloseTo(60); - expect(note.bendPoints[1].value).toEqual(1); + expect(note.bendPoints!.length).toEqual(2); + expect(note.bendPoints![0].offset).toBeCloseTo(0); + expect(note.bendPoints![0].value).toEqual(1); + expect(note.bendPoints![1].offset).toBeCloseTo(60); + expect(note.bendPoints![1].value).toEqual(1); // // Bar 15 note = score.tracks[0].staves[0].bars[14].voices[0].beats[0].notes[0]; expect(note.beat.graceType).toEqual(GraceType.BeforeBeat); expect(note.bendType).toEqual(BendType.Bend); - expect(note.bendPoints.length).toEqual(2); - expect(note.bendPoints[0].offset).toBeCloseTo(0); - expect(note.bendPoints[0].value).toEqual(0); - expect(note.bendPoints[1].offset).toBeCloseTo(15); - expect(note.bendPoints[1].value).toEqual(4); + expect(note.bendPoints!.length).toEqual(2); + expect(note.bendPoints![0].offset).toBeCloseTo(0); + expect(note.bendPoints![0].value).toEqual(0); + expect(note.bendPoints![1].offset).toBeCloseTo(15); + expect(note.bendPoints![1].value).toEqual(4); note = score.tracks[0].staves[0].bars[14].voices[0].beats[1].notes[0]; expect(note.fret).toEqual(12); expect(note.isTieDestination).toBe(true); expect(note.isContinuedBend).toBe(true); expect(note.bendType).toEqual(BendType.Hold); - expect(note.bendPoints.length).toEqual(2); - expect(note.bendPoints[0].offset).toBeCloseTo(0); - expect(note.bendPoints[0].value).toEqual(4); - expect(note.bendPoints[1].offset).toBeCloseTo(60); - expect(note.bendPoints[1].value).toEqual(4); + expect(note.bendPoints!.length).toEqual(2); + expect(note.bendPoints![0].offset).toBeCloseTo(0); + expect(note.bendPoints![0].value).toEqual(4); + expect(note.bendPoints![1].offset).toBeCloseTo(60); + expect(note.bendPoints![1].value).toEqual(4); note = score.tracks[0].staves[0].bars[14].voices[0].beats[1].notes[1]; expect(note.fret).toEqual(10); @@ -408,11 +408,11 @@ describe('Gp7ImporterTest', () => { // // Bar 16 note = score.tracks[0].staves[0].bars[15].voices[0].beats[0].notes[1]; expect(note.bendType).toEqual(BendType.Bend); - expect(note.bendPoints.length).toEqual(2); - expect(note.bendPoints[0].offset).toBeCloseTo(0); - expect(note.bendPoints[0].value).toEqual(0); - expect(note.bendPoints[1].offset).toBeCloseTo(15); - expect(note.bendPoints[1].value).toEqual(4); + expect(note.bendPoints!.length).toEqual(2); + expect(note.bendPoints![0].offset).toBeCloseTo(0); + expect(note.bendPoints![0].value).toEqual(0); + expect(note.bendPoints![1].offset).toBeCloseTo(15); + expect(note.bendPoints![1].value).toEqual(4); }); it('whammy-advanced', async () => { @@ -422,207 +422,207 @@ describe('Gp7ImporterTest', () => { // Bar 1 let beat: Beat = score.tracks[0].staves[0].bars[0].voices[0].beats[0]; expect(beat.whammyBarType).toEqual(WhammyType.Dive); - expect(beat.whammyBarPoints.length).toEqual(2); - expect(beat.whammyBarPoints[0].offset).toBeCloseTo(0); - expect(beat.whammyBarPoints[0].value).toEqual(0); - expect(beat.whammyBarPoints[1].offset).toBeCloseTo(45); - expect(beat.whammyBarPoints[1].value).toEqual(-4); + expect(beat.whammyBarPoints!.length).toEqual(2); + expect(beat.whammyBarPoints![0].offset).toBeCloseTo(0); + expect(beat.whammyBarPoints![0].value).toEqual(0); + expect(beat.whammyBarPoints![1].offset).toBeCloseTo(45); + expect(beat.whammyBarPoints![1].value).toEqual(-4); beat = score.tracks[0].staves[0].bars[0].voices[0].beats[2]; expect(beat.whammyBarType).toEqual(WhammyType.PrediveDive); - expect(beat.whammyBarPoints.length).toEqual(2); - expect(beat.whammyBarPoints[0].offset).toBeCloseTo(0); - expect(beat.whammyBarPoints[0].value).toEqual(-4); - expect(beat.whammyBarPoints[1].offset).toBeCloseTo(60); - expect(beat.whammyBarPoints[1].value).toEqual(-16); + expect(beat.whammyBarPoints!.length).toEqual(2); + expect(beat.whammyBarPoints![0].offset).toBeCloseTo(0); + expect(beat.whammyBarPoints![0].value).toEqual(-4); + expect(beat.whammyBarPoints![1].offset).toBeCloseTo(60); + expect(beat.whammyBarPoints![1].value).toEqual(-16); // Bar 2 beat = score.tracks[0].staves[0].bars[1].voices[0].beats[0]; expect(beat.whammyBarType).toEqual(WhammyType.Dip); - expect(beat.whammyBarPoints.length).toEqual(3); - expect(beat.whammyBarPoints[0].offset).toBeCloseTo(0); - expect(beat.whammyBarPoints[0].value).toEqual(0); - expect(beat.whammyBarPoints[1].offset).toBeCloseTo(15); - expect(beat.whammyBarPoints[1].value).toEqual(-16); - expect(beat.whammyBarPoints[2].offset).toBeCloseTo(30); - expect(beat.whammyBarPoints[2].value).toEqual(0); + expect(beat.whammyBarPoints!.length).toEqual(3); + expect(beat.whammyBarPoints![0].offset).toBeCloseTo(0); + expect(beat.whammyBarPoints![0].value).toEqual(0); + expect(beat.whammyBarPoints![1].offset).toBeCloseTo(15); + expect(beat.whammyBarPoints![1].value).toEqual(-16); + expect(beat.whammyBarPoints![2].offset).toBeCloseTo(30); + expect(beat.whammyBarPoints![2].value).toEqual(0); beat = score.tracks[0].staves[0].bars[1].voices[0].beats[2]; expect(beat.whammyBarType).toEqual(WhammyType.Dip); - expect(beat.whammyBarPoints.length).toEqual(4); - expect(beat.whammyBarPoints[0].offset).toBeCloseTo(0); - expect(beat.whammyBarPoints[0].value).toEqual(0); - expect(beat.whammyBarPoints[1].offset).toBeCloseTo(14.4); - expect(beat.whammyBarPoints[1].value).toEqual(-12); - expect(beat.whammyBarPoints[2].offset).toBeCloseTo(31.8); - expect(beat.whammyBarPoints[2].value).toEqual(-12); - expect(beat.whammyBarPoints[3].offset).toBeCloseTo(53.4); - expect(beat.whammyBarPoints[3].value).toEqual(0); + expect(beat.whammyBarPoints!.length).toEqual(4); + expect(beat.whammyBarPoints![0].offset).toBeCloseTo(0); + expect(beat.whammyBarPoints![0].value).toEqual(0); + expect(beat.whammyBarPoints![1].offset).toBeCloseTo(14.4); + expect(beat.whammyBarPoints![1].value).toEqual(-12); + expect(beat.whammyBarPoints![2].offset).toBeCloseTo(31.8); + expect(beat.whammyBarPoints![2].value).toEqual(-12); + expect(beat.whammyBarPoints![3].offset).toBeCloseTo(53.4); + expect(beat.whammyBarPoints![3].value).toEqual(0); // Bar 3 beat = score.tracks[0].staves[0].bars[2].voices[0].beats[0]; expect(beat.whammyBarType).toEqual(WhammyType.Dip); - expect(beat.whammyBarPoints.length).toEqual(3); - expect(beat.whammyBarPoints[0].offset).toBeCloseTo(0); - expect(beat.whammyBarPoints[0].value).toEqual(0); - expect(beat.whammyBarPoints[1].offset).toBeCloseTo(15); - expect(beat.whammyBarPoints[1].value).toEqual(-16); - expect(beat.whammyBarPoints[2].offset).toBeCloseTo(30); - expect(beat.whammyBarPoints[2].value).toEqual(0); + expect(beat.whammyBarPoints!.length).toEqual(3); + expect(beat.whammyBarPoints![0].offset).toBeCloseTo(0); + expect(beat.whammyBarPoints![0].value).toEqual(0); + expect(beat.whammyBarPoints![1].offset).toBeCloseTo(15); + expect(beat.whammyBarPoints![1].value).toEqual(-16); + expect(beat.whammyBarPoints![2].offset).toBeCloseTo(30); + expect(beat.whammyBarPoints![2].value).toEqual(0); beat = score.tracks[0].staves[0].bars[2].voices[0].beats[2]; expect(beat.whammyBarType).toEqual(WhammyType.Dip); - expect(beat.whammyBarPoints.length).toEqual(4); - expect(beat.whammyBarPoints[0].offset).toBeCloseTo(0); - expect(beat.whammyBarPoints[0].value).toEqual(0); - expect(beat.whammyBarPoints[1].offset).toBeCloseTo(14.4); - expect(beat.whammyBarPoints[1].value).toEqual(-12); - expect(beat.whammyBarPoints[2].offset).toBeCloseTo(31.8); - expect(beat.whammyBarPoints[2].value).toEqual(-12); - expect(beat.whammyBarPoints[3].offset).toBeCloseTo(53.4); - expect(beat.whammyBarPoints[3].value).toEqual(0); + expect(beat.whammyBarPoints!.length).toEqual(4); + expect(beat.whammyBarPoints![0].offset).toBeCloseTo(0); + expect(beat.whammyBarPoints![0].value).toEqual(0); + expect(beat.whammyBarPoints![1].offset).toBeCloseTo(14.4); + expect(beat.whammyBarPoints![1].value).toEqual(-12); + expect(beat.whammyBarPoints![2].offset).toBeCloseTo(31.8); + expect(beat.whammyBarPoints![2].value).toEqual(-12); + expect(beat.whammyBarPoints![3].offset).toBeCloseTo(53.4); + expect(beat.whammyBarPoints![3].value).toEqual(0); // Bar 4 beat = score.tracks[0].staves[0].bars[3].voices[0].beats[0]; expect(beat.whammyBarType).toEqual(WhammyType.Predive); - expect(beat.whammyBarPoints.length).toEqual(2); - expect(beat.whammyBarPoints[0].offset).toBeCloseTo(0); - expect(beat.whammyBarPoints[0].value).toEqual(-8); - expect(beat.whammyBarPoints[1].offset).toBeCloseTo(60); - expect(beat.whammyBarPoints[1].value).toEqual(-8); + expect(beat.whammyBarPoints!.length).toEqual(2); + expect(beat.whammyBarPoints![0].offset).toBeCloseTo(0); + expect(beat.whammyBarPoints![0].value).toEqual(-8); + expect(beat.whammyBarPoints![1].offset).toBeCloseTo(60); + expect(beat.whammyBarPoints![1].value).toEqual(-8); // Bar 5 beat = score.tracks[0].staves[0].bars[4].voices[0].beats[0]; expect(beat.whammyBarType).toEqual(WhammyType.PrediveDive); - expect(beat.whammyBarPoints.length).toEqual(2); - expect(beat.whammyBarPoints[0].offset).toBeCloseTo(0); - expect(beat.whammyBarPoints[0].value).toEqual(-4); - expect(beat.whammyBarPoints[1].offset).toBeCloseTo(30); - expect(beat.whammyBarPoints[1].value).toEqual(0); + expect(beat.whammyBarPoints!.length).toEqual(2); + expect(beat.whammyBarPoints![0].offset).toBeCloseTo(0); + expect(beat.whammyBarPoints![0].value).toEqual(-4); + expect(beat.whammyBarPoints![1].offset).toBeCloseTo(30); + expect(beat.whammyBarPoints![1].value).toEqual(0); // Bar 6 beat = score.tracks[0].staves[0].bars[5].voices[0].beats[0]; expect(beat.whammyBarType).toEqual(WhammyType.PrediveDive); - expect(beat.whammyBarPoints.length).toEqual(2); - expect(beat.whammyBarPoints[0].offset).toBeCloseTo(0); - expect(beat.whammyBarPoints[0].value).toEqual(-4); - expect(beat.whammyBarPoints[1].offset).toBeCloseTo(29.4); - expect(beat.whammyBarPoints[1].value).toEqual(-12); + expect(beat.whammyBarPoints!.length).toEqual(2); + expect(beat.whammyBarPoints![0].offset).toBeCloseTo(0); + expect(beat.whammyBarPoints![0].value).toEqual(-4); + expect(beat.whammyBarPoints![1].offset).toBeCloseTo(29.4); + expect(beat.whammyBarPoints![1].value).toEqual(-12); beat = score.tracks[0].staves[0].bars[5].voices[0].beats[1]; expect(beat.whammyBarType).toEqual(WhammyType.Dive); - expect(beat.whammyBarPoints.length).toEqual(2); - expect(beat.whammyBarPoints[0].offset).toBeCloseTo(0); - expect(beat.whammyBarPoints[0].value).toEqual(-12); - expect(beat.whammyBarPoints[1].offset).toBeCloseTo(45.6); - expect(beat.whammyBarPoints[1].value).toEqual(0); + expect(beat.whammyBarPoints!.length).toEqual(2); + expect(beat.whammyBarPoints![0].offset).toBeCloseTo(0); + expect(beat.whammyBarPoints![0].value).toEqual(-12); + expect(beat.whammyBarPoints![1].offset).toBeCloseTo(45.6); + expect(beat.whammyBarPoints![1].value).toEqual(0); // Bar 7 beat = score.tracks[0].staves[0].bars[6].voices[0].beats[0]; expect(beat.whammyBarType).toEqual(WhammyType.Dive); - expect(beat.whammyBarPoints.length).toEqual(2); - expect(beat.whammyBarPoints[0].offset).toBeCloseTo(0); - expect(beat.whammyBarPoints[0].value).toEqual(0); - expect(beat.whammyBarPoints[1].offset).toBeCloseTo(45); - expect(beat.whammyBarPoints[1].value).toEqual(-4); + expect(beat.whammyBarPoints!.length).toEqual(2); + expect(beat.whammyBarPoints![0].offset).toBeCloseTo(0); + expect(beat.whammyBarPoints![0].value).toEqual(0); + expect(beat.whammyBarPoints![1].offset).toBeCloseTo(45); + expect(beat.whammyBarPoints![1].value).toEqual(-4); beat = score.tracks[0].staves[0].bars[6].voices[0].beats[1]; expect(beat.whammyBarType).toEqual(WhammyType.Hold); - expect(beat.whammyBarPoints.length).toEqual(2); - expect(beat.whammyBarPoints[0].offset).toBeCloseTo(0); - expect(beat.whammyBarPoints[0].value).toEqual(-4); - expect(beat.whammyBarPoints[1].offset).toBeCloseTo(60); - expect(beat.whammyBarPoints[1].value).toEqual(-4); + expect(beat.whammyBarPoints!.length).toEqual(2); + expect(beat.whammyBarPoints![0].offset).toBeCloseTo(0); + expect(beat.whammyBarPoints![0].value).toEqual(-4); + expect(beat.whammyBarPoints![1].offset).toBeCloseTo(60); + expect(beat.whammyBarPoints![1].value).toEqual(-4); // Bar 8 beat = score.tracks[0].staves[0].bars[7].voices[0].beats[0]; expect(beat.whammyBarType).toEqual(WhammyType.Dive); - expect(beat.whammyBarPoints.length).toEqual(2); - expect(beat.whammyBarPoints[0].offset).toBeCloseTo(0); - expect(beat.whammyBarPoints[0].value).toEqual(-4); - expect(beat.whammyBarPoints[1].offset).toBeCloseTo(46.2); - expect(beat.whammyBarPoints[1].value).toEqual(-12); + expect(beat.whammyBarPoints!.length).toEqual(2); + expect(beat.whammyBarPoints![0].offset).toBeCloseTo(0); + expect(beat.whammyBarPoints![0].value).toEqual(-4); + expect(beat.whammyBarPoints![1].offset).toBeCloseTo(46.2); + expect(beat.whammyBarPoints![1].value).toEqual(-12); beat = score.tracks[0].staves[0].bars[7].voices[0].beats[1]; expect(beat.whammyBarType).toEqual(WhammyType.Dive); - expect(beat.whammyBarPoints.length).toEqual(2); - expect(beat.whammyBarPoints[0].offset).toBeCloseTo(0); - expect(beat.whammyBarPoints[0].value).toEqual(-12); - expect(beat.whammyBarPoints[1].offset).toBeCloseTo(44.4); - expect(beat.whammyBarPoints[1].value).toEqual(8); + expect(beat.whammyBarPoints!.length).toEqual(2); + expect(beat.whammyBarPoints![0].offset).toBeCloseTo(0); + expect(beat.whammyBarPoints![0].value).toEqual(-12); + expect(beat.whammyBarPoints![1].offset).toBeCloseTo(44.4); + expect(beat.whammyBarPoints![1].value).toEqual(8); // Bar 9 beat = score.tracks[0].staves[0].bars[8].voices[0].beats[0]; expect(beat.whammyBarType).toEqual(WhammyType.Dip); - expect(beat.whammyBarPoints.length).toEqual(3); - expect(beat.whammyBarPoints[0].offset).toBeCloseTo(0); - expect(beat.whammyBarPoints[0].value).toEqual(8); - expect(beat.whammyBarPoints[1].offset).toBeCloseTo(15); - expect(beat.whammyBarPoints[1].value).toEqual(12); - expect(beat.whammyBarPoints[2].offset).toBeCloseTo(30); - expect(beat.whammyBarPoints[2].value).toEqual(0); + expect(beat.whammyBarPoints!.length).toEqual(3); + expect(beat.whammyBarPoints![0].offset).toBeCloseTo(0); + expect(beat.whammyBarPoints![0].value).toEqual(8); + expect(beat.whammyBarPoints![1].offset).toBeCloseTo(15); + expect(beat.whammyBarPoints![1].value).toEqual(12); + expect(beat.whammyBarPoints![2].offset).toBeCloseTo(30); + expect(beat.whammyBarPoints![2].value).toEqual(0); beat = score.tracks[0].staves[0].bars[8].voices[0].beats[1]; expect(beat.whammyBarType).toEqual(WhammyType.Dip); - expect(beat.whammyBarPoints.length).toEqual(3); - expect(beat.whammyBarPoints[0].offset).toBeCloseTo(0); - expect(beat.whammyBarPoints[0].value).toEqual(0); - expect(beat.whammyBarPoints[1].offset).toBeCloseTo(15); - expect(beat.whammyBarPoints[1].value).toEqual(-4); - expect(beat.whammyBarPoints[2].offset).toBeCloseTo(30); - expect(beat.whammyBarPoints[2].value).toEqual(0); + expect(beat.whammyBarPoints!.length).toEqual(3); + expect(beat.whammyBarPoints![0].offset).toBeCloseTo(0); + expect(beat.whammyBarPoints![0].value).toEqual(0); + expect(beat.whammyBarPoints![1].offset).toBeCloseTo(15); + expect(beat.whammyBarPoints![1].value).toEqual(-4); + expect(beat.whammyBarPoints![2].offset).toBeCloseTo(30); + expect(beat.whammyBarPoints![2].value).toEqual(0); }); it('tremolo', async () => { const reader = await prepareGp7ImporterWithFile('guitarpro7/tremolo.gp'); let score: Score = reader.readScore(); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints.length).toEqual(3); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints!.length).toEqual(3); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints[0].offset).toBeCloseTo(0); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints[0].value).toEqual(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![0].offset).toBeCloseTo(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![0].value).toEqual(0); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints[1].offset).toBeCloseTo(30); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints[1].value).toEqual(-4); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![1].offset).toBeCloseTo(30); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![1].value).toEqual(-4); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints[2].offset).toBeCloseTo(60); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints[2].value).toEqual(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![2].offset).toBeCloseTo(60); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![2].value).toEqual(0); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints.length).toEqual(2); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints!.length).toEqual(2); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints[0].offset).toBeCloseTo(0); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints[0].value).toEqual(-4); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints![0].offset).toBeCloseTo(0); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints![0].value).toEqual(-4); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints[1].offset).toBeCloseTo(60); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints[1].value).toEqual(0); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints![1].offset).toBeCloseTo(60); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints![1].value).toEqual(0); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints.length).toEqual(4); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints!.length).toEqual(4); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints[0].offset).toBeCloseTo(0); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints[0].value).toEqual(0); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![0].offset).toBeCloseTo(0); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![0].value).toEqual(0); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints[1].offset).toBeCloseTo(30); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints[1].value).toEqual(-4); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![1].offset).toBeCloseTo(30); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![1].value).toEqual(-4); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints[2].offset).toBeCloseTo(30); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints[2].value).toEqual(-4); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![2].offset).toBeCloseTo(30); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![2].value).toEqual(-4); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints[3].offset).toBeCloseTo(60); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints[3].value).toEqual(-4); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![3].offset).toBeCloseTo(60); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![3].value).toEqual(-4); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints.length).toEqual(4); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints!.length).toEqual(4); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints[0].offset).toBeCloseTo(0); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints[0].value).toEqual(-4); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints![0].offset).toBeCloseTo(0); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints![0].value).toEqual(-4); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints[1].offset).toBeCloseTo(15); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints[1].value).toEqual(-12); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints![1].offset).toBeCloseTo(15); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints![1].value).toEqual(-12); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints[2].offset).toBeCloseTo(30.6); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints[2].value).toEqual(-12); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints![2].offset).toBeCloseTo(30.6); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints![2].value).toEqual(-12); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints[3].offset).toBeCloseTo(45); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints[3].value).toEqual(0); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints![3].offset).toBeCloseTo(45); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints![3].value).toEqual(0); }); it('slides', async () => { @@ -765,9 +765,9 @@ describe('Gp7ImporterTest', () => { it('fermata', async () => { const reader = await prepareGp7ImporterWithFile('guitarpro7/fermata.gp'); let score: Score = reader.readScore(); - expect(score.masterBars[0].fermata.size).toEqual(5); - expect(score.masterBars[1].fermata.size).toEqual(5); - expect(score.masterBars[2].fermata.size).toEqual(5); // Short + expect(score.masterBars[0].fermata!.size).toEqual(5); + expect(score.masterBars[1].fermata!.size).toEqual(5); + expect(score.masterBars[2].fermata!.size).toEqual(5); // Short let offsets = [ 0, (MidiUtils.QuarterTime * (1 / 2)) | 0, @@ -778,15 +778,15 @@ describe('Gp7ImporterTest', () => { let types: FermataType[] = [FermataType.Short, FermataType.Medium, FermataType.Long]; for (let i: number = 0; i < 3; i++) { let masterBar: MasterBar = score.masterBars[i]; - expect(masterBar.fermata.size).toEqual(5); + expect(masterBar.fermata!.size).toEqual(5); for (let offset of offsets) { - let fermata = masterBar.fermata.get(offset); + let fermata = masterBar.fermata!.get(offset); expect(fermata).toBeTruthy(); expect(fermata!.type).toEqual(types[i]); } let beats: Beat[] = score.tracks[0].staves[0].bars[i].voices[0].beats; for (let beat of beats) { - let fermata = masterBar.fermata.get(beat.playbackStart); + let fermata = masterBar.fermata!.get(beat.playbackStart); let beatFermata = beat.fermata; expect(beatFermata).toBeTruthy(); expect(fermata).toBeTruthy(); diff --git a/test/importer/GpImporterTestHelper.ts b/test/importer/GpImporterTestHelper.ts index dc9aeed2c..965f11fc5 100644 --- a/test/importer/GpImporterTestHelper.ts +++ b/test/importer/GpImporterTestHelper.ts @@ -160,74 +160,74 @@ export class GpImporterTestHelper { } public static checkBend(score: Score): void { - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints.length).toEqual(3); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints!.length).toEqual(3); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints[0].offset).toEqual(0); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints[0].value).toEqual(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints![0].offset).toEqual(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints![0].value).toEqual(0); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints[1].offset).toEqual(15); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints[1].value).toEqual(4); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints![1].offset).toEqual(15); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints![1].value).toEqual(4); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints[2].offset).toEqual(60); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints[2].value).toEqual(4); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints![2].offset).toEqual(60); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints![2].value).toEqual(4); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints.length).toEqual(7); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints!.length).toEqual(7); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints[0].offset).toEqual(0); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints[0].value).toEqual(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![0].offset).toEqual(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![0].value).toEqual(0); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints[1].offset).toEqual(10); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints[1].value).toEqual(4); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![1].offset).toEqual(10); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![1].value).toEqual(4); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints[2].offset).toEqual(20); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints[2].value).toEqual(4); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![2].offset).toEqual(20); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![2].value).toEqual(4); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints[3].offset).toEqual(30); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints[3].value).toEqual(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![3].offset).toEqual(30); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![3].value).toEqual(0); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints[4].offset).toEqual(40); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints[4].value).toEqual(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![4].offset).toEqual(40); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![4].value).toEqual(0); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints[5].offset).toEqual(50); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints[5].value).toEqual(4); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![5].offset).toEqual(50); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![5].value).toEqual(4); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints[6].offset).toEqual(60); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints[6].value).toEqual(4); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![6].offset).toEqual(60); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![6].value).toEqual(4); } public static checkTremolo(score: Score): void { - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints.length).toEqual(3); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints!.length).toEqual(3); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints[0].offset).toEqual(0); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints[0].value).toEqual(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![0].offset).toEqual(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![0].value).toEqual(0); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints[1].offset).toEqual(30); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints[1].value).toEqual(-4); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![1].offset).toEqual(30); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![1].value).toEqual(-4); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints[2].offset).toEqual(60); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints[2].value).toEqual(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![2].offset).toEqual(60); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![2].value).toEqual(0); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints.length).toEqual(3); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints!.length).toEqual(3); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints[0].offset).toEqual(0); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints[0].value).toEqual(-4); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints![0].offset).toEqual(0); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints![0].value).toEqual(-4); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints[1].offset).toEqual(45); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints[1].value).toEqual(-4); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints![1].offset).toEqual(45); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints![1].value).toEqual(-4); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints[2].offset).toEqual(60); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints[2].value).toEqual(0); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints![2].offset).toEqual(60); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints![2].value).toEqual(0); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints.length).toEqual(3); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints!.length).toEqual(3); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints[0].offset).toEqual(0); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints[0].value).toEqual(0); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![0].offset).toEqual(0); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![0].value).toEqual(0); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints[1].offset).toEqual(45); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints[1].value).toEqual(-4); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![1].offset).toEqual(45); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![1].value).toEqual(-4); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints[2].offset).toEqual(60); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints[2].value).toEqual(-4); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![2].offset).toEqual(60); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![2].value).toEqual(-4); } public static checkSlides(score: Score): void { @@ -477,7 +477,7 @@ export class GpImporterTestHelper { public static checkChords(score: Score): void { let track: Track = score.tracks[0]; let staff: Staff = track.staves[0]; - expect(staff.chords.size).toEqual(8); + expect(staff.chords!.size).toEqual(8); GpImporterTestHelper.checkChord( GpImporterTestHelper.createChord('C', 1, [0, 1, 0, 2, 3, -1]), diff --git a/test/importer/GpxImporter.test.ts b/test/importer/GpxImporter.test.ts index e56fe537e..58b74aa63 100644 --- a/test/importer/GpxImporter.test.ts +++ b/test/importer/GpxImporter.test.ts @@ -105,84 +105,84 @@ describe('GpxImporterTest', () => { it('bends', async () => { const reader = await prepareGpxImporterWithFile('guitarpro6/bends.gpx'); let score: Score = reader.readScore(); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints.length).toEqual(2); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints!.length).toEqual(2); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints[0].offset).toBeCloseTo(0); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints[0].value).toEqual(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints![0].offset).toBeCloseTo(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints![0].value).toEqual(0); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints[1].offset).toBeCloseTo(60); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints[1].value).toEqual(4); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints![1].offset).toBeCloseTo(60); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints![1].value).toEqual(4); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints.length).toEqual(2); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints!.length).toEqual(2); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints[0].offset).toBeCloseTo(0); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints[0].value).toEqual(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![0].offset).toBeCloseTo(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![0].value).toEqual(0); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints[1].offset).toBeCloseTo(60); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints[1].value).toEqual(4); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![1].offset).toBeCloseTo(60); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints![1].value).toEqual(4); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints.length).toEqual(3); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints!.length).toEqual(3); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints[0].offset).toBeCloseTo(0); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints[0].value).toEqual(0); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints![0].offset).toBeCloseTo(0); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints![0].value).toEqual(0); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints[0].offset).toBeCloseTo(0); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints[0].value).toEqual(0); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints![0].offset).toBeCloseTo(0); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints![0].value).toEqual(0); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints[1].offset).toBeCloseTo(30); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints[1].value).toEqual(12); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints![1].offset).toBeCloseTo(30); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints![1].value).toEqual(12); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints[2].offset).toBeCloseTo(60); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints[2].value).toEqual(6); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints![2].offset).toBeCloseTo(60); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints![2].value).toEqual(6); }); it('tremolo', async () => { const reader = await prepareGpxImporterWithFile('guitarpro6/tremolo.gpx'); let score: Score = reader.readScore(); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints.length).toEqual(3); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints!.length).toEqual(3); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints[0].offset).toBeCloseTo(0); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints[0].value).toEqual(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![0].offset).toBeCloseTo(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![0].value).toEqual(0); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints[1].offset).toBeCloseTo(30); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints[1].value).toEqual(-4); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![1].offset).toBeCloseTo(30); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![1].value).toEqual(-4); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints[2].offset).toBeCloseTo(60); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints[2].value).toEqual(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![2].offset).toBeCloseTo(60); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![2].value).toEqual(0); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints.length).toEqual(2); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints!.length).toEqual(2); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints[0].offset).toBeCloseTo(0); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints[0].value).toEqual(-4); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints![0].offset).toBeCloseTo(0); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints![0].value).toEqual(-4); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints[1].offset).toBeCloseTo(60); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints[1].value).toEqual(0); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints![1].offset).toBeCloseTo(60); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints![1].value).toEqual(0); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints.length).toEqual(3); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints!.length).toEqual(3); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints[0].offset).toBeCloseTo(0); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints[0].value).toEqual(0); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![0].offset).toBeCloseTo(0); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![0].value).toEqual(0); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints[1].offset).toBeCloseTo(30); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints[1].value).toEqual(-4); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![1].offset).toBeCloseTo(30); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![1].value).toEqual(-4); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints[2].offset).toBeCloseTo(60); - expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints[2].value).toEqual(-4); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![2].offset).toBeCloseTo(60); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints![2].value).toEqual(-4); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints.length).toEqual(4); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints!.length).toEqual(4); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints[0].offset).toBeCloseTo(0); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints[0].value).toEqual(-4); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints![0].offset).toBeCloseTo(0); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints![0].value).toEqual(-4); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints[1].offset).toBeCloseTo(15); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints[1].value).toEqual(-12); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints![1].offset).toBeCloseTo(15); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints![1].value).toEqual(-12); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints[2].offset).toBeCloseTo(30.6); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints[2].value).toEqual(-12); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints![2].offset).toBeCloseTo(30.6); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints![2].value).toEqual(-12); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints[3].offset).toBeCloseTo(45); - expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints[3].value).toEqual(0); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints![3].offset).toBeCloseTo(45); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints![3].value).toEqual(0); }); it('slides', async () => { diff --git a/test/importer/MusicXmlImporterTestHelper.ts b/test/importer/MusicXmlImporterTestHelper.ts index 3d6d49913..a700b1526 100644 --- a/test/importer/MusicXmlImporterTestHelper.ts +++ b/test/importer/MusicXmlImporterTestHelper.ts @@ -224,7 +224,11 @@ export class MusicXmlImporterTestHelper { } } - protected expectBendPointsEqual(expected: BendPoint[], actual: BendPoint[]): void { + protected expectBendPointsEqual(expected: BendPoint[] | null, actual: BendPoint[] | null): void { + if(expected == null || actual == null) { + expect(actual).toEqual(expected) + return; + } expect(actual.length).toEqual(expected.length, 'Mismatch on Count'); for (let i: number = 0; i < expected.length; i++) { expect(actual[i].value).toEqual(actual[i].value); diff --git a/test/model/ComparisonHelpers.ts b/test/model/ComparisonHelpers.ts index 00e150099..cb3ce45e2 100644 --- a/test/model/ComparisonHelpers.ts +++ b/test/model/ComparisonHelpers.ts @@ -1,6 +1,13 @@ - +/** + * @partial + */ export class ComparisonHelpers { - public static expectJsonEqual(expected: unknown, actual: unknown, path: string, ignoreKeys: string[] | null): boolean { + public static expectJsonEqual( + expected: unknown, + actual: unknown, + path: string, + ignoreKeys: string[] | null + ): boolean { const expectedType = typeof expected; const actualType = typeof actual; @@ -40,7 +47,14 @@ export class ComparisonHelpers { result = false; } else { for (let i = 0; i < actual.length; i++) { - if(!ComparisonHelpers.expectJsonEqual(expected[i], actual[i], `${path}[${i}]`, ignoreKeys)) { + if ( + !ComparisonHelpers.expectJsonEqual( + expected[i], + actual[i], + `${path}[${i}]`, + ignoreKeys + ) + ) { result = false; } } @@ -53,46 +67,54 @@ export class ComparisonHelpers { const expectedMap = expected as Map<string, unknown>; const actualMap = actual as Map<string, unknown>; - const expectedKeys = Array.from(expectedMap.keys()); - const actualKeys = Array.from(actualMap.keys()); + const ignoredKeyLookup: Set<string> = new Set<string>([ + 'id', + 'hammerpulloriginnoteid', + 'hammerpulldestinationnoteid', + 'tieoriginnoteid', + 'tiedestinationnoteid', + 'sluroriginnoteid', + 'slurdestinationnoteid' + ]); + if (ignoreKeys) { + for (const k of ignoreKeys) { + ignoredKeyLookup.add(k); + } + } + const expectedKeys = Array.from(expectedMap.keys()).filter(k => !ignoredKeyLookup.has(k)); + const actualKeys = Array.from(actualMap.keys()).filter(k => !ignoredKeyLookup.has(k)); expectedKeys.sort(); actualKeys.sort(); const actualKeyList = actualKeys.join(','); const expectedKeyList = expectedKeys.join(','); if (actualKeyList !== expectedKeyList) { - fail(`Object Keys mismatch on hierarchy: ${path}, '${actualKeyList}' != '${expectedKeyList}'`); + fail( + `Object Keys mismatch on hierarchy: ${path}, '${actualKeyList}' != '${expectedKeyList}'` + ); result = false; } else { for (const key of actualKeys) { - switch (key) { - // some ignored keys - case 'id': - case 'hammerPullOriginNoteId': - case 'hammerPullDestinationNoteId': - case 'tieOriginNoteId': - case 'tieDestinationNoteId': - break; - default: - if (!ignoreKeys || ignoreKeys.indexOf(key) === -1) { - if(!ComparisonHelpers.expectJsonEqual(expectedMap.get(key), actualMap.get(key), `${path}.${key}`, ignoreKeys)) { - result = false; - } - } - break; + if ( + !ComparisonHelpers.expectJsonEqual( + expectedMap.get(key), + actualMap.get(key), + `${path}.${key}`, + ignoreKeys + ) + ) { + result = false; } } - } } - } else { - fail('Need Map serialization for comparing json objects'); + } else if (!ComparisonHelpers.compareObjects(expected, actual, path, ignoreKeys)) { result = false; } } break; case 'string': - if ((actual as string) != (expected as string)) { + if ((actual as string) !== (expected as string)) { fail(`String mismatch on hierarchy: ${path}, '${actual}' != '${expected}'`); result = false; } @@ -108,5 +130,17 @@ export class ComparisonHelpers { return result; } - -} \ No newline at end of file + /** + * @target web + * @partial + */ + public static compareObjects( + expected: unknown, + actual: unknown, + path: string, + ignoreKeys: string[] | null + ): boolean { + fail(`Cannot compare unknown object types on path ${path}`); + return false; + } +} diff --git a/tsconfig.base.json b/tsconfig.base.json index f7f93b617..3faf0ed5f 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -18,6 +18,7 @@ "noImplicitThis": true, "noImplicitReturns": true, "noUnusedLocals": true, + "noImplicitOverride": true, "sourceMap": true, "declaration": true, "incremental": true,