Skip to content

Commit

Permalink
Add initial factory implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
rubensworks committed Aug 28, 2020
1 parent 508e36e commit 5a5eabc
Show file tree
Hide file tree
Showing 13 changed files with 789 additions and 1 deletion.
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ module.exports = {
},
rules: {
// Default
'no-implicit-coercion': 'off',
'class-methods-use-this': 'off', // Conflicts with functions from interfaces that sometimes don't require `this`
'comma-dangle': ['error', 'always-multiline'],
'dot-location': ['error', 'property'],
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ yarn-error.log
**/index.d.ts
coverage
documentation
.eslintcache
8 changes: 7 additions & 1 deletion index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
// TODO
export * from './lib/BlankNode';
export * from './lib/DataFactory';
export * from './lib/DefaultGraph';
export * from './lib/Literal';
export * from './lib/NamedNode';
export * from './lib/Quad';
export * from './lib/Variable';
17 changes: 17 additions & 0 deletions lib/BlankNode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import * as RDF from 'rdf-js';

/**
* Contains an RDF blank node.
*/
export class BlankNode implements RDF.BlankNode {
public readonly termType = 'BlankNode';
public readonly value: string;

public constructor(value: string) {
this.value = value;
}

public equals(other: RDF.Term | null | undefined): boolean {
return !!other && other.termType === 'BlankNode' && other.value === this.value;
}
}
139 changes: 139 additions & 0 deletions lib/DataFactory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import * as RDF from 'rdf-js';
import { BlankNode } from './BlankNode';
import { DefaultGraph } from './DefaultGraph';
import { Literal } from './Literal';
import { NamedNode } from './NamedNode';
import { Quad } from './Quad';
import { Variable } from './Variable';

/**
* A factory for instantiating RDF terms and quads.
*/
export class DataFactory<Q extends RDF.BaseQuad = RDF.Quad> implements RDF.DataFactory<Q> {
private blankNodeCounter = 0;

/**
* @param value The IRI for the named node.
* @return A new instance of NamedNode.
* @see NamedNode
*/
public namedNode<Iri extends string = string>(value: Iri): NamedNode<Iri> {
return new NamedNode(value);
}

/**
* @param value The optional blank node identifier.
* @return A new instance of BlankNode.
* If the `value` parameter is undefined a new identifier
* for the blank node is generated for each call.
* @see BlankNode
*/
public blankNode(value?: string): BlankNode {
return new BlankNode(value || `df-${this.blankNodeCounter++}`);
}

/**
* @param value The literal value.
* @param languageOrDatatype The optional language or datatype.
* If `languageOrDatatype` is a NamedNode,
* then it is used for the value of `NamedNode.datatype`.
* Otherwise `languageOrDatatype` is used for the value
* of `NamedNode.language`.
* @return A new instance of Literal.
* @see Literal
*/
public literal(value: string, languageOrDatatype?: string | RDF.NamedNode): Literal {
return new Literal(value, languageOrDatatype);
}

/**
* This method is optional.
* @param value The variable name
* @return A new instance of Variable.
* @see Variable
*/
public variable(value: string): Variable {
return new Variable(value);
}

/**
* @return An instance of DefaultGraph.
*/
public defaultGraph(): DefaultGraph {
return DefaultGraph.INSTANCE;
}

/**
* @param subject The quad subject term.
* @param predicate The quad predicate term.
* @param object The quad object term.
* @param graph The quad graph term.
* @return A new instance of Quad.
* @see Quad
*/
public quad(
subject: Q['subject'],
predicate: Q['predicate'],
object: Q['object'],
graph?: Q['graph'],
): Q & Quad {
return <Q> new Quad(subject, predicate, object, graph || this.defaultGraph());
}

/**
* Create a deep copy of the given term using this data factory.
* @param original An RDF term.
* @return A deep copy of the given term.
*/
public fromTerm<T extends RDF.Term>(original: T):
(T extends RDF.NamedNode ? NamedNode
: (T extends RDF.BlankNode ? BlankNode
: (T extends RDF.Literal ? Literal
: (T extends RDF.Variable ? Variable
: (T extends RDF.DefaultGraph ? DefaultGraph
: (T extends Q ? Q : unknown)))))) {
// TODO: remove nasty any casts when this TS bug has been fixed:
// https://github.com/microsoft/TypeScript/issues/26933
switch (original.termType) {
case 'NamedNode':
return <any> this.namedNode(original.value);
case 'BlankNode':
return <any> this.blankNode(original.value);
case 'Literal':
if ((<RDF.Literal> original).language) {
return <any> this.literal(original.value, (<RDF.Literal>original).language);
}
if (!(<RDF.Literal> original).datatype.equals(Literal.XSD_STRING)) {
return <any> this.literal(original.value, this.fromTerm((<RDF.Literal> original).datatype));
}
return <any> this.literal(original.value);
case 'Variable':
return <any> this.variable(original.value);
case 'DefaultGraph':
return <any> this.defaultGraph();
case 'Quad':
return <any> this.quad(
<Q['subject']> this.fromTerm((<Q> <unknown> original).subject),
<Q['predicate']> this.fromTerm((<Q> <unknown> original).predicate),
<Q['object']> this.fromTerm((<Q> <unknown> original).object),
<Q['graph']> this.fromTerm((<Q> <unknown> original).graph),
);
}
}

/**
* Create a deep copy of the given quad using this data factory.
* @param original An RDF quad.
* @return A deep copy of the given quad.
*/
public fromQuad(original: Q): Q {
return <Q> this.fromTerm(original);
}

/**
* Reset the internal blank node counter.
*/
public resetBlankNodeCounter() {
this.blankNodeCounter = 0;
}
}
20 changes: 20 additions & 0 deletions lib/DefaultGraph.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import * as RDF from 'rdf-js';

/**
* An instance of DefaultGraph represents the default graph.
* It's only allowed to assign a DefaultGraph to the .graph property of a Quad.
*/
export class DefaultGraph implements RDF.DefaultGraph {
public static INSTANCE = new DefaultGraph();

public readonly termType = 'DefaultGraph';
public readonly value = '';

private constructor() {
// Private constructor
}

public equals(other: RDF.Term | null | undefined): boolean {
return !!other && other.termType === 'DefaultGraph';
}
}
37 changes: 37 additions & 0 deletions lib/Literal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import * as RDF from 'rdf-js';
import { NamedNode } from './NamedNode';

/**
* An RDF literal, containing a string with an optional language tag and/or datatype.
*/
export class Literal implements RDF.Literal {
public readonly termType = 'Literal';
public readonly value: string;
public readonly language: string;
public readonly datatype: RDF.NamedNode;

public static readonly RDF_LANGUAGE_STRING: RDF.NamedNode =
new NamedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#langString');

public static readonly XSD_STRING: RDF.NamedNode =
new NamedNode('http://www.w3.org/2001/XMLSchema#string');

public constructor(value: string, languageOrDatatype?: string | RDF.NamedNode) {
this.value = value;
if (typeof languageOrDatatype === 'string') {
this.language = languageOrDatatype;
this.datatype = Literal.RDF_LANGUAGE_STRING;
} else if (languageOrDatatype) {
this.language = '';
this.datatype = languageOrDatatype;
} else {
this.language = '';
this.datatype = Literal.XSD_STRING;
}
}

public equals(other: RDF.Term | null | undefined): boolean {
return !!other && other.termType === 'Literal' && other.value === this.value &&
other.language === this.language && other.datatype.equals(this.datatype);
}
}
17 changes: 17 additions & 0 deletions lib/NamedNode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import * as RDF from 'rdf-js';

/**
* Contains an IRI.
*/
export class NamedNode<Iri extends string = string> implements RDF.NamedNode<Iri> {
public readonly termType = 'NamedNode';
public readonly value: Iri;

public constructor(value: Iri) {
this.value = value;
}

public equals(other: RDF.Term | null | undefined): boolean {
return !!other && other.termType === 'NamedNode' && other.value === this.value;
}
}
30 changes: 30 additions & 0 deletions lib/Quad.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import * as RDF from 'rdf-js';

/**
* An instance of DefaultGraph represents the default graph.
* It's only allowed to assign a DefaultGraph to the .graph property of a Quad.
*/
export class Quad implements RDF.BaseQuad {
public readonly termType = 'Quad';
public readonly value = '';
public readonly subject: RDF.Term;
public readonly predicate: RDF.Term;
public readonly object: RDF.Term;
public readonly graph: RDF.Term;

public constructor(
subject: RDF.Term,
predicate: RDF.Term,
object: RDF.Term,
graph: RDF.Term,
) {
this.subject = subject;
this.predicate = predicate;
this.object = object;
this.graph = graph;
}

public equals(other: RDF.Term | null | undefined): boolean {
return !!other && other.termType === 'Quad';
}
}
17 changes: 17 additions & 0 deletions lib/Variable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import * as RDF from 'rdf-js';

/**
* A variable name.
*/
export class Variable implements RDF.Variable {
public readonly termType = 'Variable';
public readonly value: string;

public constructor(value: string) {
this.value = value;
}

public equals(other: RDF.Term | null | undefined): boolean {
return !!other && other.termType === 'Variable' && other.value === this.value;
}
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"eslint-plugin-tsdoc": "^0.2.6",
"eslint-plugin-unused-imports": "^0.1.3",
"jest": "^26.0.0",
"jest-rdf": "^1.5.0",
"manual-git-changelog": "^1.0.0",
"pre-commit": "^1.2.2",
"ts-jest": "^26.0.0",
Expand Down

0 comments on commit 5a5eabc

Please sign in to comment.