| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="utf-8"> |
| <title>JSDoc: Source: BinaryObject.js</title> |
| |
| <script src="scripts/prettify/prettify.js"> </script> |
| <script src="scripts/prettify/lang-css.js"> </script> |
| <!--[if lt IE 9]> |
| <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> |
| <![endif]--> |
| <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css"> |
| <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css"> |
| </head> |
| |
| <body> |
| |
| <div id="main"> |
| |
| <h1 class="page-title">Source: BinaryObject.js</h1> |
| |
| |
| |
| |
| |
| |
| <section> |
| <article> |
| <pre class="prettyprint source linenums"><code>/* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| 'use strict'; |
| |
| const Util = require('util'); |
| const ObjectType = require('./ObjectType').ObjectType; |
| const ComplexObjectType = require('./ObjectType').ComplexObjectType; |
| const Errors = require('./Errors'); |
| const BinaryUtils = require('./internal/BinaryUtils'); |
| const BinaryType = require('./internal/BinaryType'); |
| const BinaryField = require('./internal/BinaryType').BinaryField; |
| const BinaryTypeBuilder = require('./internal/BinaryType').BinaryTypeBuilder; |
| const BinaryWriter = require('./internal/BinaryWriter'); |
| const ArgumentChecker = require('./internal/ArgumentChecker'); |
| const Logger = require('./internal/Logger'); |
| |
| const HEADER_LENGTH = 24; |
| const VERSION = 1; |
| |
| // user type |
| const FLAG_USER_TYPE = 0x0001; |
| // schema exists |
| const FLAG_HAS_SCHEMA = 0x0002; |
| // object contains raw data |
| const FLAG_HAS_RAW_DATA = 0x0004; |
| // offsets take 1 byte |
| const FLAG_OFFSET_ONE_BYTE = 0x0008; |
| // offsets take 2 bytes |
| const FLAG_OFFSET_TWO_BYTES = 0x0010; |
| // compact footer, no field IDs |
| const FLAG_COMPACT_FOOTER = 0x0020; |
| |
| /** |
| * Class representing a complex Ignite object in the binary form. |
| * |
| * It corresponds to COMPOSITE_TYPE.COMPLEX_OBJECT {@link ObjectType.COMPOSITE_TYPE}, |
| * has mandatory type Id, which corresponds to a name of the complex type, |
| * and includes optional fields. |
| * |
| * An instance of the BinaryObject can be obtained/created by the following ways: |
| * - returned by the client when a complex object is received from Ignite cache |
| * and is not deserialized to another JavaScript object. |
| * - created using the public constructor. Fields may be added to such an instance using setField() method. |
| * - created from a JavaScript object using static fromObject() method. |
| */ |
| class BinaryObject { |
| |
| /** |
| * Creates an instance of the BinaryObject without any fields. |
| * |
| * Fields may be added later using setField() method. |
| * |
| * @param {string} typeName - name of the complex type to generate the type Id. |
| * |
| * @return {BinaryObject} - new BinaryObject instance. |
| * |
| * @throws {IgniteClientError} if error. |
| */ |
| constructor(typeName) { |
| ArgumentChecker.notEmpty(typeName, 'typeName'); |
| this._buffer = null; |
| this._fields = new Map(); |
| this._typeBuilder = BinaryTypeBuilder.fromTypeName(typeName); |
| this._modified = false; |
| this._schemaOffset = null; |
| this._compactFooter = false; |
| } |
| |
| /** |
| * Creates an instance of the BinaryObject from the specified instance of JavaScript Object. |
| * |
| * All fields of the JavaScript Object instance with their values are added to the BinaryObject. |
| * Fields may be added or removed later using setField() and removeField() methods. |
| * |
| * If complexObjectType parameter is specified, then the type Id is taken from it. |
| * Otherwise, the type Id is generated from the name of the JavaScript Object. |
| * |
| * @async |
| * |
| * @param {object} jsObject - instance of JavaScript Object |
| * which adds and initializes the fields of the BinaryObject instance. |
| * @param {ComplexObjectType} [complexObjectType] - instance of complex type definition |
| * which specifies non-standard mapping of the fields of the BinaryObject instance |
| * to/from the Ignite types. |
| * |
| * @return {BinaryObject} - new BinaryObject instance. |
| * |
| * @throws {IgniteClientError} if error. |
| */ |
| static async fromObject(jsObject, complexObjectType = null) { |
| ArgumentChecker.notEmpty(jsObject, 'jsObject'); |
| ArgumentChecker.hasType(complexObjectType, 'complexObjectType', false, ComplexObjectType); |
| const typeBuilder = BinaryTypeBuilder.fromObject(jsObject, complexObjectType); |
| const result = new BinaryObject(typeBuilder.getTypeName()); |
| result._typeBuilder = typeBuilder; |
| let fieldName; |
| for (let field of result._typeBuilder.getFields()) { |
| fieldName = field.name; |
| if (jsObject && jsObject[fieldName] !== undefined) { |
| result.setField( |
| fieldName, |
| jsObject[fieldName], |
| complexObjectType ? complexObjectType._getFieldType(fieldName) : null); |
| } |
| else { |
| throw Errors.IgniteClientError.serializationError( |
| true, Util.format('field "%s" is undefined', fieldName)); |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * Sets the new value of the specified field. |
| * Adds the specified field, if it did not exist before. |
| * |
| * Optionally, specifies a type of the field. |
| * If the type is not specified then during operations the Ignite client |
| * will try to make automatic mapping between JavaScript types and Ignite object types - |
| * according to the mapping table defined in the description of the {@link ObjectType} class. |
| * |
| * @param {string} fieldName - name of the field. |
| * @param {*} fieldValue - new value of the field. |
| * @param {ObjectType.PRIMITIVE_TYPE | CompositeType} [fieldType] - type of the field: |
| * - either a type code of primitive (simple) type |
| * - or an instance of class representing non-primitive (composite) type |
| * - or null (or not specified) that means the type is not specified. |
| * |
| * @return {BinaryObject} - the same instance of BinaryObject |
| * |
| * @throws {IgniteClientError} if error. |
| */ |
| setField(fieldName, fieldValue, fieldType = null) { |
| ArgumentChecker.notEmpty(fieldName, 'fieldName'); |
| this._modified = true; |
| const field = new BinaryObjectField(fieldName, fieldValue, fieldType); |
| this._fields.set(field.id, field); |
| this._typeBuilder.setField(fieldName, field.typeCode); |
| return this; |
| } |
| |
| /** |
| * Removes the specified field. |
| * Does nothing if the field does not exist. |
| * |
| * @param {string} fieldName - name of the field. |
| * |
| * @return {BinaryObject} - the same instance of BinaryObject |
| * |
| * @throws {IgniteClientError} if error. |
| */ |
| removeField(fieldName) { |
| ArgumentChecker.notEmpty(fieldName, 'fieldName'); |
| this._modified = true; |
| this._fields.delete(BinaryField._calculateId(fieldName)); |
| this._typeBuilder.removeField(fieldName); |
| return this; |
| } |
| |
| /** |
| * Checks if the specified field exists in this BinaryObject instance. |
| * |
| * @param {string} fieldName - name of the field. |
| * |
| * @return {boolean} - true if exists, false otherwise. |
| * |
| * @throws {IgniteClientError} if error. |
| */ |
| hasField(fieldName) { |
| ArgumentChecker.notEmpty(fieldName, 'fieldName'); |
| return this._fields.has(BinaryField._calculateId(fieldName)); |
| } |
| |
| /** |
| * Returns a value of the specified field. |
| * |
| * Optionally, specifies a type of the field. |
| * If the type is not specified then the Ignite client |
| * will try to make automatic mapping between JavaScript types and Ignite object types - |
| * according to the mapping table defined in the description of the {@link ObjectType} class. |
| * |
| * @async |
| * |
| * @param {string} fieldName - name of the field. |
| * @param {ObjectType.PRIMITIVE_TYPE | CompositeType} [fieldType] - type of the field: |
| * - either a type code of primitive (simple) type |
| * - or an instance of class representing non-primitive (composite) type |
| * - or null (or not specified) that means the type is not specified. |
| * |
| * @return {*} - value of the field or JavaScript undefined if the field does not exist. |
| * |
| * @throws {IgniteClientError} if error. |
| */ |
| async getField(fieldName, fieldType = null) { |
| ArgumentChecker.notEmpty(fieldName, 'fieldName'); |
| const field = this._fields.get(BinaryField._calculateId(fieldName)); |
| return field ? await field.getValue(fieldType) : undefined; |
| } |
| |
| /** |
| * Deserializes this BinaryObject instance into an instance of the specified complex object type. |
| * |
| * @async |
| * |
| * @param {ComplexObjectType} complexObjectType - instance of class representing complex object type. |
| * |
| * @return {object} - instance of the JavaScript object |
| * which corresponds to the specified complex object type. |
| * |
| * @throws {IgniteClientError} if error. |
| */ |
| async toObject(complexObjectType) { |
| ArgumentChecker.notNull(complexObjectType, 'complexObjectType'); |
| ArgumentChecker.hasType(complexObjectType, 'complexObjectType', false, ComplexObjectType); |
| const result = new (complexObjectType._objectConstructor); |
| let binaryField; |
| let fieldName; |
| for (let field of this._fields.values()) { |
| binaryField = this._typeBuilder.getField(field.id); |
| if (!binaryField) { |
| throw Errors.IgniteClientError.serializationError( |
| false, Util.format('field with id "%s" can not be deserialized', field.id)); |
| } |
| fieldName = binaryField.name; |
| result[fieldName] = await field.getValue(complexObjectType._getFieldType(fieldName)); |
| } |
| return result; |
| } |
| |
| /** |
| * Returns type name of this BinaryObject instance. |
| * |
| * @return {string} - type name. |
| */ |
| getTypeName() { |
| return this._typeBuilder.getTypeName(); |
| } |
| |
| /** |
| * Returns names of all fields of this BinaryObject instance. |
| * |
| * @return {Array<string>} - names of all fields. |
| * |
| * @throws {IgniteClientError} if error. |
| */ |
| getFieldNames() { |
| return this._typeBuilder._schema.fieldIds.map(fieldId => { |
| const field = this._typeBuilder.getField(fieldId); |
| if (field) { |
| return field.name; |
| } |
| else { |
| throw Errors.IgniteClientError.internalError( |
| Util.format('Field "%s" is absent in binary type fields', fieldId)); |
| } |
| }); |
| } |
| |
| /** Private methods */ |
| |
| /** |
| * @ignore |
| */ |
| static _isFlagSet(flags, flag) { |
| return (flags & flag) === flag; |
| } |
| |
| /** |
| * @ignore |
| */ |
| static async _fromBuffer(buffer) { |
| const result = new BinaryObject(new ComplexObjectType({})._typeName); |
| result._buffer = buffer; |
| result._startPos = buffer.position; |
| await result._read(); |
| return result; |
| } |
| |
| /** |
| * @ignore |
| */ |
| async _write(buffer) { |
| if (this._buffer && !this._modified) { |
| buffer.writeBuffer(this._buffer.buffer, this._startPos, this._startPos + this._length); |
| } |
| else { |
| await this._typeBuilder.finalize(); |
| this._startPos = buffer.position; |
| buffer.position = this._startPos + HEADER_LENGTH; |
| // write fields |
| for (let field of this._fields.values()) { |
| await field._writeValue(buffer, this._typeBuilder.getField(field.id).typeCode); |
| } |
| this._schemaOffset = buffer.position - this._startPos; |
| // write schema |
| for (let field of this._fields.values()) { |
| field._writeOffset(buffer, this._startPos); |
| } |
| this._length = buffer.position - this._startPos; |
| this._buffer = buffer; |
| // write header |
| this._writeHeader(); |
| this._buffer.position = this._startPos + this._length; |
| this._modified = false; |
| } |
| |
| if (Logger.debug) { |
| Logger.logDebug('BinaryObject._write [' + [...this._buffer.getSlice(this._startPos, this._startPos + this._length)] + ']'); |
| } |
| } |
| |
| /** |
| * @ignore |
| */ |
| _writeHeader() { |
| this._buffer.position = this._startPos; |
| // type code |
| this._buffer.writeByte(BinaryUtils.TYPE_CODE.COMPLEX_OBJECT); |
| // version |
| this._buffer.writeByte(VERSION); |
| // flags |
| this._buffer.writeShort(FLAG_USER_TYPE | FLAG_HAS_SCHEMA | FLAG_COMPACT_FOOTER); |
| // type id |
| this._buffer.writeInteger(this._typeBuilder.getTypeId()); |
| // hash code |
| this._buffer.writeInteger(BinaryUtils.contentHashCode( |
| this._buffer, this._startPos + HEADER_LENGTH, this._schemaOffset - 1)); |
| // length |
| this._buffer.writeInteger(this._length); |
| // schema id |
| this._buffer.writeInteger(this._typeBuilder.getSchemaId()); |
| // schema offset |
| this._buffer.writeInteger(this._schemaOffset); |
| } |
| |
| /** |
| * @ignore |
| */ |
| async _read() { |
| await this._readHeader(); |
| this._buffer.position = this._startPos + this._schemaOffset; |
| const fieldOffsets = new Array(); |
| const fieldIds = this._typeBuilder._schema.fieldIds; |
| let index = 0; |
| let fieldId; |
| while (this._buffer.position < this._startPos + this._length) { |
| if (!this._compactFooter) { |
| fieldId = this._buffer.readInteger(); |
| this._typeBuilder._schema.addField(fieldId); |
| } |
| else { |
| if (index >= fieldIds.length) { |
| throw Errors.IgniteClientError.serializationError( |
| false, 'wrong number of fields in schema'); |
| } |
| fieldId = fieldIds[index]; |
| index++; |
| } |
| fieldOffsets.push([fieldId, this._buffer.readNumber(this._offsetType)]); |
| } |
| fieldOffsets.sort((val1, val2) => val1[1] - val2[1]); |
| let offset; |
| let nextOffset; |
| let field; |
| for (let i = 0; i < fieldOffsets.length; i++) { |
| fieldId = fieldOffsets[i][0]; |
| offset = fieldOffsets[i][1]; |
| nextOffset = i + 1 < fieldOffsets.length ? fieldOffsets[i + 1][1] : this._schemaOffset; |
| field = BinaryObjectField._fromBuffer( |
| this._buffer, this._startPos + offset, nextOffset - offset, fieldId); |
| this._fields.set(field.id, field); |
| } |
| this._buffer.position = this._startPos + this._length; |
| } |
| |
| /** |
| * @ignore |
| */ |
| async _readHeader() { |
| // type code |
| this._buffer.readByte(); |
| // version |
| const version = this._buffer.readByte(); |
| if (version !== VERSION) { |
| throw Errors.IgniteClientError.internalError(); |
| } |
| // flags |
| const flags = this._buffer.readShort(); |
| // type id |
| const typeId = this._buffer.readInteger(); |
| // hash code |
| this._buffer.readInteger(); |
| // length |
| this._length = this._buffer.readInteger(); |
| // schema id |
| const schemaId = this._buffer.readInteger(); |
| // schema offset |
| this._schemaOffset = this._buffer.readInteger(); |
| const hasSchema = BinaryObject._isFlagSet(flags, FLAG_HAS_SCHEMA); |
| this._compactFooter = BinaryObject._isFlagSet(flags, FLAG_COMPACT_FOOTER); |
| this._offsetType = BinaryObject._isFlagSet(flags, FLAG_OFFSET_ONE_BYTE) ? |
| BinaryUtils.TYPE_CODE.BYTE : |
| BinaryObject._isFlagSet(flags, FLAG_OFFSET_TWO_BYTES) ? |
| BinaryUtils.TYPE_CODE.SHORT : |
| BinaryUtils.TYPE_CODE.INTEGER; |
| |
| if (BinaryObject._isFlagSet(FLAG_HAS_RAW_DATA)) { |
| throw Errors.IgniteClientError.serializationError( |
| false, 'complex objects with raw data are not supported'); |
| } |
| if (this._compactFooter && !hasSchema) { |
| throw Errors.IgniteClientError.serializationError( |
| false, 'schema is absent for object with compact footer'); |
| } |
| this._typeBuilder = await BinaryTypeBuilder.fromTypeId(typeId, schemaId, hasSchema); |
| } |
| } |
| |
| /** |
| * @ignore |
| */ |
| class BinaryObjectField { |
| constructor(name, value = undefined, type = null) { |
| this._name = name; |
| this._id = BinaryField._calculateId(name); |
| this._value = value; |
| this._type = type; |
| if (!type && value !== undefined && value !== null) { |
| this._type = BinaryUtils.calcObjectType(value); |
| } |
| this._typeCode = null; |
| if (this._type) { |
| this._typeCode = BinaryUtils.getTypeCode(this._type); |
| } |
| } |
| |
| get id() { |
| return this._id; |
| } |
| |
| get typeCode() { |
| return this._typeCode; |
| } |
| |
| async getValue(type = null) { |
| if (this._value === undefined || this._buffer && this._type !== type) { |
| this._buffer.position = this._offset; |
| const BinaryReader = require('./internal/BinaryReader'); |
| this._value = await BinaryReader.readObject(this._buffer, type); |
| this._type = type; |
| } |
| return this._value; |
| } |
| |
| static _fromBuffer(buffer, offset, length, id) { |
| const result = new BinaryObjectField(null); |
| result._id = id; |
| result._buffer = buffer; |
| result._offset = offset; |
| result._length = length; |
| return result; |
| } |
| |
| async _writeValue(buffer, expectedTypeCode) { |
| const offset = buffer.position; |
| if (this._buffer) { |
| buffer.writeBuffer(this._buffer.buffer, this._offset, this._offset + this._length); |
| } |
| else { |
| BinaryUtils.checkCompatibility(this._value, expectedTypeCode); |
| await BinaryWriter.writeObject(buffer, this._value, this._type); |
| } |
| this._buffer = buffer; |
| this._length = buffer.position - offset; |
| this._offset = offset; |
| } |
| |
| _writeOffset(buffer, headerStartPos) { |
| buffer.writeInteger(this._offset - headerStartPos); |
| } |
| } |
| |
| module.exports = BinaryObject; |
| </code></pre> |
| </article> |
| </section> |
| |
| |
| |
| |
| </div> |
| |
| <nav> |
| <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="BinaryObject.html">BinaryObject</a></li><li><a href="CacheClient.html">CacheClient</a></li><li><a href="CacheConfiguration.html">CacheConfiguration</a></li><li><a href="CacheEntry.html">CacheEntry</a></li><li><a href="CacheKeyConfiguration.html">CacheKeyConfiguration</a></li><li><a href="CollectionObjectType.html">CollectionObjectType</a></li><li><a href="ComplexObjectType.html">ComplexObjectType</a></li><li><a href="CompositeType.html">CompositeType</a></li><li><a href="Cursor.html">Cursor</a></li><li><a href="EnumItem.html">EnumItem</a></li><li><a href="IgniteClient.html">IgniteClient</a></li><li><a href="IgniteClientConfiguration.html">IgniteClientConfiguration</a></li><li><a href="IgniteClientError.html">IgniteClientError</a></li><li><a href="IllegalStateError.html">IllegalStateError</a></li><li><a href="LostConnectionError.html">LostConnectionError</a></li><li><a href="MapObjectType.html">MapObjectType</a></li><li><a href="ObjectArrayType.html">ObjectArrayType</a></li><li><a href="ObjectType.html">ObjectType</a></li><li><a href="OperationError.html">OperationError</a></li><li><a href="Query.html">Query</a></li><li><a href="QueryEntity.html">QueryEntity</a></li><li><a href="QueryField.html">QueryField</a></li><li><a href="QueryIndex.html">QueryIndex</a></li><li><a href="ScanQuery.html">ScanQuery</a></li><li><a href="SqlFieldsCursor.html">SqlFieldsCursor</a></li><li><a href="SqlFieldsQuery.html">SqlFieldsQuery</a></li><li><a href="SqlQuery.html">SqlQuery</a></li><li><a href="Timestamp.html">Timestamp</a></li></ul> |
| </nav> |
| |
| <br class="clear"> |
| |
| <footer> |
| Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.5.5</a> on Tue May 22 2018 12:08:48 GMT+0300 (Russia TZ 2 Standard Time) |
| </footer> |
| |
| <script> prettyPrint(); </script> |
| <script src="scripts/linenumber.js"> </script> |
| </body> |
| </html> |