@qooxdoo/framework
Version:
The JS Framework for Coders
480 lines (387 loc) • 12.6 kB
JavaScript
/* ************************************************************************
qooxdoo - the new era of web development
http://qooxdoo.org
Copyright:
2004-2008 1&1 Internet AG, Germany, http://www.1und1.de
License:
MIT: https://opensource.org/licenses/MIT
See the LICENSE file in the project's top-level directory for details.
Authors:
* Sebastian Werner (wpbasti)
* Andreas Ecker (ecker)
* Fabian Jakobs (fjakobs)
************************************************************************ */
/**
* The qooxdoo root class. All other classes are direct or indirect subclasses of this one.
*
* This class contains methods for:
*
* * object management (creation and destruction)
* * interfaces for event system
* * generic setter/getter support
* * interfaces for logging console
* * user friendly OO interfaces like {@link #self} or {@link #base}
*
* @require(qx.core.ObjectRegistry)
*/
qx.Class.define("qx.core.Object",
{
extend : Object,
include : qx.core.Environment.filter({
"module.databinding" : qx.data.MBinding,
"module.logger" : qx.core.MLogging,
"module.events" : qx.core.MEvent,
"module.property" : qx.core.MProperty,
"module.objectid" : qx.core.MObjectId,
"qx.debug" : qx.core.MAssert
}),
/*
*****************************************************************************
CONSTRUCTOR
*****************************************************************************
*/
/**
* Create a new instance
*/
construct : function() {
if (!qx.core.Environment.get("qx.automaticMemoryManagement") || qx.Class.hasInterface(this.constructor, qx.core.IDisposable)) {
qx.core.ObjectRegistry.register(this);
} else {
qx.core.ObjectRegistry.toHashCode(this);
}
},
/*
*****************************************************************************
STATICS
*****************************************************************************
*/
statics :
{
/** Internal type */
$$type : "Object"
},
/*
*****************************************************************************
MEMBERS
*****************************************************************************
*/
members :
{
__Property : qx.core.Environment.get("module.property") ? qx.core.Property : null,
/*
---------------------------------------------------------------------------
BASICS
---------------------------------------------------------------------------
*/
/**
* Return unique hash code of object
*
* @return {Integer} unique hash code of the object
*/
toHashCode : function() {
return this.$$hash;
},
/**
* Returns a string representation of the qooxdoo object.
*
* @return {String} string representation of the object
*/
toString : function() {
return this.classname + "[" + this.$$hash + "]";
},
/**
* Call the same method of the super class.
*
* @param args {IArguments} the arguments variable of the calling method
* @param varargs {var?} variable number of arguments passed to the overwritten function
* @return {var} the return value of the method of the base class.
*/
base : function(args, varargs)
{
if (qx.core.Environment.get("qx.debug"))
{
if (!qx.Bootstrap.isFunctionOrAsyncFunction(args.callee.base)) {
throw new Error(
"Cannot call super class. Method is not derived: " +
args.callee.displayName
);
}
}
if (arguments.length === 1) {
return args.callee.base.call(this);
} else {
return args.callee.base.apply(this, Array.prototype.slice.call(arguments, 1));
}
},
/**
* Returns the static class (to access static members of this class)
*
* @param args {arguments} the arguments variable of the calling method
* @return {var} the return value of the method of the base class.
*/
self : function(args) {
return args.callee.self;
},
/*
---------------------------------------------------------------------------
CLONE SUPPORT
---------------------------------------------------------------------------
*/
/**
* EXPERIMENTAL - NOT READY FOR PRODUCTION
*
* Returns a clone of this object. Copies over all user configured
* property values. Do not configure a parent nor apply the appearance
* styles directly.
*
* @return {qx.core.Object} The clone
*/
clone : function()
{
if (!qx.core.Environment.get("module.property")) {
throw new Error("Cloning only possible with properties.");
}
var clazz = this.constructor;
var clone = new clazz;
var props = qx.Class.getProperties(clazz);
var user = this.__Property.$$store.user;
var setter = this.__Property.$$method.set;
var name;
// Iterate through properties
for (var i=0, l=props.length; i<l; i++)
{
name = props[i];
if (this.hasOwnProperty(user[name])) {
clone[setter[name]](this[user[name]]);
}
}
// Return clone
return clone;
},
/*
---------------------------------------------------------------------------
USER DATA
---------------------------------------------------------------------------
*/
/** @type {Map} stored user data */
__userData : null,
/**
* Store user defined data inside the object.
*
* @param key {String} the key
* @param value {Object} the value of the user data
*/
setUserData : function(key, value)
{
if (!this.__userData) {
this.__userData = {};
}
this.__userData[key] = value;
},
/**
* Load user defined data from the object
*
* @param key {String} the key
* @return {Object} the user data
*/
getUserData : function(key)
{
if (!this.__userData) {
return null;
}
var data = this.__userData[key];
return data === undefined ? null : data;
},
/*
---------------------------------------------------------------------------
DISPOSER
---------------------------------------------------------------------------
*/
/**
* Returns true if the object is disposed.
*
* @return {Boolean} Whether the object has been disposed
*/
isDisposed : function() {
return this.$$disposed || false;
},
/**
* Returns true if the object is being disposed, ie this.dispose() has started but
* not finished
*
* @return {Boolean} Whether the object is being disposed
*/
isDisposing : function() {
return this.$$disposing || false;
},
/**
* Dispose this object
*
*/
dispose : function()
{
// Check first
if (this.$$disposed) {
return;
}
// Mark as disposed (directly, not at end, to omit recursions)
this.$$disposed = true;
this.$$disposing = true;
this.$$instance = null;
this.$$allowconstruct = null;
// Debug output
if (qx.core.Environment.get("qx.debug"))
{
if (qx.core.Environment.get("qx.debug.dispose.level") > 2) {
qx.Bootstrap.debug(this, "Disposing " + this.classname + "[" + this.toHashCode() + "]");
}
}
// Deconstructor support for classes
var clazz = this.constructor;
var mixins;
while (clazz.superclass)
{
// Processing this class...
if (clazz.$$destructor) {
clazz.$$destructor.call(this);
}
// Destructor support for mixins
if (clazz.$$includes)
{
mixins = clazz.$$flatIncludes;
for (var i=0, l=mixins.length; i<l; i++)
{
if (mixins[i].$$destructor) {
mixins[i].$$destructor.call(this);
}
}
}
// Jump up to next super class
clazz = clazz.superclass;
}
this.$$disposing = false;
// Additional checks
if (qx.core.Environment.get("qx.debug"))
{
if (qx.core.Environment.get("qx.debug.dispose.level") > 0)
{
var key, value;
for (key in this)
{
value = this[key];
// Check for Objects but respect values attached to the prototype itself
if (value !== null && typeof value === "object" && !(qx.Bootstrap.isString(value)))
{
// Check prototype value
// undefined is the best, but null may be used as a placeholder for
// private variables (hint: checks in qx.Class.define). We accept both.
if (this.constructor.prototype[key] != null) {
continue;
}
if (qx.core.Environment.get("qx.debug.dispose.level") > 1) {
qx.Bootstrap.warn(this, "Missing destruct definition for '" + key + "' in " + this.classname + "[" + this.toHashCode() + "]: " + value);
delete this[key];
}
}
}
}
}
},
/*
---------------------------------------------------------------------------
DISPOSER UTILITIES
---------------------------------------------------------------------------
*/
/**
* Disconnects and disposes given objects from instance.
* Only works with qx.core.Object based objects e.g. Widgets.
*
* @param varargs {arguments} Names of fields (which store objects) to dispose
*/
_disposeObjects : function(varargs) {
qx.util.DisposeUtil.disposeObjects(this, arguments);
},
/**
* Disconnects and disposes given singleton objects from instance.
* Only works with qx.core.Object based objects e.g. Widgets.
*
* @param varargs {arguments} Names of fields (which store objects) to dispose
*/
_disposeSingletonObjects : function(varargs) {
qx.util.DisposeUtil.disposeObjects(this, arguments, true);
},
/**
* Disposes all members of the given array and deletes
* the field which refers to the array afterwards.
*
* @param field {String} Name of the field which refers to the array
*/
_disposeArray : function(field) {
qx.util.DisposeUtil.disposeArray(this, field);
},
/**
* Disposes all members of the given map and deletes
* the field which refers to the map afterwards.
*
* @param field {String} Name of the field which refers to the map
*/
_disposeMap : function(field) {
qx.util.DisposeUtil.disposeMap(this, field);
}
},
/*
*****************************************************************************
ENVIRONMENT SETTINGS
*****************************************************************************
*/
environment : {
"qx.debug.dispose.level" : 0
},
/*
*****************************************************************************
DESTRUCTOR
*****************************************************************************
*/
destruct : function()
{
if (qx.core.Environment.get("module.events")) {
if (!qx.core.ObjectRegistry.inShutDown) {
// Cleanup event listeners
qx.event.Registration.removeAllListeners(this);
} else {
// on shutdown, just clear the internal listener map
qx.event.Registration.deleteAllListeners(this);
}
}
// Cleanup object registry
qx.core.ObjectRegistry.unregister(this);
// Cleanup user data
this.__userData = null;
// only of properties are available
if (qx.core.Environment.get("module.property")) {
// Cleanup properties
var clazz = this.constructor;
var properties;
var store = this.__Property.$$store;
var storeUser = store.user;
var storeTheme = store.theme;
var storeInherit = store.inherit;
var storeUseinit = store.useinit;
var storeInit = store.init;
while(clazz)
{
properties = clazz.$$properties;
if (properties)
{
for (var name in properties)
{
if (properties[name].dereference) {
this[storeUser[name]] = this[storeTheme[name]] = this[storeInherit[name]] = this[storeUseinit[name]] = this[storeInit[name]] = undefined;
}
}
}
clazz = clazz.superclass;
}
}
}
});