blob: 4479a26859da8adad7639b3300b5546bd902ffba [file] [log] [blame]
<!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 &amp;&amp; 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&lt;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 &amp; 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 &amp;&amp; !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 &lt; 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 &lt; fieldOffsets.length; i++) {
fieldId = fieldOffsets[i][0];
offset = fieldOffsets[i][1];
nextOffset = i + 1 &lt; 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 &amp;&amp; !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 &amp;&amp; value !== undefined &amp;&amp; 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 &amp;&amp; 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>