Skip to content

Commit

Permalink
Datafactory & serializer types, enums linkeddata#355
Browse files Browse the repository at this point in the history
  • Loading branch information
joepio committed Nov 4, 2019
1 parent fd6fee2 commit 428cc0a
Show file tree
Hide file tree
Showing 15 changed files with 360 additions and 169 deletions.
45 changes: 28 additions & 17 deletions src/data-factory-internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,30 @@ import Literal from './literal'
import NamedNode from './named-node'
import Statement from './statement'
import Variable from './variable'
import { TFSubject, TFPredicate, TFObject, TFGraph, TFNamedNode } from './types'
import {
TFNamedNode,
SubjectType,
PredicateType,
ObjectType,
GraphType,
} from './types'
import { Feature, IdentityFactory } from './data-factory-type'
import { Node } from './index'

export const defaultGraphURI = 'chrome:theSession'

/**
* Creates a new blank node
* @param value The blank node's identifier
*/
function blankNode(value: string): BlankNode {
function blankNode(value?: string): BlankNode {
return new BlankNode(value)
}

/**
* Gets the default graph
*/
function defaultGraph(): TFNamedNode {
function defaultGraph(): NamedNode {
return new NamedNode(defaultGraphURI)
}

Expand All @@ -27,12 +35,12 @@ function defaultGraph(): TFNamedNode {
*
* Equivalent to {Term.hashString}
*/
function id (term) {
function id (term: Node) {
if (!term) {
return term
}
if (Object.prototype.hasOwnProperty.call(term, "id") && typeof term.id === "function") {
return term.id()
if (Object.prototype.hasOwnProperty.call(term, "id") && typeof (term as NamedNode).id === "function") {
return (term as NamedNode).id()
}
if (Object.prototype.hasOwnProperty.call(term, "hashString")) {
return term.hashString()
Expand Down Expand Up @@ -93,10 +101,10 @@ function namedNode(value: string): NamedNode {
* @param graph The containing graph
*/
function quad(
subject: TFSubject,
predicate: TFPredicate,
object: TFObject,
graph?: TFGraph
subject: SubjectType,
predicate: PredicateType,
object: ObjectType,
graph?: GraphType
): Statement {
graph = graph || defaultGraph()
return new Statement(subject, predicate, object, graph)
Expand All @@ -110,8 +118,7 @@ function variable(name?: string): Variable {
return new Variable(name)
}

/** Contains the factory methods as defined in the spec, plus id */
export default {
const CanonicalDataFactory: IdentityFactory = {
blankNode,
defaultGraph,
literal,
Expand All @@ -120,10 +127,14 @@ export default {
variable,
id,
supports: {
COLLECTIONS: false,
DEFAULT_GRAPH_TYPE: true,
EQUALS_METHOD: true,
NODE_LOOKUP: false,
VARIABLE_TYPE: true,
[Feature.collections]: false,
[Feature.defaultGraphType]: true,
[Feature.equalsMethod]: true,
[Feature.identity]: true,
[Feature.reversibleIdentity]: false,
[Feature.variableType]: true,
}
}

/** Contains the factory methods as defined in the spec, plus id */
export default CanonicalDataFactory
94 changes: 94 additions & 0 deletions src/data-factory-type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { TFNamedNode, TFBlankNode, TFLiteral, TFQuad, TFTerm, TFDataFactory, TFSubject, TFObject } from "./types"

/**
* Defines a strict subset of the DataFactory as defined in the RDF/JS: Data model specification
* Non RDF-native features have been removed (e.g. no Variable, no Literal as predicate, etc.).
* bnIndex is optional but useful.
*/
export interface DataFactory<
NamedNode extends TFNamedNode = TFNamedNode,
BlankNode extends TFBlankNode = TFBlankNode,
Literal extends TFLiteral = TFLiteral,
FactoryTypes = NamedNode | TFBlankNode | Literal | TFQuad
> extends TFDataFactory {
bnIndex?: number

supports: SupportTable

literal(value: string, languageOrDatatype?: string | NamedNode): TFLiteral

literal(value: unknown): Literal

defaultGraph(): NamedNode | BlankNode

quad(
subject: NamedNode | BlankNode,
predicate: NamedNode,
object: NamedNode | BlankNode | Literal,
graph?: NamedNode
): TFQuad

isQuad(obj: any): obj is TFQuad

fromTerm(original: Literal | TFTerm): TFTerm

fromQuad(original: TFQuad): TFQuad

equals(a: Comparable, b: Comparable): boolean

toNQ(term: FactoryTypes): string
}

export interface IdentityFactory<
IndexType = Indexable,
FactoryTypes = TFNamedNode | TFBlankNode | TFLiteral | TFQuad
> extends DataFactory<FactoryTypes> {
/**
* Generates a unique session-idempotent identifier for the given object.
*
* @example NQ serialization (reversible from value)
* @example MD5 hash of termType + value (irreversible from value, map needed)
*
* @return {Indexable} A unique value which must also be a valid JS object key type.
*/
id(obj: FactoryTypes): IndexType | unknown

}

/**
* Factory type which supports reverse id lookups.
*
* It should be able to resolve the value for any given id which it handed out. Passing an id not
* generated by the same instance might result in a value or an exception depending on the
* implementation.
*/
export interface ReversibleIdentityFactory<
IndexType = Indexable,
FactoryTypes = TFNamedNode | TFBlankNode | TFLiteral | TFQuad
> extends IdentityFactory<FactoryTypes> {
fromId(id: IndexType): FactoryTypes;
}

export type Namespace = (term:string) => TFNamedNode
export type NamespaceCreator = (ns: string) => Namespace

export type SupportTable = Record<Feature, boolean>

export enum Feature {
/** Whether the factory supports termType:Collection terms */
collections = "COLLECTIONS",
/** Whether the factory supports termType:DefaultGraph terms */
defaultGraphType = "DEFAULT_GRAPH_TYPE",
/** Whether the factory supports equals on produced instances */
equalsMethod = "EQUALS_METHOD",
/** Whether the factory can generate a unique session-idempotent identifier for a given object */
identity = "IDENTITY",
/** Whether the factory supports mapping ids back to instances */
reversibleIdentity = "REVERSIBLE_IDENTITY",
/** Whether the factory supports termType:Variable terms */
variableType = "VARIABLE_TYPE",
}

export type Comparable = TFNamedNode | TFBlankNode | TFLiteral | TFQuad | undefined | null

export type Indexable = number | string
37 changes: 28 additions & 9 deletions src/data-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ import CanonicalDataFactory from './data-factory-internal'
import Fetcher from './fetcher'
import Literal from './literal'
import Statement from './statement'
import { ValueType, TFNamedNode, TFSubject, TFPredicate, TFObject, TFGraph } from './types'
import { ValueType, TFNamedNode, TFSubject, TFPredicate, TFObject, TFGraph, SubjectType, PredicateType, ObjectType, GraphType } from './types'
import IndexedFormula from './store'
import Formula from './formula'
import { DataFactory } from './data-factory-type'
import { NamedNode } from './index'

/**
* Data factory which also supports Collections
Expand All @@ -25,16 +27,34 @@ const ExtendedTermFactory = {
}
}

interface IRDFlibDataFactory extends DataFactory<NamedNode> {
fetcher: (store: Formula, options: any) => Fetcher
graph: (features, opts) => IndexedFormula
lit: (val: string, lang?: string, dt?: TFNamedNode) => Literal
st: (
subject: TFSubject,
predicate: TFPredicate,
object: TFObject,
graph?: TFGraph
) => Statement
triple: (
subject: TFSubject,
predicate: TFPredicate,
object: TFObject
) => Statement
}

/** Full RDFLib.js Data Factory */
const DataFactory = {
const RDFlibDataFactory: IRDFlibDataFactory = {
...ExtendedTermFactory,
fetcher,
graph,
lit,
st,
triple,
}
export default DataFactory

export default RDFlibDataFactory

function id (term) {
if (!term) {
Expand Down Expand Up @@ -91,12 +111,11 @@ function lit(val: string, lang?: string, dt?: TFNamedNode): Literal {
* @param graph The containing graph
*/
function st(
subject: TFSubject,
predicate: TFPredicate,
object: TFObject,
graph?: TFGraph
): Statement;
function st (subject, predicate, object, graph) {
subject: SubjectType,
predicate: PredicateType,
object: ObjectType,
graph?: GraphType
): Statement {
return new Statement(subject, predicate, object, graph)
}
/**
Expand Down
2 changes: 1 addition & 1 deletion src/default-graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { TFDefaultGraph, TermType, DefaultGraphTermType } from './types';
* The RDF default graph
*/
export default class DefaultGraph extends Node implements TFDefaultGraph {
value: ''
value: string
termType: DefaultGraphTermType;

constructor () {
Expand Down
16 changes: 9 additions & 7 deletions src/formula.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,15 @@ import {
} from './types'
import Variable from './variable'
import Literal from './literal'
import { IdentityFactory, Indexable } from './data-factory-type'
import IndexedFormula from './store'

export function isFormula<T>(value: T | TFTerm): value is Formula {
return (value as Node).termType === TermType.Graph
}

interface FormulaOpts {
rdfFactory?: TFDataFactory
rdfFactory?: IdentityFactory
}

/**
Expand Down Expand Up @@ -61,7 +63,7 @@ export default class Formula extends Node {
optional: ReadonlyArray<any>

/** The factory used to generate statements and terms */
rdfFactory: TFDataFactory
rdfFactory: IdentityFactory

/**
* Initializes this formula
Expand Down Expand Up @@ -113,7 +115,7 @@ export default class Formula extends Node {
predicate: TFPredicate,
object: TFObject,
graph?: TFGraph
) {
): number {
return (this.statements as Statement[])
.push(this.rdfFactory.quad(subject, predicate, object, graph))
}
Expand Down Expand Up @@ -234,7 +236,7 @@ export default class Formula extends Node {
*
* Falls back to the rdflib hashString implementation if the given factory doesn't support id.
*/
id (term: TFTerm) {
id (term: TFTerm): Indexable {
return this.rdfFactory.id(term)
}

Expand Down Expand Up @@ -718,7 +720,7 @@ fromNT(str: string): TFTerm {
* @param context - The store
* @return The term for the statement
*/
list(values: Iterable<ValueType>, context): Collection | BlankNode {
list(values: [], context: IndexedFormula): Collection | TFBlankNode {
if (context.rdfFactory.supports["COLLECTIONS"]) {
const collection = context.rdfFactory.collection()
values.forEach(function (val) {
Expand Down Expand Up @@ -802,7 +804,7 @@ fromNT(str: string): TFTerm {

/**
* Gets a new formula with the substituting bindings applied
* @param bindings The bindings to substitute
* @param bindings - The bindings to substitute
*/
substitute(bindings: Bindings): Formula {
var statementsCopy = this.statements.map(function (ea) {
Expand Down Expand Up @@ -940,7 +942,7 @@ fromNT(str: string): TFTerm {
}

/**
* Serializes this formulat to a string
* Serializes this formula to a string
*/
toString(): string {
return '{' + this.statements.join('\n') + '}'
Expand Down
9 changes: 5 additions & 4 deletions src/literal.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import NamedNode, { isNamedNode } from './named-node'
import NamedNode from './named-node'
import Node from './node-internal'
import XSD from './xsd-internal'
import { ValueType, TFTerm, LiteralTermType, TFLiteral, TFNamedNode, TermType } from './types'
import classOrder from './class-order'
import { isTFTerm } from './utils'
import { isTFTerm, isNamedNode } from './utils'

export function isTFLiteral<T>(value: T | TFTerm): value is TFLiteral {
return (value as TFTerm).termType === TermType.Literal
Expand Down Expand Up @@ -88,7 +88,8 @@ export default class Literal extends Node implements TFLiteral {
toNT() {
return Literal.toNT(this)
}
static toNT (literal) {
/** Serializes a literal to an N-Triples string */
static toNT (literal: Literal): string {
if (typeof literal.value === 'number') {
return '' + literal.value
} else if (typeof literal.value !== 'string') {
Expand Down Expand Up @@ -161,7 +162,7 @@ export default class Literal extends Node implements TFLiteral {
* Builds a literal node from an input value
* @param value The input value
*/
static fromValue(value: ValueType): TFTerm {
static fromValue(value: ValueType): Literal | TFTerm {
if (isTFTerm(value)) {
return value
}
Expand Down
2 changes: 1 addition & 1 deletion src/named-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export default class NamedNode extends Node implements TFNamedNode {
termType: NamedNodeTermType;

/**
* Initializes this node
* Create a named (IRI) RDF Node
* @param iri The IRI for this node
*/
constructor (iri: NamedNode | string) {
Expand Down
4 changes: 2 additions & 2 deletions src/node-internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,9 @@ export default abstract class Node {
* Gets the substituted node for this one, according to the specified bindings
* @param bindings Bindings of identifiers to nodes
*/
substitute (bindings: Bindings): TFTerm {
substitute <T extends Node = Node>(bindings: Bindings): T {
console.log('@@@ node substitute' + this)
return this
return this as unknown as T
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import Collection from './collection'
import Literal from './literal'
import { ValueType, TFTerm } from './types'
import Namespace from './namespace'
import { isCollection } from './collection';
import { isTFLiteral } from './literal';
import { isCollection } from './utils'

export default Node

Expand Down
Loading

0 comments on commit 428cc0a

Please sign in to comment.