/**
* @copyright 2015 Tridium, Inc. All Rights Reserved.
* @author Gareth Johnson
*/
/**
* Defines {@link baja.Simple}.
*
* @module baja/obj/Simple
*/
define([ "bajaScript/sys",
"bajaScript/baja/obj/Value" ], function (
baja,
Value) {
'use strict';
var subclass = baja.subclass,
callSuper = baja.callSuper,
bajaHasType = baja.hasType;
/**
* Represents `baja:Simple` in BajaScript.
*
* `Simple`s are immutable and represent primitive data types in Niagara.
* They are the basic building blocks of the architecture. Simples contain
* no slots themselves but do contain an implicit data value that can be
* encoded and decoded in a `String` format.
*
* `Simple`s must be immutable and under no circumstances should there be
* any attempt to modify the contents of a Simple.
*
* All `Simple`s must conform to the following conventions...
*
* * Define a `DEFAULT` instance on the `Simple` constructor.
* * Define a `make` method.
* * Define a `decodeFromString` method on the Object's instance that takes a
* `String` and returns an instance of the `Simple`.
* * Define an `encodeToString` method on the Object's instance that encodes
* the value to a `String`.
*
* Since this Constructor represents an abstract class, it should never
* be directly used to create a new object.
*
* @class
* @alias baja.Simple
* @extends baja.Value
*/
var Simple = function Simple() {
callSuper(Simple, this, arguments);
};
subclass(Simple, Value);
/**
* Equality test.
*
* @param obj
* @returns {Boolean}
*/
Simple.prototype.equals = function (obj) {
// Comparing in via encodeToString is ok because most Simples
// lazily cache their string encoding (since they're all immutable)
return bajaHasType(obj) &&
obj.getType().equals(this.getType()) &&
obj.encodeToString() === this.encodeToString();
};
/**
* Returns the String representation of this object.
*
* @see baja.Object#toString
*
* @param {Object} [cx] optional context information to be used when
* formatting the string
*
* @returns {String|Promise.<String>} a string (if no context passed), or
* either a string or a Promise (if context passed).
*/
Simple.prototype.toString = function (cx) {
return this.encodeToString();
};
/**
* The string encoding of certain Simples may include Type information, or other data that may
* require asynchronous operations to decode. For instance, some Simples may function as
* "containers" for other Simples, and may include that Type information in the string encoding.
* That Simple would then need to import those Types before it could be fully decoded in the
* browser.
*
* baja.Facets is a great example of this. A Facets may contain FrozenEnum values that are
* defined by Types, such as `baja:Weekday`. For a Facets containing a Weekday to be fully
* constructed in the browser, the `baja:Weekday` Type must be imported first. Since importing
* a Type may require a network call, this Facets instance might not be able to be constructed
* synchronously, using only `decodeFromString()`. baja.Facets has itself implemented
* `decodeAsync()` to import any necessary Types.
*
* When implementing a Type Extension for a Simple, if your Simple references arbitrary Types that
* need to be imported when decoding your Simple, implement `decodeAsync()` and perform any Type
* imports (or other asynchronous operations) there.
*
* If you are writing code that decodes Simples from strings - that is, when you have a type spec
* and string encoding, and you need to be able to decode any kind of Simple - favor the use of
* `decodeAsync`, as it gives the individual Simple a chance to perform async operations to ensure
* that the decoded Simple is fully correct.
*
* The default implementation just returns `decodeFromString` directly.
*
* Prior to Niagara 4.14, `decodeAsync()` was only used in field editors. In 4.14 and later,
* `decodeAsync()` will be used by BajaScript itself to support asynchronous decoding of Simples
* when resolving ORDs and retrieving other data from the station.
*
* (Note: `decodeAsync()` cannot be used by the framework to decode frozen slots. If you
* have a frozen slot, whose definition is a Simple that would *require* the use of
* `decodeAsync()` to construct it, it will not work. The best approach would be to implement a
* Type Extension that would use the `baja!` plugin to preload all the types referenced by the
* default value of that frozen slot, so that `decodeFromString` would have all the information
* it needed to construct it synchronously.)
*
* @param {string} str
* @param {baja.comm.Batch} [batch] optional batch to use
* @returns {baja.Simple|Promise.<baja.Simple>} may return the Simple instance
* directly, or a Promise resolving to same - so wrap in `Promise.resolve()`
* if unsure.
* @example
* return Promise.resolve(baja.$(simpleTypeSpec).decodeAsync(stringEncoding))
* .then((simpleInstance) => {});
*/
Simple.prototype.decodeAsync = function (str, batch) {
return this.decodeFromString(str);
};
/**
* @returns {String} the string encoding of the Simple, by default
*/
Simple.prototype.valueOf = function () {
return this.encodeToString();
};
/**
* This flag is intended for internal use to be passed to decodeFromString in order to indicate
* that the synchronous decode is okay for simples that have a precondition such as all types
* are loaded.
* @private
*/
Simple.$unsafeDecode = { unsafe: true };
/**
* Every Simple implementation must have an `encodeToString` function.
*
* @function
* @name baja.Simple#encodeToString
* @abstract
* @returns {string}
*/
/**
* Every Simple implementation must have a `decodeFromString` function.
*
* @function
* @name baja.Simple#decodeFromString
* @abstract
* @param {string} str
* @returns {baja.Simple}
*/
/**
* Every Simple implementation must have a `make` function. It can take
* any arbitrary arguments. When constructing the Simple with `baja.$`, any
* additional arguments will be passed to `make()`, with the following special
* cases:
*
* - When _no_ arguments are passed, `baja.$` will return the `DEFAULT`
* instance.
* - When _one_ argument is passed and it is a string, `baja.$` will use
* `decodeFromString()` to create the instance.
*
* @function
* @name baja.Simple#make
* @abstract
* @returns {baja.Simple}
* @example
* //in the Simple declaration:
* MySimple.prototype.make = function (a, b, c) {
* var mySimple = new MySimple();
* mySimple.a = a;
* mySimple.b = b;
* mySimple.c = c;
* return mySimple;
* };
*
* //when constructing:
* var mySimple = baja.$('myModule:MySimple', 'a', 'b', 'c');
*/
return Simple;
});