/*
** Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
**
**34567890123456789012345678901234567890123456789012345678901234567890123456789
*/
/*jslint browser: true*/
/*global define: false,goog: true*/
/**
* Base class of all OJET Objects.
* <p>
* To create a subclass of another oj.Object, use oj.Object.createSubclass.
* The subclass can specify class-level initialization by implementing an
* <code>InitClass()</code> method on its constructor. <code>InitClass</code>
* is guaranteed to be called only once per class. Further, a class'
* <code>InitClass</code> method is guranteed to be called only after its
* superclass' class initialization has been called. When <code>InitClass</code>
* is called, <code>this</code> is the class' constructor. This allows class
* initialization implementations to be shared in some cases.
* </p>
* @author Blake Sullivan
*/
/**
* @constructor
* @export
*/
oj.Object = function()
{
this.Init();
};
oj.Object.superclass = null;
/**
* @private
*/
oj.Object._typeName = "oj.Object";
// regular expressicloneon for stripping out the name of a function
/**
* @private
*/
oj.Object._GET_FUNCTION_NAME_REGEXP = /function\s+([\w\$][\w\$\d]*)\s*\(/;
// oj.Object._TRIM_REGEXP = /(^\s*)|(\s*$)/g; this.replace(/(^\s*)|(\s*$)/g, "");
oj.Object.prototype = {};
oj.Object.prototype.constructor = oj.Object;
/**
* Calls to this method are added by the Closure Compiler pass during JET's build process.
* It should never be called by the Application code
*
* The method delegates to goog.exportProperty() for exporting a symbol with Closure compiler,
* while recording a map of the renamed names to an original names and a map of original names to the renamed names
* @param {string} name - name of the property ('CCCC.prototype.FFFF' is expected)
* @param {Object} valueMapping - a name-value pair, where tke key is the renamed name (renamed FFFF), and the value is the refernce to the member function
* whose name was exported
* @ignore
*/
oj.Object.exportPrototypeSymbol = function(name, valueMapping)
{
var renamed = null;
var val = null, prop;
for (prop in valueMapping)
{
if (valueMapping.hasOwnProperty(prop)) {
renamed = prop;
val = valueMapping[prop];
break;
}
}
var tokens = name.split('.');
var constructor = oj[tokens[0]];
var original = tokens[2];
// Do nothing if we are exporting a function that has not been renamed
if (renamed == original || renamed == null)
{
return;
}
var renameMap = constructor._r2o;
if (!renameMap)
{
renameMap = {};
constructor._r2o = renameMap;
}
renameMap[renamed] = original;
goog.exportProperty(constructor.prototype, original, val);
};
/**
* Creates a subclass of a baseClass
* @param {Function} extendingClass The class to extend from the base class
* @param {Function} baseClass class to make the superclass of extendingClass
* @param {string=} typeName to use for new class. If not specified, the typeName will be extracted from the
* baseClass's function if possible
* @export
*/
oj.Object.createSubclass = function(
extendingClass,
baseClass,
typeName) // optional name to name this class
{
oj.Assert.assertFunction(extendingClass);
oj.Assert.assertFunctionOrNull(baseClass);
oj.Assert.assertStringOrNull(typeName);
if (baseClass === undefined)
{
// assume oj.Object
baseClass = oj.Object;
}
oj.Assert.assert(extendingClass !== baseClass, "Class can't extend itself");
// use a temporary constructor to get our superclass as our prototype
// without out having to initialize the superclass
/**
* @private
* @constructor
*/
var TempConstructor = oj.Object._tempSubclassConstructor;
TempConstructor.prototype = baseClass.prototype;
extendingClass.prototype = new TempConstructor();
extendingClass.prototype.constructor = extendingClass;
extendingClass.superclass = extendingClass["superclass"] = baseClass.prototype;
if (typeName) {
extendingClass._typeName = typeName;
}
};
/**
* Copies properties from the source object to the prototype of the target class
* Only properties 'owned' by the source object will be copied, i.e. the properties
* from the source object's prototype chain will not be included.
* To copy properties from another class with methods defined on the prototype, pass
* otherClass.prototype as the source.
* @param {Function} targetClass - the function whose prototype will be used a
* copy target
* @param {Object} source - object whose properties will be copied
* @export
*/
oj.Object.copyPropertiesForClass = function(targetClass, source)
{
var prop;
oj.Assert.assertFunction(targetClass);
oj.Assert.assert(source != null, "source object cannot be null");
for(prop in source)
{
if(source.hasOwnProperty(prop))
{
targetClass.prototype[prop] = source[prop];
}
}
};
/**
* @private
*/
oj.Object._tempSubclassConstructor = function(){};
/**
* Returns the class object for the instance
* @param {Object=} otherInstance - if specified, the instance whose type
* should be returned. Otherwise the type if this instance will be returned
* @return {Function} the class object for the instance
* @final
* @export
*/
oj.Object.prototype.getClass = function(
otherInstance)
{
if (otherInstance === undefined) {
otherInstance = this;
}
else if (otherInstance === null)
{
return null;
}
return otherInstance["constructor"];
};
/**
* Returns a clone of this object. The default implementation is a shallow
* copy. Subclassers can override this method to implement a deep copy.
* @return {Object} a clone of this object
* @export
*/
oj.Object.prototype.clone = function()
{
var clone = new this.constructor();
oj.CollectionUtils.copyInto(clone, this);
return clone;
};
/**
* @export
*/
oj.Object.prototype.toString = function()
{
return this.toDebugString();
};
/**
* @export
*/
oj.Object.prototype.toDebugString = function()
{
return this.getTypeName() + " Object";
};
/**
* Returns the type name for a class derived from oj.Object
* @param {Function!|null} clazz Class to get the name of
* @return {String} name of the Class
* @export
*/
oj.Object.getTypeName = function(clazz)
{
oj.Assert.assertFunction(clazz);
var typeName = clazz._typeName, constructorText, matches;
if (typeName == null)
{
constructorText = clazz.toString();
matches = oj.Object._GET_FUNCTION_NAME_REGEXP.exec(constructorText);
if (matches)
{
typeName = matches[1];
}
else
{
typeName = "anonymous";
}
// cache the result on the function
clazz._typeName = typeName;
}
return typeName;
};
/**
* Returns the type name for this instance
* @return {String} name of the Class
* @final
* @export
*/
oj.Object.prototype.getTypeName = function()
{
return oj.Object.getTypeName(this.constructor);
};
/**
* Initializes the instance. Subclasses of oj.Object must call
* their superclass' Init
* @export
*/
oj.Object.prototype.Init = function()
{
if (oj.Assert.isDebug()) {
oj.Assert.assert(this["getTypeName"], "Not an oj.Object");
}
// do any class initialization. This code is duplicated from
// oj.Object.ensureClassInitialization()
var currClass = this.constructor;
if (!currClass._initialized) {
oj.Object._initClasses(currClass);
}
};
/**
* Ensures that a class is initialized. Although class initialization occurs
* by default the first time that an instance of a class is created, classes that
* use static factory methods to create their instances may
* still need to ensure that their class has been initialized when the factory
* method is called.
*
* @param {Function} clazz The class to ensure initialization of
* @export
*/
oj.Object.ensureClassInitialization = function(clazz)
{
oj.Assert.assertFunction(clazz);
if (!clazz._initialized) {
oj.Object._initClasses(clazz);
}
};
/**
* Indicates whether some other oj.Object is "equal to" this one.
* Method is equivalent to java ".equals()" method.
* @param {Object} object - comparison target
* @return {boolean} true if if the comparison target is equal to this object, false otherwise
* @export
*/
oj.Object.prototype.equals = function(
object)
{
return this === object;
};
/**
* Binds the supplied callback function to an object
* @param {Object!} obj - object that will be available to the supplied callback
* function as 'this'
* @param {Function!} func - the original callback
* @return {Function} a function that will be invoking the original callback with
* 'this' object assigned to obj
* @export
*/
oj.Object.createCallback = function(obj, func)
{
oj.Assert.assertFunction(func);
// All browsers supported by JET support bind() method
return func.bind(obj);
};
/**
* @private
*/
oj.Object._initClasses = function(currClass)
{
if (oj.Assert.isDebug())
{
oj.Assert.assertFunction(currClass);
oj.Assert.assert(!currClass._initialized);
}
currClass._initialized = true;
var superclass = currClass.superclass, superclassConstructor, typeName, InitClassFunc;
// initialize the superclass if necessary
if (superclass)
{
superclassConstructor = superclass.constructor;
if (superclassConstructor && !superclassConstructor._initialized) {
oj.Object._initClasses(superclassConstructor);
}
oj.Object._applyRenamesToSubclass(currClass);
}
// if the class has an initialization function, call it
InitClassFunc = currClass["InitClass"] || null;
// Check for the quoted name in case InitClass is renamed by Closure compiler
if (!InitClassFunc)
{
InitClassFunc = currClass["InitClass"];
}
if (InitClassFunc)
{
InitClassFunc.call(currClass);
}
};
/**
* Compares 2 values using strict equality except for the case of
* <ol>
* <li> Array [order matters]; will traverse through the arrays and compare oj.Object.compareValues(array[i], array2[i]) </li>
* <li> Instances that support valueOf [i.e. Boolean, String, Number, Date, and etc] will be compared by usage of that function </li>
* </ol>
*
* @public
* @export
*/
oj.Object.compareValues = function (obj1, obj2)
{
if (obj1 === obj2)
{
return true;
}
var obj1Type = typeof obj1,
obj2Type = typeof obj2;
if (obj1Type !== obj2Type)
{
//of different type so consider them unequal
return false;
}
//At this point means the types are equal
//note that if the operand is an array or a null then typeof is an object
//check if either is null and if so return false [i.e. case where one might be a null and another an object]
//and one wishes to avoid the null pointer in the following checks. Note that null === null has been already tested
if(obj1 === null || obj2 === null)
{
return false;
}
//now check for constructor since I think by here one has ruled out primitive values and if the constructors
//aren't equal then return false
if(obj1.constructor === obj2.constructor)
{
//these are special cases and will need to be modded on a need to have basis
if(Array.isArray(obj1))
{
return oj.Object._compareArrayValues(obj1, obj2);
}
else if(obj1.constructor === Object)
{
//for now invoke innerEquals and in the future if there are issues then resolve them
return oj.Object.__innerEquals(obj1, obj2);
}
else if(obj1["valueOf"] && typeof obj1["valueOf"] === "function")
{
//test cases for Boolean, String, Number, Date
//Note if some future JavaScript constructors
//do not impl it then it's their fault
return obj1.valueOf() === obj2.valueOf();
}
}
return false;
};
oj.Object._compareArrayValues = function (array1, array2)
{
if (array1.length !== array2.length)
{
return false;
}
for (var i = 0, j = array1.length;i < j;i++)
{
//recurse on each of the values, order does matter for our case since do not wish to search
//for the value [expensive]
if (!oj.Object.compareValues(array1[i], array2[i]))
{
return false;
}
}
return true;
}
//Shadow comparion of two arrays, order needn't be same, but no duplicates
oj.Object._compareSet = function (array1, array2)
{
//null and [] are equals
if (!array1)
return (!array2 || array2.length == 0);
if (!array2)
return (!array1 || array1.length == 0);
if (array1.length != array2.length)
return false;
for (var i = 0; i < array1.length; i++)
{
if ((array1[i] != array2[i]) &&
((array1.indexOf(array2[i]) == -1 ||
array2.indexOf(array1[i]) == -1)))
return false;
}
return true;
}
oj.Object.__innerEquals = function (obj1, obj2) {
var prop, hasProperties = false;
if (obj1 === obj2) {
return true;
}
if (!(obj1 instanceof Object) || !(obj2 instanceof Object)) {
return false;
}
if (obj1.constructor !== obj2.constructor)
{
return false;
}
for (prop in obj1)
{
if (!hasProperties)
{
hasProperties = true;
}
if (obj1.hasOwnProperty(prop)) {
if (!obj2.hasOwnProperty(prop))
{
return false;
}
if (obj1[prop] !== obj2[prop])
{
if (typeof(obj1[prop]) !== 'object') {
return false;
}
if (!oj.Object.__innerEquals(obj1[prop], obj2[prop]))
{
return false;
}
}
}
}
for (prop in obj2)
{
if (!hasProperties)
{
hasProperties = true;
}
if (obj2.hasOwnProperty(prop) && !obj1.hasOwnProperty(prop)) {
return false;
}
}
if (!hasProperties)
{
// we are dealing with objects that have no properties like Number or Date.
return JSON.stringify(obj1) === JSON.stringify(obj2);
}
return true;
};
oj.Object.isEmpty = function(object) {
var prop;
// Test if an object is empty
if (object === undefined || object === null) {
return true;
}
for (prop in object) {
if (object.hasOwnProperty(prop)) {
return false;
}
}
return true;
};
/**
* @private
*/
oj.Object._applyRenamesToSubclass = function (currClass)
{
// Check whether any renames actually happened
if (!oj.Object._r2o)
{
return;
}
var ancestor = currClass.superclass;
oj.Object._applyRenamesFromChain(currClass, ancestor);
};
/**
* @private
*/
oj.Object._applyRenamesFromChain = function(currClass, superclass)
{
if (!superclass)
{
return;
}
var ancestor = superclass.constructor;
//Recurse up the inheritance chain first
oj.Object._applyRenamesFromChain(currClass, ancestor.superclass);
var renameMap = ancestor._r2o, alias;
if (renameMap)
{
for (alias in renameMap)
{
if (renameMap.hasOwnProperty(alias)) {
var orig = renameMap[alias];
if (alias != orig)
{
var prot = currClass.prototype;
if (!prot.hasOwnProperty(alias) && prot.hasOwnProperty(orig))
{
prot[alias] = prot[orig];
}
else if(!prot.hasOwnProperty(orig) && prot.hasOwnProperty(alias))
{
prot[orig] = prot[alias];
}
}
}
}
}
};
/**
* @private
* @return {boolean} true if AMD Loader (such as Require.js) is present,
* false otherwise
*/
oj.__isAmdLoaderPresent = function()
{
return (typeof define === 'function' && define['amd']);
};
Source: src/main/javascript/oracle/oj/ojcore/Object.js
Oracle® JavaScript Extension Toolkit (JET)
1.1.2
E65298-01