baja/ord/NSpaceScheme.js

/**
 * @copyright 2020 Tridium, Inc. All Rights Reserved.
 * @author Vikram Nagulan
 */

/**
 * Defines {@link baja.NSpaceScheme}.
 * @module baja/ord/NSpaceScheme
 */
define([
  "bajaScript/sys",
  "bajaScript/baja/ord/OrdQuery",
  "bajaScript/baja/ord/OrdScheme",
  "bajaScript/baja/ord/OrdTarget",
  "bajaScript/baja/ord/SlotPath"
], function (
  baja,
  OrdQuery,
  OrdScheme,
  OrdTarget,
  SlotPath
) {
  'use strict';

  var subclass = baja.subclass,
    callSuper = baja.callSuper;

  /**
   * NSpace ORD Scheme.
   * 
   * This scheme resolves to Component Space or Virtual Component Space.
   *
   * @class
   * @alias baja.NSpaceScheme
   * @extends baja.OrdScheme
   * @private
   */
  var NSpaceScheme = function NSpaceScheme() {
    callSuper(NSpaceScheme, this, arguments);
  };

  subclass(NSpaceScheme, OrdScheme);

  /**
   * Default NSpace ORD Scheme instance.
   * @private
   * @type {baja.NSpaceScheme}
   */
  NSpaceScheme.DEFAULT = new NSpaceScheme();

  NSpaceScheme.prototype.resolve = function (target, query, cursor, options) {
    var body = query.getBody();
    var newTarget = new OrdTarget(target);
    newTarget.object = target.object;
    if (!body) {
      newTarget.object = baja.nav.localhost.station;
      return cursor.resolveNext(newTarget, options);
    }

    return baja.rpc({
      typeSpec: "niagaraDriver:NiagaraNetwork",
      method: "getStationSpaceOrdByName",
      args: [ body ]
    })
      .then(function (stationSpaceOrd) {
        return baja.Ord.make(stationSpaceOrd).resolve()
          .then(function (target) {
            newTarget.object = target.object;
            return cursor.resolveNext(newTarget, options);
          });
      })
      .catch(function (err) {
        options.callback.fail(err);
      });
  };

  /**
   * Return an ORD Query for the scheme.
   *
   * @returns {baja.OrdQuery}
   */
  NSpaceScheme.prototype.parse = function (schemeName, body) {
    return new OrdQuery({
      scheme: this,
      schemeName: schemeName,
      body: body,
      isHost: false,
      isSession: false,
      normalize: function (list, index, cx) {
        // Only when this normalize() call happens at ORD resolution time (as
        // determined by the Context parameter), check to see if the OrdQuery
        // immediately trailing this "nspace" one is a SlotPath (or VirtualPath).
        // If so, and it's absolute, then replace it with an equivalent relative
        // SlotPath. This is necessary because a trailing absolute SlotPath,
        // when resolved, could clobber the virtual path ORD that gets
        // substituted for this "nspace" OrdQuery at resolution time, and that's
        // particularly problematic when targeting a reachable station (one that
        // isn't directly specified in the local station's NiagaraNetwork).
        // Since "nspace" is a space Ord scheme, it's fine to convert an
        // absolute SlotPath (or VirtualPath) that immediately trails it to a
        // relative one at resolution time.  For compatibility reasons with
        // older versions (and persisted BOrds in the old format), we don't
        // want to do this conversion when normalize() is called for the non-ORD
        // resolution case.  For example, consider the following ORD:
        //
        //   nspace:JaceB|slot:/a/b/c
        //
        // At ORD resolution time, the OrdQueries are resolved from left to
        // right.  If "JaceB" is a reachable station (not a direct
        // BNiagaraStation child in the local NiagaraNetwork, but reachable thru
        // "JaceA" which is in the local station's NiagaraNetwork), then just the
        // "nspace:JaceB" part would resolve to something like:
        //
        //   station:|slot:/Drivers/NiagaraNetwork/JaceA/virtual|virtual:/Drivers/NiagaraNetwork/JaceB/virtual/virtual
        //
        // If the trailing absolute SlotPath "slot:/a/b/c" was left to resolve
        // after the full virtual path substituted above, it would look like this:
        //
        //   station:|slot:/Drivers/NiagaraNetwork/JaceA/virtual|virtual:/Drivers/NiagaraNetwork/JaceB/virtual/virtual|slot:/a/b/c
        //
        // When resolved, because of how absolute SlotPaths clobber any
        // SlotPaths (including VirtualPaths) to their left until it reaches
        // a space (virtual space in this case), it would incorrectly resolve to:
        //
        //   station:|slot:/Drivers/NiagaraNetwork/JaceA/virtual|virtual:|slot:/a/b/c
        //
        // That's obviously not right. Therefore we need to change the absolute
        // "slot:/a/b/c" part to a relative form: "slot:a/b/c". That way the
        // example ORD above will correctly resolve to the following, without
        // clobbering the virtual path that was substituted for the "nspace"
        // part (because relative SlotPaths don't clobber like absolute ones):
        //
        //   station:|slot:/Drivers/NiagaraNetwork/JaceA/virtual|virtual:/Drivers/NiagaraNetwork/JaceB/virtual/virtual|slot:a/b/c

        if (cx && cx === OrdQuery.RESOLVING_ORD_CX) {
          var i, q, nextQuery, slotPathBody, modified = false,
            nextIdx = index + 1;

          if (nextIdx < list.size()) {
            nextQuery = list.get(nextIdx);
            if (nextQuery && nextQuery instanceof baja.SlotPath) {
              slotPathBody = nextQuery.getBody();
              if (nextQuery.isAbsolute()) {
                if (slotPathBody && slotPathBody.startsWith('/')) {
                  if (slotPathBody.length === 1) {
                    list.set(nextIdx, new SlotPath(''));
                  } else {
                    list.set(nextIdx, nextQuery.makeSlotPath(slotPathBody.substr(1)));
                  }
                  modified = true;
                }
              } else if ((!slotPathBody || slotPathBody === '') && nextQuery instanceof baja.VirtualPath) {
                // If the next path is simply "virtual:", then just remove it
                // since this "nspace" ORD resolves to the proper virtual space
                list.remove(nextIdx);
                modified = true;
              }
            }
          }
        }

        // Always shift to session just like the "station" component space ORD scheme
        // (since "nspace" is similar in that it resolves to a virtual component
        // space)
        for (i = index - 1; i >= 0; --i) {
          q = list.get(i);
          if (!q.isHost() && !q.isSession()) {
            list.remove(i);
            modified = true;
          }
        }
        return modified;
      }
    });
  };

  return NSpaceScheme;
});