123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217 |
- // Copyright 2011 Mark Cavage, Inc. All rights reserved.
-
- var assert = require('assert-plus');
-
- var Attribute = require('./attribute');
- var Protocol = require('./protocol');
-
-
- ///--- API
-
- function Change(options) {
- if (options) {
- assert.object(options);
- assert.optionalString(options.operation);
- } else {
- options = {};
- }
-
- this._modification = false;
- this.operation = options.operation || options.type || 'add';
- this.modification = options.modification || {};
- }
- Object.defineProperties(Change.prototype, {
- operation: {
- get: function getOperation() {
- switch (this._operation) {
- case 0x00: return 'add';
- case 0x01: return 'delete';
- case 0x02: return 'replace';
- default:
- throw new Error('0x' + this._operation.toString(16) + ' is invalid');
- }
- },
- set: function setOperation(val) {
- assert.string(val);
- switch (val.toLowerCase()) {
- case 'add':
- this._operation = 0x00;
- break;
- case 'delete':
- this._operation = 0x01;
- break;
- case 'replace':
- this._operation = 0x02;
- break;
- default:
- throw new Error('Invalid operation type: 0x' + val.toString(16));
- }
- },
- configurable: false
- },
- modification: {
- get: function getModification() {
- return this._modification;
- },
- set: function setModification(val) {
- if (Attribute.isAttribute(val)) {
- this._modification = val;
- return;
- }
- // Does it have an attribute-like structure
- if (Object.keys(val).length == 2 &&
- typeof (val.type) === 'string' &&
- Array.isArray(val.vals)) {
- this._modification = new Attribute({
- type: val.type,
- vals: val.vals
- });
- return;
- }
-
- var keys = Object.keys(val);
- if (keys.length > 1) {
- throw new Error('Only one attribute per Change allowed');
- } else if (keys.length === 0) {
- return;
- }
-
- var k = keys[0];
- var _attr = new Attribute({type: k});
- if (Array.isArray(val[k])) {
- val[k].forEach(function (v) {
- _attr.addValue(v.toString());
- });
- } else {
- _attr.addValue(val[k].toString());
- }
- this._modification = _attr;
- },
- configurable: false
- },
- json: {
- get: function getJSON() {
- return {
- operation: this.operation,
- modification: this._modification ? this._modification.json : {}
- };
- },
- configurable: false
- }
- });
-
- Change.isChange = function isChange(change) {
- if (!change || typeof (change) !== 'object') {
- return false;
- }
- if ((change instanceof Change) ||
- ((typeof (change.toBer) === 'function') &&
- (change.modification !== undefined) &&
- (change.operation !== undefined))) {
- return true;
- }
- return false;
- };
-
- Change.compare = function (a, b) {
- if (!Change.isChange(a) || !Change.isChange(b))
- throw new TypeError('can only compare Changes');
-
- if (a.operation < b.operation)
- return -1;
- if (a.operation > b.operation)
- return 1;
-
- return Attribute.compare(a.modification, b.modification);
- };
-
- /**
- * Apply a Change to properties of an object.
- *
- * @param {Object} change the change to apply.
- * @param {Object} obj the object to apply it to.
- * @param {Boolean} scalar convert single-item arrays to scalars. Default: false
- */
- Change.apply = function apply(change, obj, scalar) {
- assert.string(change.operation);
- assert.string(change.modification.type);
- assert.ok(Array.isArray(change.modification.vals));
- assert.object(obj);
-
- var type = change.modification.type;
- var vals = change.modification.vals;
- var data = obj[type];
- if (data !== undefined) {
- if (!Array.isArray(data)) {
- data = [data];
- }
- } else {
- data = [];
- }
- switch (change.operation) {
- case 'replace':
- if (vals.length === 0) {
- // replace empty is a delete
- delete obj[type];
- return obj;
- } else {
- data = vals;
- }
- break;
- case 'add':
- // add only new unique entries
- var newValues = vals.filter(function (entry) {
- return (data.indexOf(entry) === -1);
- });
- data = data.concat(newValues);
- break;
- case 'delete':
- data = data.filter(function (entry) {
- return (vals.indexOf(entry) === -1);
- });
- if (data.length === 0) {
- // Erase the attribute if empty
- delete obj[type];
- return obj;
- }
- break;
- default:
- break;
- }
- if (scalar && data.length === 1) {
- // store single-value outputs as scalars, if requested
- obj[type] = data[0];
- } else {
- obj[type] = data;
- }
- return obj;
- };
-
-
- Change.prototype.parse = function (ber) {
- assert.ok(ber);
-
- ber.readSequence();
- this._operation = ber.readEnumeration();
- this._modification = new Attribute();
- this._modification.parse(ber);
-
- return true;
- };
-
-
- Change.prototype.toBer = function (ber) {
- assert.ok(ber);
-
- ber.startSequence();
- ber.writeEnumeration(this._operation);
- ber = this._modification.toBer(ber);
- ber.endSequence();
-
- return ber;
- };
-
-
- ///--- Exports
-
- module.exports = Change;
|