lib_docs/SBOLDocument.js
/*
* Copyright (C) 2015 ICOS Group, Newcastle University. All rights reserved.
* Contact: James Alastair McLaughlin <j.a.mclaughlin@ncl.ac.uk>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
"use strict";
import fs from 'fs'
import URI from 'urijs'
import ModuleDefinition from './ModuleDefinition'
import Module from './Module'
import MapsTo from './MapsTo'
import Collection from './Collection'
import Model from './Model'
import ComponentDefinition from './ComponentDefinition'
import Sequence from './Sequence'
import FunctionalComponent from './FunctionalComponent'
import Interaction from './Interaction'
import Participation from './Participation'
import Range from './Range'
import SequenceAnnotation from './SequenceAnnotation'
import Component from './Component'
import SequenceConstraint from './SequenceConstraint'
/**
* Class to represent an SBOL2 document in memory.
*/
export default class SBOLDocument
{
constructor() {
this._collections = [];
this._moduleDefinitions = [];
this._modules = [];
this._mappings = [];
this._models = [];
this._componentDefinitions = [];
this._sequences = [];
this._functionalComponents = [];
this._interactions = [];
this._participations = [];
this._ranges = [];
this._sequenceAnnotations = [];
this._sequenceConstraints = [];
this._components = [];
this._URIs = {};
this._unresolvedURIs = {};
}
/**
* Create a new Collection in the document.
* @param {string} [uri] - The URI of the Collection
* @returns {Collection}
*/
collection(uri) {
var collection = new Collection(this, uri);
this._collections.push(collection);
return collection;
}
/**
* Create a new Model in the document.
* @param {string} [uri] - The URI of the Model
* @returns {Model}
*/
model(uri) {
var model = new Model(this, uri);
this._models.push(model);
return model;
}
/**
* Create a new ModuleDefinition in the document.
* @param {string} [uri] - The URI of the ModuleDefinition
* @returns {ModuleDefinition}
*/
moduleDefinition(uri) {
var moduleDefinition = new ModuleDefinition(this, uri);
this._moduleDefinitions.push(moduleDefinition);
return moduleDefinition;
}
/**
* Create a new Module in the document.
* @param {string} [uri] - The URI of the Module
* @returns {Module}
*/
module(uri) {
var module = new Module(this, uri);
this._modules.push(module);
return module;
}
/**
* Create a new Mapping in the document.
* @param {string} [uri] - The URI of the Mapping
* @returns {Mapping}
*/
mapping(uri) {
var mapping = new MapsTo(this, uri);
this._mappings.push(mapping);
return mapping;
}
/**
* Create a new ComponentDefinition in the document.
* @param {string} [uri] - The URI of the ComponentDefinition
* @returns {ComponentDefinition}
*/
componentDefinition(uri) {
var componentDefinition = new ComponentDefinition(this, uri);
this._componentDefinitions.push(componentDefinition);
return componentDefinition;
}
/**
* Create a new Sequence in the document.
* @param {string} [uri] - The URI of the Sequence
* @returns {Sequence}
*/
sequence(uri) {
var sequence = new Sequence(this, uri);
this._sequences.push(sequence);
return sequence;
}
/**
* Create a new FunctionalComponent in the document.
* @param {string} [uri] - The URI of the FunctionalComponent
* @returns {FunctionalComponent}
*/
functionalComponent(uri) {
var functionalComponent = new FunctionalComponent(this, uri);
this._functionalComponents.push(functionalComponent);
return functionalComponent;
}
/**
* Create a new Interaction in the document.
* @param {string} [uri] - The URI of the Interaction
* @returns {Interaction}
*/
interaction(uri) {
var interaction = new Interaction(this, uri);
this._interactions.push(interaction);
return interaction;
}
/**
* Create a new Participation in the document.
* @param {string} [uri] - The URI of the Participation
* @returns {Participation}
*/
participation(uri) {
var participation = new Participation(this, uri);
this._participations.push(participation);
return participation;
}
/**
* Create a new Range in the document.
* @param {string} [uri] - The URI of the Range
* @returns {Range}
*/
range(uri) {
var range = new Range(this, uri);
this._ranges.push(range);
return range;
}
/**
* Create a new Cut in the document.
* @param {string} [uri] - The URI of the Cut
* @returns {Cut}
*/
cut(uri) {
var cut = new Cut(this, uri);
this._cuts.push(cut);
return cut;
}
/**
* Create a new GenericLocation in the document.
* @param {string} [uri] - The URI of the GenericLocation
* @returns {GenericLocation}
*/
genericLocation(uri) {
var genericLocation = new GenericLocation(this, uri);
this._genericLocations.push(genericLocation);
return genericLocation;
}
/**
* Create a new SequenceAnnotation in the document.
* @param {string} [uri] - The URI of the SequenceAnnotation
* @returns {SequenceAnnotation}
*/
sequenceAnnotation(uri) {
var sequenceAnnotation = new SequenceAnnotation(this, uri);
this._sequenceAnnotations.push(sequenceAnnotation);
return sequenceAnnotation;
}
/**
* Create a new Component in the document.
* @param {string} [uri] - The URI of the Component
* @returns {Component}
*/
component(uri) {
var component = new Component(this, uri);
this._components.push(component);
return component;
}
/**
* Create a new SequenceConstraint in the document.
* @param {string} [uri] - The URI of the SequenceConstraint
* @returns {SequenceConstraint}
*/
sequenceConstraint(uri) {
var sequenceConstraint = new SequenceConstraint(this, uri);
this._sequenceConstraints.push(sequenceConstraint);
return sequenceConstraint;
}
/**
* Attempt to resolve any unresolved URIs in the document, replacing them
* with their instantiated object.
*/
link() {
this.moduleDefinitions.forEach(function(moduleDefinition) {
moduleDefinition.link();
});
this.modules.forEach(function(module) {
module.link();
});
this.mappings.forEach(function(mapping) {
mapping.link();
});
this.sequenceAnnotations.forEach(function(sequenceAnnotation) {
sequenceAnnotation.link();
});
this.componentDefinitions.forEach(function(componentDefinition) {
componentDefinition.link();
});
this.components.forEach(function(component) {
component.link();
});
this.interactions.forEach(function(interaction) {
interaction.link();
});
this.participations.forEach(function(participation) {
participation.link();
});
}
/**
* Returns a list of all ModuleDefinitions in the document
* @returns {ModuleDefinition[]}
*/
get moduleDefinitions() {
return this._moduleDefinitions.slice(0);
}
/**
* Returns a list of all Modules in the document
* @returns {Module[]}
*/
get modules() {
return this._modules.slice(0);
}
/**
* Returns a list of all Mappings in the document
* @returns {Mapping[]}
*/
get mappings() {
return this._mappings.slice(0);
}
/**
* Returns a list of all ComponentDefinitions in the document
* @returns {ComponentDefinition[]}
*/
get componentDefinitions() {
return this._componentDefinitions.slice(0);
}
/**
* Returns a list of all SequenceAnnotations in the document
* @returns {SequenceAnnotation[]}
*/
get sequenceAnnotations() {
return this._sequenceAnnotations.slice(0);
}
/**
* Returns a list of all Interactions in the document
* @returns {Interaction[]}
*/
get interactions() {
return this._interactions.slice(0);
}
/**
* Returns a list of all Participations in the document
* @returns {Participation[]}
*/
get participations() {
return this._participations.slice(0);
}
/**
* Returns a list of all Sequences in the document
* @returns {Sequence[]}
*/
get sequences() {
return this._sequences.slice(0);
}
/**
* Returns a list of all SequenceConstraints in the document
* @returns {SequenceConstraint[]}
*/
get sequenceConstraints() {
return this._sequenceConstraints.slice(0);
}
/**
* Returns a list of all Models in the document
* @returns {Model[]}
*/
get models() {
return this._models.slice(0);
}
/**
* Returns a list of all Components in the document
* @returns {Component[]}
*/
get components() {
return this._components.slice(0);
}
/**
* Map a URI to an object, such that lookupURI will return the object and
* the URI will be removed from unresolvedURIs if present.
*
* @param {string|URI} uri - The URI to map
* @param {*} object - The object to map to the URI
*/
mapURI(uri, object) {
if(uri instanceof URI)
uri = uri.toString();
if(this._URIs[uri] !== undefined) {
console.warn('Replacing URI: ' + uri);
}
this._URIs[uri] = object;
}
/**
* The reverse of mapURI. Unmap a URI from an object, such that lookupURI
* will no longer return the object.
*
* @param {string|URI} uri - The URI to unmap
* @param {*} object - The object to unmap from the URI
*/
unmapURI(uri, object) {
if(this._URIs[uri] === object)
delete this._URIs[uri];
}
/**
* Return the corresponding object for a URI, if resolved. If the URI
* has not been resolved, it will be returned unmodified.
*
* @param {string|URI} uri
* @returns {Array}
*/
lookupURI(uri) {
if(typeof(uri) === 'string')
uri = URI(uri);
if(uri instanceof URI) {
uri = uri.toString();
var object = this._URIs[uri];
if(object === undefined) {
console.warn('Unresolved URI: ' + uri);
this._unresolvedURIs[uri] = true;
} else {
delete this._unresolvedURIs[uri]
}
return object;
}
return uri;
}
/**
* Map a list of URIs to their corresponding objects, if resolved.
* Unresolved URIs will be returned unmodified.
* @param {URI[]} uris
* @returns {Array}
*/
lookupURIs(uris) {
return uris.map((uri) => {
if(uri instanceof URI) {
return this.lookupURI(uri);
} else {
return uri;
}
});
}
/**
* Returns a list of all unresolved URIs in the document.
* @returns {string[]}
*/
get unresolvedURIs() {
return Object.keys(this._unresolvedURIs)
}
toDisplayList() {
var displayList = {
segments: []
};
this.componentDefinitions.forEach(function(componentDefinition) {
var segment = {
sequence: []
};
displayList.segments.push(segment);
componentDefinition.sequenceAnnotations.forEach(function(sequenceAnnotation) {
var component = sequenceAnnotation.getComponent();
segment.sequence.push({
name: component.getName()
});
});
});
return displayList;
}
/**
* Load a new SBOL document from an RDF/XML string.
*
* @param {string} rdf - A string containing valid RDF/XML
* @param {function(err: Error, sbol: SBOLDocument)} callback
*/
static loadRDF(rdf, callback) {
var sbolDocument = new SBOLDocument();
sbolDocument.loadRDF(rdf, (err) => {
if(err)
callback(err);
else
callback(null, sbolDocument);
})
}
/**
* Load a new SBOL document from an RDF/XML file.
*
* @param {string} filename - The name of the file
* @param {function(err: Error, sbol: SBOLDocument)} callback
*/
static loadRDFFile(filename, callback) {
fs.readFile(filename, function(err, file) {
SBOLDocument.loadRDF(file + '', callback);
})
}
/**
* Load RDF/XML into the document. The existing contents of the document
* will be preserved.
*
* @param {string} rdf - A string containing valid RDF/XML
* @param {function(err: Error)} callback
*/
loadRDF(rdf, callback) {
var load = require('./load/rdf/loadRDF');
load(this, rdf, callback);
}
/**
* Serialize the document to SBOL RDF/XML
* @returns {string}
*/
serializeXML() {
var args = Array.prototype.slice.call(arguments, 0);
return require('./serialize/xml/serializeXML').apply(this, [this].concat(args));
}
/**
* Serialize the document to JSON
* @returns {string}
*/
serializeJSON() {
var args = Array.prototype.slice.call(arguments, 0);
return require('./serialize/json/serializeJSON').apply(this, [this].concat(args));
}
}
SBOLDocument.terms = {
promoter: 'http://identifiers.org/so/SO:0000167',
operator: 'http://identifiers.org/so/SO:0000057',
cds: 'http://identifiers.org/so/SO:0000316',
fivePrimeUtr: 'http://identifiers.org/so/SO:0000204',
terminator: 'http://identifiers.org/so/SO:0000141',
insulator: 'http://identifiers.org/so/SO:0000627',
originOfReplication: 'http://identifiers.org/so/SO:0000296',
primerBindingSite: 'http://identifiers.org/so/SO:0005850',
ribosomeBindingSite: 'http://identifiers.org/so/SO:0000139',
gene: 'http://identifiers.org/so/SO:0000704',
rna: 'http://identifiers.org/so/SO:0000234',
restrictionSite: 'http://identifiers.org/so/SO:0001687',
bluntRestrictionSite: 'http://identifiers.org/so/SO:0001691',
assemblyScar: 'http://identifiers.org/so/SO:0001953',
engineeredGene: 'http://identifiers.org/so/SO:0000280',
engineeredRegion: 'http://identifiers.org/so/SO:0000804',
conservedRegion: 'http://identifiers.org/so/SO:0000330',
dnaRegion: 'http://www.biopax.org/release/biopax-level3.owl#DnaRegion',
rnaRegion: 'http://www.biopax.org/release/biopax-level3.owl#RnaRegion',
protein: 'http://www.biopax.org/release/biopax-level3.owl#Protein',
smallMolecule: 'http://www.biopax.org/release/biopax-level3.owl#SmallMolecule',
effector: 'http://identifiers.org/chebi/CHEBI:35224',
dnaSequence: 'http://www.chem.qmul.ac.uk/iubmb/misc/naseq.html',
rnaSequence: 'http://www.chem.qmul.ac.uk/iubmb/misc/naseq.html',
proteinSequence: 'http://www.chem.qmul.ac.uk/iupac/AminoAcid/'
}
module.exports = SBOLDocument;