read-gedcom
Version:
Gedcom file reader
300 lines • 12 kB
JavaScript
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var _SelectionAny_instances, _SelectionAny_selfConstructor;
import { enumerable } from '../meta';
import { nodeToString } from '../tree';
import { SelectionGedcom } from './internal';
/**
* A selection of Gedcom nodes, represented in an array-like datastructure.
*/
export class SelectionAny {
/**
* @param rootNode
* @param nodes
* @category Base
*/
constructor(rootNode, nodes) {
_SelectionAny_instances.add(this);
this.rootNode = rootNode;
this.length = nodes.length;
for (let i = 0; i < nodes.length; i++) {
this[i] = nodes[i];
}
}
/**
* Returns an array of {@link TreeNode.tag}.
* @category Base
*/
tag() {
const array = [];
for (let i = 0; i < this.length; i++) {
array.push(this[i].tag);
}
return array;
}
/**
* Returns an array of {@link TreeNode.pointer}.
* @category Base
*/
pointer() {
const array = [];
for (let i = 0; i < this.length; i++) {
array.push(this[i].pointer);
}
return array;
}
/**
* Returns an array of {@link TreeNode.value}.
* @category Base
*/
value() {
const array = [];
for (let i = 0; i < this.length; i++) {
array.push(this[i].value);
}
return array;
}
/**
* Calls {@link value} and filters out <code>null</code> values.
* @category Base
*/
valueNonNull() {
return this.value().filter((v) => v !== null);
}
/**
* Wraps the value of {@link rootNode} in {@link SelectionGedcom}.
* The selection will contain exactly one node.
* @category Base
*/
root() {
return new SelectionGedcom(this.rootNode, [this.rootNode]);
}
// Implementation
get(tag, pointer, adapter) {
const Adapter = adapter != null ? adapter : SelectionAny; // Type safety of this cast is enforced by the signature of the visible methods
const tags = tag != null ? (Array.isArray(tag) ? tag : [tag]) : null;
const pointers = pointer != null ? (Array.isArray(pointer) ? pointer : [pointer]) : null;
const selection = [];
for (let i = 0; i < this.length; i++) {
const node = this[i];
if (tags === null && pointers === null) { // No need of index
node.children.forEach(child => selection.push(child));
}
else if (node._index !== undefined) { // Use index
const index = node._index;
const intermediary = [];
const requiresSorting = (tags !== null && tags.length > 0) || (pointers !== null && pointers.length > 0);
if (pointers !== null) {
const rootIndex = index;
if (rootIndex.byTagPointer !== undefined) { // If the cast is unsafe then the selection should be empty
if (tags !== null) {
tags.forEach(t => pointers.forEach(p => {
const childIdx = rootIndex.byTagPointer[t][p];
if (childIdx !== undefined) {
intermediary.push(node.children[childIdx]);
}
}));
}
else {
Object.values(rootIndex.byTagPointer).forEach(nodes => pointers.forEach(p => {
const childIdx = nodes[p];
if (childIdx !== undefined) {
intermediary.push(node.children[childIdx]);
}
}));
}
}
}
else {
const tagsNonNull = tags; // Safe cast
tagsNonNull.forEach(t => {
const nodesIdx = index.byTag[t];
if (nodesIdx !== undefined) {
nodesIdx.forEach(childIdx => intermediary.push(node.children[childIdx]));
}
});
}
if (requiresSorting) {
intermediary.sort((a, b) => a.indexSource - b.indexSource);
}
intermediary.forEach(child => selection.push(child));
}
else { // No index fallback
node.children.filter(child => (tags === null || (child.tag !== null && tags.includes(child.tag))) &&
(pointers === null || (child.pointer !== null && pointers.includes(child.pointer)))).forEach(child => selection.push(child));
}
}
return new Adapter(this.rootNode, selection);
}
/**
* Filter nodes from the selection based on a predicate.
* @param f The filtering predicate
* @category Base
*/
filter(f) {
const nodes = [];
for (let i = 0; i < this.length; i++) {
const node = this[i];
if (f(node)) {
nodes.push(node);
}
}
const Constructor = __classPrivateFieldGet(this, _SelectionAny_instances, "m", _SelectionAny_selfConstructor).call(this);
return new Constructor(this.rootNode, nodes);
}
/**
* Filter lifted nodes from the selection based on a predicate.
* The argument is a selection of one node.
* @param f The filtering predicate
* @category Base
*/
filterSelect(f) {
const Constructor = __classPrivateFieldGet(this, _SelectionAny_instances, "m", _SelectionAny_selfConstructor).call(this);
const nodes = [];
for (let i = 0; i < this.length; i++) {
const node = this[i];
if (f(new Constructor(this.rootNode, [node]))) {
nodes.push(node);
}
}
return new Constructor(this.rootNode, nodes);
}
/**
* View this selection as a different type. This method can be used to extend functionality for non-standard Gedcom files.
* @param Adapter The class adapter
* @category Base
*/
as(Adapter) {
return new Adapter(this.rootNode, this);
}
/**
* Export the selection as an array of nodes.
* The inverse operation is {@link of}.
* @category Base
*/
array() {
const array = [];
for (let i = 0; i < this.length; i++) {
array.push(this[i]);
}
return array;
}
/**
* Exports the selection as an array of selections of one element.
* @category Base
*/
arraySelect() {
const Constructor = __classPrivateFieldGet(this, _SelectionAny_instances, "m", _SelectionAny_selfConstructor).call(this);
const array = [];
for (let i = 0; i < this.length; i++) {
array.push(new Constructor(this.rootNode, [this[i]]));
}
return array;
}
/**
* Returns a concatenation of two selections.
* The right hand side selection should be a subtype of the left hand side's.
* The resulting selection will be the same type as the left hand side's.
* @param other The right hand side selection
* @category Base
*/
concatenate(other) {
const Constructor = __classPrivateFieldGet(this, _SelectionAny_instances, "m", _SelectionAny_selfConstructor).call(this);
const nodes = [];
for (let i = 0; i < this.length; i++) {
nodes.push(this[i]);
}
for (let i = 0; i < other.length; i++) {
nodes.push(other[i]);
}
return new Constructor(this.rootNode, nodes);
}
/**
* Returns a concatenation of two selections.
* The right hand side selection should be a subtype of the left hand side's.
* The resulting selection will be the same type as the left hand side's, with the elements of the right hand side's first.
* @param other The right hand side selection
* @category Base
*/
concatenateLeft(other) {
const Constructor = __classPrivateFieldGet(this, _SelectionAny_instances, "m", _SelectionAny_selfConstructor).call(this);
const nodes = [];
for (let i = 0; i < other.length; i++) {
nodes.push(other[i]);
}
for (let i = 0; i < this.length; i++) {
nodes.push(this[i]);
}
return new Constructor(this.rootNode, nodes);
}
/**
* Returns a sorted selection, with respect to the comparator.
* The default comparator relies on the {@link TreeNode.indexSource} attribute.
* @param comparator The comparator
* @category Base
*/
sorted(comparator = (a, b) => a.indexSource - b.indexSource) {
const Constructor = __classPrivateFieldGet(this, _SelectionAny_instances, "m", _SelectionAny_selfConstructor).call(this);
const nodes = [];
for (let i = 0; i < this.length; i++) {
nodes.push(this[i]);
}
nodes.sort(comparator);
return new Constructor(this.rootNode, nodes);
}
/**
* Checks whether two selections are equal.
* Note that the strategy used here is reference equality, hence for this method to return <code>true</code>, the nodes must be the same references (and in the same order).
* @param other The selection to compare it against
* @category Base
*/
equals(other) {
if (this.length !== other.length) {
return false;
}
for (let i = 0; i < this.length; i++) {
if (this[i] !== other[i]) {
return false;
}
}
return true;
}
/**
* Returns a string representation for this selection.
* @category Base
*/
toString() {
return this.length > 0 ? this.array().map(nodeToString).join('\n\n') : '(empty selection)';
}
// Implementation
static of(previous, nodes, Adapter) {
const AdapterClass = Adapter != null ? Adapter : SelectionAny;
const nodesArray = Array.isArray(nodes) ? nodes : [nodes];
return new AdapterClass(previous.rootNode, nodesArray);
}
}
_SelectionAny_instances = new WeakSet(), _SelectionAny_selfConstructor = function _SelectionAny_selfConstructor() {
if (Object.getPrototypeOf !== undefined) {
return Object.getPrototypeOf(this).constructor;
}
else {
return this.__proto__.constructor; // eslint-disable-line no-proto
}
};
__decorate([
enumerable(false),
__metadata("design:type", Object)
], SelectionAny.prototype, "rootNode", void 0);
//# sourceMappingURL=SelectionAny.js.map