UNPKG

kekule

Version:

Open source JavaScript toolkit for chemoinformatics

1,521 lines (1,481 loc) 44.3 kB
/** * @fileoverview * Abstract root classes for concrete strcuture reader/writers of different structure formats. * @author Partridge Jiang */ /* * requires /lan/classes.js * requires /core/kekule.common.js * requires /utils/kekule.utils.js * requires /xbrowsers/kekule.x.js */ (function(){ /* * Root object of I/O default options * @object */ Kekule.globalOptions.add('IO', {}); /** * Name space for IO package of Kekule. * @namespace */ Kekule.IO = {}; /** * Enumeration of possible type of input data. * @class */ Kekule.IO.ChemDataType = { /** Plain Text, e.g. MOL data. */ TEXT: 'text', /** DOM Structure, e.g. CML data */ DOM: 'dom', /** JSON Object */ JSON: 'json', /** Binary data */ BINARY: 'bin', /** Other, not used now */ OTHER: 'other', /** Unknown type, usually treat as binary. */ UNKNOWN: 'unknown', /** * Check if type is in binary format. * @param {Int} dataType * @returns {Bool} */ isBinaryType: function(dataType) { var T = Kekule.IO.ChemDataType; return (!dataType) || (dataType === T.BINARY) || (dataType === T.OTHER) || (dataType === T.UNKNOWN); } }; /** * Enumeration of common data format ids. * @class */ Kekule.IO.DataFormat = { }; /** * Enumeration of common MIME types. * @class */ Kekule.IO.MimeType = { TEXT: 'text/plain', HTML: 'text/html', XML: 'text/xml', JSON: 'application/json', JAVASCRIPT: 'application/javascript', GIF: 'image/gif', JPEG: 'image/jpeg', PNG: 'image/png', XPNG: 'image/x-png', OCTSTREAM: 'application/octet-stream' }; /** * Manager class of data formats. * Generally data format item is a hash containing the following fields: * { * id: String, unique ID of format. * dataType: type of data, text, binary or other. * fileExts: Array, possible file extension associated with this format. * mimeType: String, MIME type associated with this format. * } * @class */ Kekule.IO.DataFormatsManager = { /** @private */ _formats: {}, /** * Register a new data format. If the mimeType already exists, settings will be merged. * @param {String} id * @param {String} mimeType * @param {Array} fileExts * @param {String} title * @param {String} description * @param {Hash} additionalInfo */ register: function(id, mimeType, fileExts, dataType, title, description, additionalInfo) { //var result = Kekule.IO.DataFormatsManager.findFormat(mimeType); var result = null; // MDL 3000/2000 share the same mimeType, so can not use it to make unique /* if (result) console.log('merge', result.fileExts, result.fileExts.length, fileExts, mimeType); */ if (!result) result = FM._formats[id]; if (!result) { result = {'id': id}; FM._formats[id] = result; } result.mimeType = mimeType; var exts = DataType.isArrayValue(fileExts)? fileExts: [fileExts]; exts = exts.map(function(ext) { return ext? ext.toLowerCase(): ext; } ) if (!result.fileExts) result.fileExts = exts; else // merge Kekule.ArrayUtils.pushUnique(result.fileExts, exts); if (dataType && (dataType !== Kekule.IO.ChemDataType.UNKNOWN)) result.dataType = dataType; result.title = title; result.description = description; if (additionalInfo) result = Object.extend(result, additionalInfo); return result; }, /** * Unregister a data format. * @param {String} id */ unregister: function(id) { if (FM._formats[id]) delete FM._formats[id]; }, /** * Returns all registered */ getAllIds: function() { return Kekule.ObjUtils.getOwnedFieldNames(FM._formats); }, /** * Returns format detail information. If id not found, null will be returned. * @param {String} id * @returns {Hash} */ getFormatInfo: function(id) { return FM._formats[id] || null; }, /** * Returns MIME type of format id. * @param {String} id * @returns {String} */ getMimeType: function(id) { var info = FM.getFormatInfo(id); return info? info.mimeType: null; }, /** * Returns file extensions of format id. * @param {String} id * @returns {Array} */ getFileExts: function(id) { var info = FM.getFormatInfo(id); return info? info.fileExts: null; }, /** * Find format detail item by mimeType or fileExt. * @param {String} mimeType * @param {String} fileExt * @returns {Hash} */ findFormat: function(mimeType, fileExt) { if (!mimeType && !fileExt) return null; var ids = FM.getAllIds(); var matched = false; for (var i = 0, l = ids.length; i < l; ++i) { var info = FM.getFormatInfo(ids[i]); /* matched = (!mimeType || (mimeType === info.mimeType)) && (!fileExt || (info.fileExts.indexOf(fileExt) >= 0)); */ matched = (mimeType && (mimeType === info.mimeType)); if (!matched) matched = (fileExt && (info.fileExts.indexOf(fileExt.toLowerCase()) >= 0)); if (matched) return info; } return null; }, /** * Find format ID by mimeType or fileExt. * @param {String} mimeType * @param {String} fileExt * @returns {String} */ findFormatId: function(mimeType, fileExt) { var info = Kekule.IO.DataFormatsManager.findFormat(mimeType, fileExt); return info? info.id: null; } }; /** @ignore */ var FM = Kekule.IO.DataFormatsManager; /** @ignore */ Kekule.IO.dataFormatsManager = Kekule.IO.DataFormatsManager; /** * Abstract root class for all chemistry data readers. * @class * @augments ObjectEx * @param {Hash} options Some additional params passed to reader. */ Kekule.IO.ChemDataReader = Class.create(ObjectEx, /** @lends Kekule.IO.ChemDataReader# */ { /** @constructs */ initialize: function(/*$super, */options) { this.tryApplySuper('initialize') /* $super() */; }, /** @private */ CLASS_NAME: 'Kekule.IO.ChemDataReader', /* * Read from data, create related instance and encapsulation the instance inside a {@link Kekule.ChemDocument}. * @param {Variant} data * @param {String} dataType Type of data, value should fom {@link Kekule.IO.ChemDataType}. * @param {String} format Format ID. * @returns {Kekule.ChemDocument} Instance created by data. */ /* readDocument: function(data, dataType, format) { return this.doReadDocument(data, dataType, format); }, */ /* * Do actual work for {@link Kekule.IO.ChemDataReader#readDocument}. Descendants should override this method. * @param {Variant} data * @param {String} dataType Type of data, value should fom {@link Kekule.IO.ChemDataType}. * @param {String} format Format ID. * @returns {Kekule.ChemDocument} Instance created by data. */ /* doReadDocument: function(data, dataType, format) { var doc; var r = this.readData(data, dataType, format); if (r && r.setOwner) { doc = new Kekule.ChemDocument(); doc.setDocObj(r); } return doc; }, */ /** * Read from data and return a related instance. * @param {Variant} data * @param {String} dataType Type of data, value should fom {@link Kekule.ChemStructureDataType}. * @param {String} format Format ID. * @param {Hash} options Additional options to read data. Different reader may have different options. * @returns {Variant} Instance created by data. */ readData: function(data, dataType, format, options) { if (!dataType) // auto detect { var finfo = Kekule.IO.DataFormatsManager.getFormatInfo(format); dataType = finfo? finfo.dataType: null; if (!dataType) dataType = (typeof(data) === 'string')? Kekule.IO.ChemDataType.TEXT: (data.getElementsByTagName? Kekule.IO.ChemDataType.DOM: Kekule.IO.ChemDataType.BINARY ); } var result = this.doReadData(data, dataType, format, options || {}); if ((result instanceof Kekule.ChemObject) && (result.getSrcInfo)) { var info = result.getSrcInfo(); info.data = data; info.dataType = dataType; var formatDetail = Kekule.IO.DataFormatsManager.getFormatInfo(format); if (formatDetail) { info.format = format; info.mimeType = formatDetail.mimeType; info.possibleFileExts = formatDetail.fileExts; } //console.log('set src info', info); } return result; }, /** * Do actual work for {@link Kekule.IO.ChemDataReader#readData}. Descendants should override this method. * @param {Variant} data * @param {String} dataType Type of data, value should fom {@link Kekule.IO.ChemDataType}. * @param {String} format Format ID. * @param {Hash} options Additional options to read data. Different reader may have different options. * @returns {Variant} Instance created by data. */ doReadData: function(data, dataType, format, options) { // do nothing here } }); /** * Abstract root class for all chemistry data writers. * @class * @augments ObjectEx * @param {Hash} options Some additional params passed to reader. */ Kekule.IO.ChemDataWriter = Class.create(ObjectEx, /** @lends Kekule.IO.ChemDataWriter# */ { /** @private */ CLASS_NAME: 'Kekule.IO.ChemDataWriter', /** @constructs */ initialize: function(/*$super, */options) { this.tryApplySuper('initialize') /* $super() */; }, /* * Read document object of {@link Kekule.ChemDocument} and write it to proper data form. * @param {@link Kekule.ChemDocument} doc Document to write. * @param {String} dataType Type of data, value should fom {@link Kekule.IO.ChemDataType}. * @param {String} format Format ID. * @returns {Variant} Data output, the type of data is decided by dataType param. */ /* writeDocument: function(doc, dataType, format) { return this.doWriteDocument(doc, dataType, format); }, */ /* * Do actual work for {@link Kekule.IO.ChemDataReader#writeDocument}. Descendants should override this method. * @param {@link Kekule.ChemDocument} doc Document to write. * @param {String} dataType Type of data, value should fom {@link Kekule.IO.ChemDataType}. * @param {String} format Format ID. * @returns {Variant} Data output, the type of data is decided by dataType param. */ /* doWriteDocument: function(doc, dataType, format) { return this.writeData(doc.docObj, dataType, format); }, */ /** * Write Kekule object to data. * @param {Kekule.ChemObject} obj Object to write * @param {String} dataType Type of data, value should fom {@link Kekule.IO.ChemDataType}. * @param {String} format Format ID. * @param {Hash} options Additional options to write data. Different writer may have different options. * @returns {Variant} Data output, the type of data is decided by dataType param. */ writeData: function(obj, dataType, format, options) { //var dtype = dataType || Kekule.IO.ChemDataType.TEXT; if (!dataType) // auto detect { var finfo = Kekule.IO.DataFormatsManager.getFormatInfo(format); dataType = finfo? finfo.dataType: null; } return this.doWriteData(obj, dataType, format, options || {}); }, /** * Do actual work for {@link Kekule.IO.ChemDataReader#writeData}. Descendants should override this method. * @param {Kekule.ChemObject} obj Object to write * @param {String} dataType Type of data, value should fom {@link Kekule.IO.ChemDataType}. * @param {String} format Format ID. * @param {Hash} options Additional options to write data. Different writer may have different options. * @returns {Variant} Data output, the type of data is decided by dataType param. * @private */ doWriteData: function(obj, dataType, format, options) { // do nothing here } }); /** * A manager to create suitable data reader. * @class */ Kekule.IO.ChemDataReaderManager = { /** * Stores registered reader information. Each item including the following fields: * { * id: String, * readerClass: Class, * instances: Object // reader instance, null at first * additionalInfos... * } * @private */ _readers: [], /** * Register a data reader. * @param {String} id A UID string for reader. * @param {Class} readerClass Class of reader. * @param {Variant} formatId String or Array, associated format IDs. * @param {Hash} additionalInfo More information on this reader class. Can include the following fields: * { * {Hash} createOptions: hash passed into reader's constructor. * (String) title: Reader title, * {String} formatId: id of data format * //(String) mimeType * //(Variant) fileExt: if load from a file, the file ext. Can be a string or Array of string. * } */ register: function(id, readerClass, formatId, additionalInfo) { if (!id || !readerClass || !formatId || (DataType.isArrayValue(formatId) && !formatId.length)) // empty format return; if (Kekule.IO.ChemDataReaderManager.getReaderInfoById(id)) // id can not be duplicate { Kekule.raise(/*Kekule.ErrorMsg.READER_ID_ALREADY_EXISTS*/Kekule.$L('ErrorMsg.READER_ID_ALREADY_EXISTS')); return null; } var item = { 'id': id, 'readerClass': readerClass, 'formatId': DataType.isArrayValue(formatId)? formatId: [formatId] }; item = Object.extend(item, additionalInfo); Kekule.IO.ChemDataReaderManager._readers.push(item); return item; }, /** * Unregister a data reader. * @param {String} id A UID string for reader. */ unregister: function(id) { var index = -1; var readers = Kekule.IO.ChemDataReaderManager._readers; for (var i = 0, l = readers.length; i < l; ++i) { var currId = readers[i].id; if (currId === id) { index = i; break; } } if (index >= 0) { var reader = readers[index]; readers.splice(index, 1); return reader; } else return null; }, /** * Returns all file format IDs that has corresponding reader. * @returns {Array} Array of format id. */ getAllReadableFormatIds: function() { var result = []; var readers = Kekule.IO.ChemDataReaderManager._readers; for (var i = 0, l = readers.length; i < l; ++i) { var item = readers[i]; var fids = item.formatId; //result = result.concat(fids || []); Kekule.ArrayUtils.pushUnique(result, fids); } return result; }, /** * Returns all file formats that has corresponding reader. * @returns {Array} Array of format object. */ getAllReadableFormats: function() { var result = []; /* var readers = Kekule.IO.ChemDataReaderManager._readers; for (var i = 0, l = readers.length; i < l; ++i) { var item = readers[i]; var fids = item.formatId; for (var j = 0, k = fids.length; j < k; ++j) { var fid = fids[j]; var info = FM.getFormatInfo(fid); if (info) result.push(info); } } */ var ids = Kekule.IO.ChemDataReaderManager.getAllReadableFormatIds(); for (var i = 0, l = ids.length; i < l; ++i) { var info = FM.getFormatInfo(ids[i]); if (info) result.push(info); } return result; }, /** * Get all available reader infos by condition provided and be suitable for targetClass. * For example: Kekule.IO.ChemDataReaderManager.getAvailableReaderInfos({'id': 'mol'}); * @param {Hash} condition * @returns {Array} */ getAvailableReaderInfos: function(condition) { var result = []; var rs = Kekule.IO.ChemDataReaderManager._readers; for (var i = rs.length - 1; i >=0; --i) { if (Kekule.ObjUtils.match(rs[i], condition)) { result.push(rs[i]); } } return result; }, /** * Get reader information by condition provided. * For example: Kekule.IO.ChemDataWriterManager.getReaderInfo({'formatId': 'cml'}); * @param {Hash} condition * @returns {Hash} */ getReaderInfo: function(condition) { var rs = Kekule.IO.ChemDataReaderManager._readers; for (var i = rs.length - 1; i >=0; --i) { if (Kekule.ObjUtils.match(rs[i], condition)) return rs[i]; } return null; }, /** * Get reader information by Id. * @param {String} id * @returns {Hash} */ getReaderInfoById: function(id) { return Kekule.IO.ChemDataReaderManager.getReaderInfo({'id': id}); }, /** * Get reader information by data format id. * @param {String} formatId * @param {Hash} otherConditions * @returns {Hash} */ getReaderInfoByFormat: function(formatId, otherConditions) { var rs = Kekule.IO.ChemDataReaderManager._readers; for (var i = rs.length - 1; i >=0; --i) { if ((!otherConditions) || Kekule.ObjUtils.match(rs[i], otherConditions)) { if (rs[i].formatId.indexOf(formatId) >= 0) return rs[i]; } } return null; }, /** * Get reader information by file ext. * @param {String} fileExt * @param {Hash} otherConditions * @returns {Hash} */ getReaderInfoByFileExt: function(fileExt, otherConditions) { /* var rs = Kekule.IO.ChemDataReaderManager._readers; for (var i = rs.length - 1; i >=0; --i) { if ((!otherConditions) || Kekule.ObjUtils.match(rs[i], otherConditions)) { if (rs[i].fileExt == fileExt) return rs[i]; else if (rs[i].fileExt.indexOf && (rs[i].fileExt.indexOf(fileExt) >= 0)) return rs[i]; } } return null; */ var fid = Kekule.IO.DataFormatsManager.findFormatId(null, fileExt); return fid? Kekule.IO.ChemDataReaderManager.getReaderInfoByFormat(fid, otherConditions): null; }, /** @private */ createReaderOfInfo: function(info) { return new info.readerClass(info.createOptions); }, /** @private */ getReaderOfInfo: function(info) { if (!info.instance) info.instance = Kekule.IO.ChemDataReaderManager.createReaderOfInfo(info); return info.instance; }, /** * Create a new reader instance by condition. * @param {Hash} condition * @returns {Kekule.IO.ChemDataReader} */ createReader: function(condition) { var info = Kekule.IO.ChemDataReaderManager.getReaderInfo(condition); if (info) return Kekule.IO.ChemDataReaderManager.createReaderOfInfo(info); }, /** * Get a reusable reader instance by condition. * @param {Hash} condition * @returns {Kekule.IO.ChemDataReader} */ getReader: function(condition) { var info = Kekule.IO.ChemDataReaderManager.getReaderInfo(condition); if (info) return Kekule.IO.ChemDataReaderManager.getReaderOfInfo(info); }, /** * Create a new reader instance by id. * @param {String} id * @returns {Kekule.IO.ChemDataReader} */ createReaderById: function(id) { return Kekule.IO.ChemDataReaderManager.createReader({'id': id}); }, /** * Get a reusable reader instance by id. * @param {String} id * @returns {Kekule.IO.ChemDataReader} */ getReaderById: function(id) { return Kekule.IO.ChemDataReaderManager.getReader({'id': id}); }, /** * Create new reader instance by formatId. * @param {String} formatId * @param {Hash} otherConditions * @returns {Kekule.IO.ChemDataReader} */ createReaderByFormat: function(formatId, otherConditions) { var info = Kekule.IO.ChemDataReaderManager.getReaderInfoByFormat(formatId, otherConditions); return info? Kekule.IO.ChemDataReaderManager.createReaderOfInfo(info): null; }, /** * Get reader instance by format Id. * @param {String} fileExt * @param {Hash} otherConditions * @returns {Kekule.IO.ChemDataReader} */ getReaderByFormat: function(formatId, otherConditions) { var info = Kekule.IO.ChemDataReaderManager.getReaderInfoByFormat(formatId, otherConditions); return info? Kekule.IO.ChemDataReaderManager.getReaderOfInfo(info): null; }, /** * Create reader instance by file ext. * @param {String} fileExt * @param {Hash} otherConditions * @returns {Kekule.IO.ChemDataReader} */ createReaderByFileExt: function(fileExt, otherConditions) { var info = Kekule.IO.ChemDataReaderManager.getReaderInfoByFileExt(fileExt, otherConditions); return info? Kekule.IO.ChemDataReaderManager.createReaderOfInfo(info): null; }, /** * Get reader instance by file ext. * @param {String} fileExt * @param {Hash} otherConditions * @returns {Kekule.IO.ChemDataReader} */ getReaderByFileExt: function(fileExt, otherConditions) { var info = Kekule.IO.ChemDataReaderManager.getReaderInfoByFileExt(fileExt, otherConditions); return info? Kekule.IO.ChemDataReaderManager.getReaderOfInfo(info): null; }, /** * Create a new reader instance by MIME type. * @param {String} mimeType * @returns {Kekule.IO.ChemDataReader} */ createReaderByMimeType: function(mimeType) { //return Kekule.IO.ChemDataReaderManager.createReader({'mimeType': mimeType}); var fid = Kekule.IO.DataFormatsManager.findFormatId(mimeType, null); return fid? Kekule.IO.ChemDataReaderManager.createReaderByFormat(fid): null; }, /** * Get a reusable reader instance by MIME type. * @param {String} mimeType * @returns {Kekule.IO.ChemDataReader} */ getReaderByMimeType: function(mimeType) { //return Kekule.IO.ChemDataReaderManager.getReader({'mimeType': mimeType}); var fid = Kekule.IO.DataFormatsManager.findFormatId(mimeType, null); return fid? Kekule.IO.ChemDataReaderManager.getReaderByFormat(fid): null; } }; /** * A manager to create suitable data writer. * @class */ Kekule.IO.ChemDataWriterManager = { /** * Stores registered writer information. Each item including the following fields: * { * id: String, * srcClass: Class, * writerClass: Class, * instances: Object // reader instance, null at first * additionalInfos... * } * @private */ _writers: [], /** * Register a data writer. * @param {String} id A UID string for writer. * @param {Class} writerClass Class of writer. * @param {Array} srcClasses Instance of which classes can be written by this writer. * @param {Variant} formatId Data format, string or array. * @param {Hash} additionalInfo More information on this reader class. Can include the following fields: * { * {Hash} createOptions: hash passed into writer's constructor. * (String) title: Writer title, * (String) mimeType * (String) fileExt: if write to a file, the file ext. * } */ register: function(id, writerClass, srcClasses, formatId, additionalInfo) { if (!id || !writerClass || !formatId || (DataType.isArrayValue(formatId) && !formatId.length)) // empty format return; var oldItem = Kekule.IO.ChemDataWriterManager.getWriterInfoById(id); if (oldItem) // id can not be duplicate { if (writerClass === oldItem.writerClass) // same writer class, may want to expand the srcClasses? { Kekule.ArrayUtils.pushUnique(oldItem.srcClasses, srcClasses); Kekule.ArrayUtils.pushUnique(oldItem.formatId, formatId); return oldItem; } else { Kekule.raise(/*Kekule.ErrorMsg.WRITER_ID_ALREADY_EXISTS*/Kekule.$L('ErrorMsg.WRITER_ID_ALREADY_EXISTS')); return null; } } var item = { 'id': id, 'writerClass': writerClass, 'srcClasses': srcClasses, 'formatId': DataType.isArrayValue(formatId)? formatId: [formatId] }; item = Object.extend(item, additionalInfo); Kekule.IO.ChemDataWriterManager._writers.push(item); return item; }, /** * Unregister a data writer. * @param {String} id A UID string for reader. */ unregister: function(id) { var index = -1; var writers = Kekule.IO.ChemDataWriterManager._writers; for (var i = 0, l = writers.length; i < l; ++i) { var currId = writers[i].id; if (currId === id) { index = i; break; } } if (index >= 0) { var writer = writers[index]; writers.splice(index, 1); return writer; } else return null; }, /** * Returns all file format IDs that has corresponding writer. * @returns {Array} Array of format id. */ getAllWritableFormatIds: function() { var result = []; var writers = Kekule.IO.ChemDataWriterManager._writers; for (var i = 0, l = writers.length; i < l; ++i) { var item = writers[i]; var fids = item.formatId; //result = result.concat(fids || []); Kekule.ArrayUtils.pushUnique(result, fids); } return result; }, /** * Returns all file formats that has corresponding writer. * @returns {Array} Array of format object. */ getAllWritableFormats: function() { var result = []; var ids = Kekule.IO.ChemDataWriterManager.getAllWritableFormatIds(); for (var i = 0, l = ids.length; i < l; ++i) { var info = FM.getFormatInfo(ids[i]); if (info) result.push(info); } return result; }, /** * Get all available writer infos by condition provided and suitble for srcObj. * For example: Kekule.IO.ChemDataWriterManager.getWriterInfo({'id': 'mol'}); * @param {Hash} condition * @param {Variant} srcObjOrClass * @returns {Array} */ getAvailableWriterInfos: function(condition, srcObjOrClass) { var result = []; var srcClass = ClassEx.isClass(srcObjOrClass)? srcObjOrClass: srcObjOrClass.getClass? srcObjOrClass.getClass(): srcObjOrClass? null: // null, src set but no class found, special marked undefined; var ws = Kekule.IO.ChemDataWriterManager._writers; for (var i = ws.length - 1; i >=0; --i) { if (Kekule.ObjUtils.match(ws[i], condition)) { if (srcClass !== undefined) { if (srcClass) { for (var j = 0, k = ws[i].srcClasses.length; j < k; ++j) { if (ClassEx.isOrIsDescendantOf(srcClass, ws[i].srcClasses[j])) result.push(ws[i]); } } } else result.push(ws[i]); } } return result; }, /** * Get writer info by condition provided and suitble for srcObj. * For example: Kekule.IO.ChemDataWriterManager.getWriterInfo({'id': 'mol'}); * @param {Hash} condition * @param {Kekule.ChemObject} srcObj * @returns {Hash} */ getWriterInfo: function(condition, srcObj) { var ws = Kekule.IO.ChemDataWriterManager._writers; for (var i = ws.length - 1; i >=0; --i) { if (Kekule.ObjUtils.match(ws[i], condition)) { if (srcObj) { for (var j = 0, k = ws[i].srcClasses.length; j < k; ++j) { if (srcObj instanceof ws[i].srcClasses[j]) return ws[i]; } return null; } else return ws[i]; } } return null; }, /** * Get writer information by Id. * @param {String} id * @param {Kekule.ChemObject} srcObj * @returns {Hash} */ getWriterInfoById: function(id, srcObj) { return Kekule.IO.ChemDataWriterManager.getWriterInfo({'id': id}, srcObj); }, /** * Get writer information by data format id. * @param {String} formatId * @param {Hash} otherConditions * @returns {Hash} */ getWriterInfoByFormat: function(formatId, otherConditions, srcObj) { /* var condition = otherConditions || {}; condition.formatId = formatId; return Kekule.IO.ChemDataWriterManager.getWriterInfo(condition, srcObj); */ var ws = Kekule.IO.ChemDataWriterManager._writers; for (var i = ws.length - 1; i >=0; --i) { if ((!otherConditions) || Kekule.ObjUtils.match(ws[i], otherConditions)) { if (ws[i].formatId.indexOf(formatId) >= 0) { if (srcObj) { for (var j = 0, k = ws[i].srcClasses.length; j < k; ++j) { if (srcObj instanceof ws[i].srcClasses[j]) return ws[i]; } return null; } return ws[i]; } } } return null; }, /** * Get writer information by file ext. * @param {String} fileExt * @param {Hash} otherConditions * @param {Kekule.ChemObject} srcObj * @returns {Hash} */ getWriterInfoByFileExt: function(fileExt, otherConditions, srcObj) { /* var ws = Kekule.IO.ChemDataWriterManager._writers; var result; for (var i = ws.length - 1; i >=0; --i) { result = null; if ((!otherConditions) || Kekule.ObjUtils.match(ws[i], otherConditions)) { if (ws[i].fileExt == fileExt) result = ws[i]; else if (ws[i].fileExt.indexOf && (ws[i].fileExt.indexOf(fileExt) >= 0)) result = ws[i]; } if (result) // check srcObj { if (srcObj) { for (var j = 0, k = result.srcClasses; j < k; ++j) { if (srcObj instanceof result.srcClasses[j]) return result; } result = null; } else return result; } } return null; */ var fid = Kekule.IO.DataFormatsManager.findFormatId(null, fileExt); return fid? Kekule.IO.ChemDataWriterManager.getWriterInfoByFormat(fid, otherConditions, srcObj): null; }, /** @private */ createWriterOfInfo: function(info) { return new info.writerClass(info.createOptions); }, /** @private */ getWriterOfInfo: function(info) { if (!info.instance) info.instance = Kekule.IO.ChemDataWriterManager.createWriterOfInfo(info); return info.instance; }, /** * Create a new writer instance by condition and suitable for srcObj. * @param {Hash} condition * @param {Kekule.ChemObject} srcObj * @returns {Kekule.IO.ChemDataWriter} */ createWriter: function(condition, srcObj) { var info = Kekule.IO.ChemDataWriterManager.getWriterInfo(condition, srcObj); if (info) return Kekule.IO.ChemDataWriterManager.createWriterOfInfo(info); else return null; }, /** * Get a reusable writer instance by condition and suitable for srcObj. * @param {Hash} condition * @param {Kekule.ChemObject} srcObj * @returns {Kekule.IO.ChemDataWriter} */ getWriter: function(condition, srcObj) { var info = Kekule.IO.ChemDataWriterManager.getWriterInfo(condition, srcObj); if (info) return Kekule.IO.ChemDataWriterManager.getWriterOfInfo(info); else return null; }, /** * Create a new writer instance by id and meet srcObj. * @param {String} id * @param {Kekule.ChemObject} srcObj * @returns {Kekule.IO.ChemDataWriter} */ createWriterById: function(id, srcObj) { return Kekule.IO.ChemDataWriterManager.createWriter({'id': id}, srcObj); }, /** * Get a reusable writer instance by id and meet srcObj. * @param {String} id * @param {Kekule.ChemObject} srcObj * @returns {Kekule.IO.ChemDataWriter} */ getWriterById: function(id, srcObj) { return Kekule.IO.ChemDataWriterManager.getWriter({'id': id}, srcObj); }, /** * Create new writer instance by formatId. * @param {String} formatId * @param {Hash} otherConditions * @returns {Kekule.IO.ChemDataWriter} */ createWriterByFormat: function(formatId, otherConditions, srcObj) { var info = Kekule.IO.ChemDataWriterManager.getWriterInfoByFormat(formatId, otherConditions, srcObj); return info? Kekule.IO.ChemDataWriterManager.createWriterOfInfo(info): null; }, /** * Get new writer instance by formatId. * @param {String} formatId * @param {Hash} otherConditions * @returns {Kekule.IO.ChemDataWriter} */ getWriterByFormat: function(formatId, otherConditions, srcObj) { var info = Kekule.IO.ChemDataWriterManager.getWriterInfoByFormat(formatId, otherConditions, srcObj); return info? Kekule.IO.ChemDataWriterManager.getWriterOfInfo(info): null; }, /** * Create writer instance by file ext. * @param {String} fileExt * @param {Hash} otherConditions * @param {Kekule.ChemObject} srcObj * @returns {Kekule.IO.ChemDataWriter} */ createWriterByFileExt: function(fileExt, otherConditions, srcObj) { var info = Kekule.IO.ChemDataWriterManager.getWriterInfoByFileExt(fileExt, otherConditions, srcObj); return info? Kekule.IO.ChemDataWriterManager.createWriterOfInfo(info): null; }, /** * Get a reusbale writer instance by file ext. * @param {String} fileExt * @param {Hash} otherConditions * @param {Kekule.ChemObject} srcObj * @returns {Kekule.IO.ChemDataWriter} */ getWriterByFileExt: function(fileExt, otherConditions, srcObj) { var info = Kekule.IO.ChemDataWriterManager.getWriterInfoByFileExt(fileExt, otherConditions, srcObj); return info? Kekule.IO.ChemDataWriterManager.getWriterOfInfo(info): null; }, /** * Create a new reader instance by MIME type. * @param {String} mimeType * @param {Kekule.ChemObject} srcObj * @returns {Kekule.IO.ChemDataWriter} */ createWriterByMimeType: function(mimeType, srcObj) { //return Kekule.IO.ChemDataWriterManager.createWriter({'mimeType': mimeType}); var fid = Kekule.IO.DataFormatsManager.findFormatId(mimeType, null); return fid? Kekule.IO.ChemDataWriterManager.createWriterByFormat(fid, null, srcObj): null; }, /** * Get a reusable new reader instance by MIME type. * @param {String} mimeType * @returns {Kekule.IO.ChemDataWriter} */ getWriterByMimeType: function(mimeType) { //return Kekule.IO.ChemDataWriterManager.getWriter({'mimeType': mimeType}); var fid = Kekule.IO.DataFormatsManager.findFormatId(mimeType, null); return fid? Kekule.IO.ChemDataWriterManager.getWriterByFormat(fid, null): null; } }; /** * Load from content with certain format. * @param {String} content * @param {String} formatId * @param {Hash} options Additional options to read data. Different data format may have different options. * @returns {Kekule.ChemObject} */ Kekule.IO.loadFormatData = function(content, formatId, options) { var reader = Kekule.IO.ChemDataReaderManager.getReaderByFormat(formatId); if (reader) { var result = reader.readData(content, null, formatId, options); /* if ((result instanceof Kekule.ChemObject) && (result.getSrcInfo)) { var info = result.getSrcInfo(); info.mimeType = mimeType; info.fileExt = fileExt; info.url = url; } */ //if (!result) if (result === false) // read data failed { var msg = Kekule.$L('ErrorMsg.FAIL_TO_READ_FORMAT') + formatId; Kekule.raise(msg); } return result; } else { var msg = /*Kekule.ErrorMsg.NO_SUITABLE_READER_FOR_FORMAT*/Kekule.$L('ErrorMsg.NO_SUITABLE_READER_FOR_FORMAT') + formatId; Kekule.raise(msg); return null; } }; /** * Load from content with mimeType and create a new chem object. * @param {String} content * @param {String} mimeType * @param {Hash} options Additional options to read data. Different data format may have different options. * @returns {Kekule.ChemObject} */ Kekule.IO.loadMimeData = function(content, mimeType, options) { /* var reader = Kekule.IO.ChemDataReaderManager.getReaderByMimeType(mimeType); if (reader) { var result = reader.readData(content); if ((result instanceof Kekule.ChemObject) && (result.getSrcInfo)) { result.getSrcInfo().mimeType = mimeType; } } else { Kekule.raise(Kekule.ErrorMsg.NO_SUITABLE_READER_FOR_MIMETYPE + mimeType); return null; } */ return Kekule.IO.loadTypedData(content, mimeType, null, options); }; /** * Load a typed content and create a new chem object, the type is recognized by mimeType or the file extension. * @param {String} content * @param {String} mimeType * @param {String} url * @param {Hash} options Additional options to read data. Different data format may have different options. * @returns {Kekule.ChemObject} */ Kekule.IO.loadTypedData = function(content, mimeType, url, options) { //var reader; var fileExt; if (url) { fileExt = Kekule.UrlUtils.extractFileExt(url); /* if (!fileExt) fileExt = urlOrFileExt; else url = urlOrFileExt; */ } /* if (mimeType) reader = Kekule.IO.ChemDataReaderManager.getReaderByMimeType(mimeType); if (!reader && fileExt) { reader = Kekule.IO.ChemDataReaderManager.getReaderByFileExt(fileExt); } if (reader) { var result = reader.readData(content); if ((result instanceof Kekule.ChemObject) && (result.getSrcInfo)) { var info = result.getSrcInfo(); if (mimeType) info.mimeType = mimeType; if (fileExt) info.fileExt = fileExt; if (url) info.url = url; } return result; } */ //var formatId = Kekule.IO.DataFormatsManager.findFormatId(mimeType, mimeType? null: fileExt); var formatId = Kekule.IO.DataFormatsManager.findFormatId(mimeType || null, fileExt); var result; if (formatId) result = Kekule.IO.loadFormatData(content, formatId, options); //if (result) if (result !== false) // read data success { if ((result instanceof Kekule.ChemObject) && (result.getSrcInfo)) { var info = result.getSrcInfo(); if (mimeType) info.mimeType = mimeType; if (fileExt) info.fileExt = fileExt; if (url) { info.url = url; info.fileName = url; } } return result; } else { var msg = mimeType? (/*Kekule.ErrorMsg.NO_SUITABLE_READER_FOR_MIMETYPE*/Kekule.$L('ErrorMsg.NO_SUITABLE_READER_FOR_MIMETYPE') + mimeType): (/*Kekule.ErrorMsg.NO_SUITABLE_READER_FOR_FILEEXT*/Kekule.$L('ErrorMsg.NO_SUITABLE_READER_FOR_FILEEXT') + fileExt); Kekule.raise(msg); return null; } }; /** * Load chem object from a File object. * Note this function relies on FileApi support. * @param {File} file * @param {Function} callback Callback function when the file is loaded. Has two params (chemObj, success, srcData). * @param {String} formatId If not set, format will be get from file name automatically. * @param {Hash} options Additional options to read data. Different data format may have different options. */ Kekule.IO.loadFileData = function(file, callback, formatId, options) { if (Kekule.BrowserFeature.fileapi) { //try { var fileName = file.name; var ext = Kekule.UrlUtils.extractFileExt(fileName); var formatInfo; if (!formatId) { formatInfo = Kekule.IO.DataFormatsManager.findFormat(null, ext); } else formatInfo = Kekule.IO.DataFormatsManager.getFormatInfo(formatId); if (!formatInfo) { var msg = /*Kekule.ErrorMsg.NO_SUITABLE_READER_FOR_FILEEXT*/Kekule.$L('ErrorMsg.NO_SUITABLE_READER_FOR_FILEEXT') + ext; Kekule.raise(msg); return; } //var isBinary = (formatInfo.dataType === Kekule.IO.ChemDataType.BINARY); var isBinary = Kekule.IO.ChemDataType.isBinaryType(formatInfo.dataType); // try open it the file by FileReader var reader = new FileReader(); reader.onload = function(e) { var content = reader.result; var chemObj = Kekule.IO.loadFormatData(content, formatInfo.id, options); if (chemObj && chemObj.getSrcInfo) { var info = chemObj.getSrcInfo(); info.fileName = fileName; } //var success = !!chemObj; var success = (chemObj !== false); callback(chemObj, success, content); }; if (isBinary) //reader.readAsBinaryString(file); reader.readAsArrayBuffer(file); else reader.readAsText(file); } /* catch(e) { console.error('EXTRA ERROR HERE'); Kekule.error(e); } */ } else { Kekule.error(/*Kekule.ErrorMsg.FILE_API_NOT_SUPPORTED*/Kekule.$L('ErrorMsg.FILE_API_NOT_SUPPORTED')); } }; /** * Load chem object from a URL. * Note this function relies on AJAX support. * @param {String} fileUrl * @param {Function} callback Callback function when the file is loaded. Has params (chemObj, success, srcData). * @param {String} formatId If not set, format will be get from file name automatically. * @param {Hash} options Additional options to read data. Different data format may have different options. */ Kekule.IO.loadUrlData = function(fileUrl, callback, formatId, options) { if (Kekule.Ajax) { Kekule.Ajax.sendRequest(fileUrl, function(data, requestObj, success){ if (data && success) { // try resolve file format var formatInfo; if (formatId) formatInfo = Kekule.IO.DataFormatsManager.getFormatInfo(formatId); if (!formatId) { var mimeType = Kekule.Ajax.getResponseMimeType(requestObj); formatInfo = Kekule.IO.DataFormatsManager.findFormat(mimeType); } if (!formatId) { var ext = Kekule.UrlUtils.extractFileExt(fileUrl); formatInfo = Kekule.IO.DataFormatsManager.findFormat(null, ext); } if (!formatInfo) { var msg = Kekule.$L('ErrorMsg.NO_SUITABLE_READER_FOR_FILEEXT') + ext; Kekule.raise(msg); return; } var chemObj = Kekule.IO.loadFormatData(data, formatInfo.id, options); var info = chemObj.getSrcInfo(); info.fileName = fileUrl; //var success = !!chemObj; var success = (chemObj !== false); callback(chemObj, success, data); } else { Kekule.raise(Kekule.$L('ErrorMsg.FAIL_TO_LOAD_FILE_URL') + fileUrl); } }); } else { Kekule.raise(Kekule.$L('ErrorMsg.AJAX_FILELOADER_NOT_FOUND')); return null; } }; /** * Save chemObj with certain format. * @param {String} content * @param {String} formatId * @param {Hash} options Additional options to save data. Different data format may have different options. * @returns {Variant} */ Kekule.IO.saveFormatData = function(chemObj, formatId, options) { var writer = Kekule.IO.ChemDataWriterManager.getWriterByFormat(formatId, null, chemObj); if (writer) { var result = writer.writeData(chemObj, null, formatId, options); return result; } else { var msg = /*Kekule.ErrorMsg.NO_SUITABLE_WRITER_FOR_FORMAT*/Kekule.$L('ErrorMsg.NO_SUITABLE_WRITER_FOR_FORMAT') + formatId; Kekule.raise(msg); return null; } }; /** * Save chemObj to string of mimeType. * @param {Kekule.ChemObject} chemObj * @param {String} mimeType * @param {Hash} options Additional options to save data. Different data format may have different options. * @returns {Variant} */ Kekule.IO.saveMimeData = function(chemObj, mimeType, options) { /* var writer = Kekule.IO.ChemDataWriterManager.getWriterByMimeType(mimeType); if (writer) return writer.writeData(chemObj); else { Kekule.raise(Kekule.ErrorMsg.NO_SUITABLE_WRITER_FOR_MIMETYPE + mimeType); return null; } */ return Kekule.IO.saveTypedData(chemObj, mimeType, null, options); }; /** * Save chem object to a typed content. The type is recognized by mimeType or the file extension. * @param {Kekule.ChemObj} chemObj * @param {String} mimeType * @param {String} urlOrFileExt URL or file ext. * @param {Hash} options Additional options to save data. Different data format may have different options. * @returns {Variant} */ Kekule.IO.saveTypedData = function(chemObj, mimeType, urlOrFileExt, options) { var fileExt; if (urlOrFileExt) { fileExt = Kekule.UrlUtils.extractFileExt(urlOrFileExt); if (!fileExt) fileExt = urlOrFileExt; /* if (!fileExt) fileExt = urlOrFileExt; else url = urlOrFileExt; */ } //var formatId = Kekule.IO.DataFormatsManager.findFormatId(mimeType, mimeType? null: fileExt); var formatId = Kekule.IO.DataFormatsManager.findFormatId(mimeType || null, fileExt); var result; if (formatId) result = Kekule.IO.saveFormatData(chemObj, formatId, options); if (Kekule.ObjUtils.isUnset(result)) { var msg = mimeType? (/*Kekule.ErrorMsg.NO_SUITABLE_WRITER_FOR_MIMETYPE*/Kekule.$L('ErrorMsg.NO_SUITABLE_WRITER_FOR_MIMETYPE') + mimeType): (/*Kekule.ErrorMsg.NO_SUITABLE_WRITER_FOR_FILEEXT*/Kekule.$L('ErrorMsg.NO_SUITABLE_WRITER_FOR_FILEEXT') + fileExt); Kekule.raise(msg); return null; } else return result; /* var writer; if (mimeType) writer = Kekule.IO.ChemDataReaderManager.getWriterByMimeType(mimeType); else if (urlOrFileExt) { var fileExt = Kekule.UrlUtils.extractFileExt(urlOrFileExt); if (!fileExt) fileExt = urlOrFileExt; writer = Kekule.IO.ChemDataReaderManager.getWriterByFileExt(fileExt); } if (writer) return writer.writeData(chemObj); else { var msg = mimeType? Kekule.ErrorMsg.NO_SUITABLE_WRITER_FOR_MIMETYPE + mimeType: NO_SUITABLE_WRITER_FOR_FILEEXT + fileExt; Kekule.raise(msg); return null; } */ }; })();