Source: delegation-set.js

/**
 * Copyright (C) 2016 Regents of the University of California.
 * @author: Jeff Thompson <jefft0@remap.ucla.edu>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * A copy of the GNU Lesser General Public License is in the file COPYING.
 */

/** @ignore */
var Name = require('./name.js').Name; /** @ignore */
var Blob = require('./util/blob.js').Blob; /** @ignore */
var WireFormat = require('./encoding/wire-format.js').WireFormat;

/**
 * A DelegationSet holds a list of DelegationSet.Delegation entries which is
 * used as the content of a Link instance. If you add elements with add(), then
 * the list is a set sorted by preference number then by name. But wireDecode
 * will add the elements from the wire encoding, preserving the given order and
 * possible duplicates (in which case a DelegationSet really holds a "list" and
 * not necessarily a "set").
 *
 * Create a new DelegationSet object, possibly copying values from another
 * object.
 *
 * @param {DelegationSet} value (optional) If value is a DelegationSet, copy its
 * values.
 * @constructor
 */
var DelegationSet = function DelegationSet(value)
{
  if (typeof value === 'object' && value instanceof DelegationSet)
    // Copy the list.
    this.delegations_ = value.delegations_.slice(0);
  else
    this.delegations_ = []; // of DelegationSet.Delegation.
};

exports.DelegationSet = DelegationSet;

/**
 * A DelegationSet.Delegation holds a preference number and delegation name.
 * Create a new DelegationSet.Delegation with the given values.
 * @param {number} preference The preference number.
 * @param {Name} name The delegation name. This makes a copy of the name.
 * @constructor
 */
DelegationSet.Delegation = function DelegationSetDelegation(preference, name)
{
  this.preference_ = preference;
  this.name_ = new Name(name);
};

/**
 * Get the preference number.
 * @return {number} The preference number.
 */
DelegationSet.Delegation.prototype.getPreference = function()
{
  return this.preference_;
};

/**
 * Get the delegation name.
 * @return {Name} The delegation name. NOTE: You must not change the name object -
 * if you need to change it then make a copy.
 */
DelegationSet.Delegation.prototype.getName = function()
{
  return this.name_;
};

/**
 * Compare this Delegation with other according to the ordering, based first
 * on the preference number, then on the delegation name.
 * @param {DelegationSet.Delegation} other The other Delegation to compare with.
 * @return {number} 0 If they compare equal, -1 if this Delegation comes before
 * other in the ordering, or 1 if this Delegation comes after.
 */
DelegationSet.Delegation.prototype.compare = function(other)
{
  if (this.preference_ < other.preference_)
    return -1;
  if (this.preference_ > other.preference_)
    return 1;

  return this.name_.compare(other.name_);
};

/**
 * Add a new DelegationSet.Delegation to the list of delegations, sorted by
 * preference number then by name. If there is already a delegation with the
 * same name, update its preference, and remove any extra delegations with the
 * same name.
 * @param {number} preference The preference number.
 * @param {Name} name The delegation name. This makes a copy of the name.
 */
DelegationSet.prototype.add = function(preference, name)
{
  this.remove(name);

  var newDelegation = new DelegationSet.Delegation(preference, name);
  // Find the index of the first entry where it is not less than newDelegation.
  var i = 0;
  while (i < this.delegations_.length) {
    if (this.delegations_[i].compare(newDelegation) >= 0)
      break;

    ++i;
  }

  this.delegations_.splice(i, 0, newDelegation);
};

/**
 * Add a new DelegationSet.Delegation to the end of the list of delegations,
 * without sorting or updating any existing entries. This is useful for adding
 * preferences from a wire encoding, preserving the supplied ordering and
 * possible duplicates.
 * @param {number} preference The preference number.
 * @param {Name} name The delegation name. This makes a copy of the name.
 */
DelegationSet.prototype.addUnsorted = function(preference, name)
{
  this.delegations_.push(new DelegationSet.Delegation(preference, name));
};

/**
 * Remove every DelegationSet.Delegation with the given name.
 * @param {Name} name The name to match the name of the delegation(s) to be
 * removed.
 * @return {boolean} True if a DelegationSet.Delegation was removed, otherwise
 * false.
 */
DelegationSet.prototype.remove = function(name)
{
  var wasRemoved = false;
  // Go backwards through the list so we can remove entries.
  for (var i = this.delegations_.length - 1; i >= 0; --i) {
    if (this.delegations_[i].getName().equals(name)) {
      wasRemoved = true;
      this.delegations_.splice(i, 1);
    }
  }

  return wasRemoved;
};

/**
 * Clear the list of delegations.
 */
DelegationSet.prototype.clear = function() { this.delegations_ = []; };

/**
 * Get the number of delegation entries.
 * @return {number} The number of delegation entries.
 */
DelegationSet.prototype.size = function() { return this.delegations_.length; };

/**
 * Get the delegation at the given index, according to the ordering described
 * in add().
 * @param {number} i The index of the component, starting from 0.
 * @return {DelegationSet.Delegation} The delegation at the index.
 */
DelegationSet.prototype.get = function(i) { return this.delegations_[i]; };

/**
 * Find the first delegation with the given name and return its index.
 * @param {Name} name Then name of the delegation to find.
 * @return {number} The index of the delegation, or -1 if not found.
 */
DelegationSet.prototype.find = function(name)
{
  for (var i = 0; i < this.delegations_.length; ++i) {
    if (this.delegations_[i].getName().equals(name))
      return i;
  }

  return -1;
};

/**
 * Encode this DelegationSet for a particular wire format.
 * @param {WireFormat} wireFormat (optional) A WireFormat object used to encode
 * this object. If omitted, use WireFormat.getDefaultWireFormat().
 * @returns {Blob} The encoded buffer in a Blob object.
 */
DelegationSet.prototype.wireEncode = function(wireFormat)
{
  wireFormat = (wireFormat || WireFormat.getDefaultWireFormat());
  return wireFormat.encodeDelegationSet(this);
};

/**
 * Decode the input using a particular wire format and update this DelegationSet.
 * @param {Blob|Buffer} input The buffer with the bytes to decode.
 * @param {WireFormat} wireFormat (optional) A WireFormat object used to decode
 * this object. If omitted, use WireFormat.getDefaultWireFormat().
 */
DelegationSet.prototype.wireDecode = function(input, wireFormat)
{
  wireFormat = (wireFormat || WireFormat.getDefaultWireFormat());
  // If input is a blob, get its buf().
  var decodeBuffer = typeof input === 'object' && input instanceof Blob ?
                     input.buf() : input;
  wireFormat.decodeDelegationSet(this, decodeBuffer);
};