diff --git a/src/data/data.ts b/src/data/data.ts index 5f1c08cb96510feda4ed6d94620d3fc5dc985b5a..7277567e6b303e6c044098c272069650b6cb0580 100644 --- a/src/data/data.ts +++ b/src/data/data.ts @@ -55,7 +55,9 @@ export interface Field { str(row: number): string | null, int(row: number): number, float(row: number): number, - bin(row: number): Uint8Array | null, + + /** The 'intrinsic value' of the field, e.g., array, binary data, ... */ + value(row: number): any, presence(row: number): ValuePresence, diff --git a/src/data/schema.ts b/src/data/schema.ts index 83f9a98906db64a4a70fee340ff1aa70e7570a81..e0fb2203b6a10d5f50545ee91099ec66cb06d2be 100644 --- a/src/data/schema.ts +++ b/src/data/schema.ts @@ -20,14 +20,11 @@ import * as Data from './data' * * const mmCIF = { atom_site }; */ -export type BlockDefinition = { [category: string]: CategoryDefinition } -export type CategoryDefinition = { '@alias'?: string } & { [field: string]: Field.Schema<any> } -export type BlockInstance<Definition extends BlockDefinition> = Block<{ [C in keyof Definition]: CategoryInstance<Definition[C]> }> -export type CategoryInstance<Definition extends CategoryDefinition> = Category<{ [F in keyof Definition]: Field<Definition[F]['type']> }> +////////////////////////////////////////////// -export function apply<Definition extends BlockDefinition>(schema: Definition, block: Data.Block): BlockInstance<Definition> { - return createBlock(schema, block) as BlockInstance<Definition>; +export function apply<Schema extends Block.Schema>(schema: Schema, block: Data.Block): Block.Instance<Schema> { + return createBlock(schema, block) as Block.Instance<Schema>; } export type Block<Categories> = Categories & { @@ -36,12 +33,22 @@ export type Block<Categories> = Categories & { _getCategory(name: string): Data.Category | undefined } +export namespace Block { + export type Schema = { [category: string]: Category.Schema } + export type Instance<T extends Schema> = Block<{ [C in keyof T]: Category.Instance<T[C]> }> +} + export type Category<Fields> = Fields & { readonly _rowCount: number, /** For accessing 'non-standard' fields */ _getField(name: string): Data.Field | undefined } +export namespace Category { + export type Schema = { '@alias'?: string } & { [field: string]: Field.Schema<any> } + export type Instance<T extends Schema> = Category<{ [F in keyof T]: Field<T[F]['type']> }> +} + export interface Field<T> { readonly isDefined: boolean, value(row: number): T, @@ -53,6 +60,13 @@ export interface Field<T> { } export namespace Field { + export interface Schema<T> { type: T, ctor: (field: Data.Field) => Field<T>, undefinedField: Data.Field, alias?: string }; + export interface Spec { undefinedField?: Data.Field, alias?: string } + + export function str(spec?: Spec) { return createSchema(spec, Str); } + export function int(spec?: Spec) { return createSchema(spec, Int); } + export function float(spec?: Spec) { return createSchema(spec, Float); } + function create<T>(field: Data.Field, value: (row: number) => T, toArray: Field<T>['toArray']): Field<T> { return { isDefined: field.isDefined, value, presence: field.presence, areValuesEqual: field.areValuesEqual, stringEquals: field.stringEquals, toArray }; } @@ -60,14 +74,13 @@ export namespace Field { function Str(field: Data.Field) { return create(field, field.str, field.toStringArray); } function Int(field: Data.Field) { return create(field, field.int, field.toNumberArray); } function Float(field: Data.Field) { return create(field, field.float, field.toNumberArray); } - function Bin(field: Data.Field) { return create(field, field.bin, (s, e, ctor) => void 0); } const DefaultUndefined: Data.Field = { isDefined: false, str: row => null, int: row => 0, float: row => 0, - bin: row => null, + value: row => null, presence: row => Data.ValuePresence.NotSpecified, areValuesEqual: (rowA, rowB) => true, @@ -82,23 +95,15 @@ export namespace Field { toNumberArray: (startRow, endRowExclusive, ctor) => new Uint8Array(endRowExclusive - startRow) as any }; - export interface Schema<T> { type: T, ctor: (field: Data.Field) => Field<T>, undefinedField: Data.Field, alias?: string }; - export interface Spec { undefinedField?: Data.Field, alias?: string } - function createSchema<T>(spec: Spec | undefined, ctor: (field: Data.Field) => Field<T>): Schema<T> { return { type: 0 as any, ctor, undefinedField: (spec && spec.undefinedField) || DefaultUndefined, alias: spec && spec.alias }; } - - export function str(spec?: Spec) { return createSchema(spec, Str); } - export function int(spec?: Spec) { return createSchema(spec, Int); } - export function float(spec?: Spec) { return createSchema(spec, Float); } - export function bin(spec?: Spec) { return createSchema(spec, Bin); } } class _Block implements Block<any> { // tslint:disable-line:class-name header = this._block.header; getCategory(name: string) { return this._block.categories[name]; } - constructor(private _block: Data.Block, schema: BlockDefinition) { + constructor(private _block: Data.Block, schema: Block.Schema) { for (const k of Object.keys(schema)) { Object.defineProperty(this, k, { value: createCategory(k, schema[k], _block), enumerable: true, writable: false, configurable: false }); } @@ -108,7 +113,7 @@ class _Block implements Block<any> { // tslint:disable-line:class-name class _Category implements Category<any> { // tslint:disable-line:class-name _rowCount = this._category.rowCount; _getField(name: string) { return this._category.getField(name); } - constructor(private _category: Data.Category, schema: CategoryDefinition) { + constructor(private _category: Data.Category, schema: Category.Schema) { const fieldKeys = Object.keys(schema).filter(k => k !== '@alias'); const cache = Object.create(null); for (const k of fieldKeys) { @@ -127,11 +132,11 @@ class _Category implements Category<any> { // tslint:disable-line:class-name } } -function createBlock(schema: BlockDefinition, block: Data.Block): any { +function createBlock(schema: Block.Schema, block: Data.Block): any { return new _Block(block, schema); } -function createCategory(key: string, schema: CategoryDefinition, block: Data.Block) { +function createCategory(key: string, schema: Category.Schema, block: Data.Block) { const cat = block.categories[schema['@alias'] || key] || Data.Category.Empty; return new _Category(cat, schema); } \ No newline at end of file diff --git a/src/data/spec/schema.spec.ts b/src/data/spec/schema.spec.ts index 1918453bf6ea97c8d0b55f17321bd8b74ffd857b..bc8788b8d0db1fdab1feef5680782c98885f0279 100644 --- a/src/data/spec/schema.spec.ts +++ b/src/data/spec/schema.spec.ts @@ -13,7 +13,7 @@ function Field(values: any[]): Data.Field { str: row => '' + values[row], int: row => +values[row] || 0, float: row => +values[row] || 0, - bin: row => null, + value: row => values[row], presence: row => Data.ValuePresence.Present, areValuesEqual: (rowA, rowB) => values[rowA] === values[rowB],