All Downloads are FREE. Search and download functionalities are using the official Maven repository.

rwt.qx.Class.js Maven / Gradle / Ivy

/*******************************************************************************
 * Copyright (c) 2004, 2022 1&1 Internet AG, Germany, http://www.1und1.de,
 *                          EclipseSource, and others.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    1&1 Internet AG and others - original API and implementation
 *    EclipseSource - adaptation for the Eclipse Remote Application Platform
 ******************************************************************************/

/*global alert:false */

namespace( "rwt.qx" );

/**
 * Each instance of a class defined by {@link #define} has the following keys attached to the
 * constructor and the prototype:
 *
 * - classname - The fully-qualified name of the class (e.g. "qx.ui.core.Widget")
 * - basename - The namespace part of the class name (e.g. "qx.ui.core")
 * - constructor - A reference to the constructor of the class
 * - superclass - A reference to the constructor of the super class
 *
 * Each method may access static members of the same class by using `this.self(arguments)`
 * ({@link rwt.qx.Object#self}):
 * 
 * statics : { FOO : "bar" },
 * members: {
 *   baz: function(x) {
 *     this.self(arguments).FOO;
 *     ...
 *   }
 * }
 * 
* * Each overriding method may call the overridden method by using * this.base(arguments [, ...]) ({@link rwt.qx.Object#base}). This is also true for * calling the constructor of the superclass. *
 * members: {
 *   foo: function(x) {
 *     this.base(arguments, x);
 *     ...
 *   }
 * }
 * 
*/ rwt.qx.Class = { _normalizeConfig : function( config ) { if( !config ) { config = {}; } if( config.include && !( config.include instanceof Array ) ) { config.include = [ config.include ]; } return config; }, /** * Define a new class using the qooxdoo class system. This sets up the * namespace for the class and generates the class from the definition map. * * Example: *
   * rwt.qx.Class.define( "name", {
   *   extend : Object, // superclass
   *   include : [Mixins],
   *
   *   statics: {
   *     CONSTANT : 3.141,
   *
   *     publicMethod: function() {},
   *     _protectedMethod: function() {},
   *     __privateMethod: function() {}
   *   },
   *
   *   properties: {
   *     "tabIndexOld": { type: "number", defaultValue : -1 }
   *     "tabIndex": { check: "Number", init : -1 }
   *   },
   *
   *   members: {
   *     publicField: "foo",
   *     publicMethod: function() {},
   *
   *     _protectedField: "bar",
   *     _protectedMethod: function() {},
   *
   *     __privateField: "baz",
   *     __privateMethod: function() {}
   *   }
   * } );
   * 
* * @param name {String} Name of the class * @param config {Map ? null} Class definition structure. The configuration map has the * following keys: * - extend {Class}: The super class the current class inherits from. * - include {Mixin | Mixin[]}: Single mixin or array of mixins, which will be merged into the * class. * - construct {Function} The constructor of the class. * - statics {Map} Map of static members of the class. * - properties {Map} Map of property definitions. For a description of the format of a * property definition see {@link rwt.qx.Property} * - members {Map}: Map of instance members of the class. * - events {Map}: Map of events the class fires. The keys are the names of the events and the * values are the corresponding event type class names. * - defer {Function}: Function that is called at the end of processing the class * declaration. It allows access to the declared statics, members and properties. * - destruct {Function}: The destructor of the class. */ define : function( name, config ) { if( this._stopLoading ) { throw new Error( "Stop loading " + name ); } try { config = this._normalizeConfig( config ); var clazz; if( !config.extend ) { clazz = config.statics || {}; } else { if( !config.construct ) { config.construct = this.__createDefaultConstructor(); } clazz = this.__wrapConstructor( config.construct, name ); if( config.statics ) { var key; for( var i = 0, a = rwt.util.Objects.getKeys( config.statics ), l = a.length; i < l; i++ ) { key = a[ i ]; clazz[ key ] = config.statics[ key ]; } } } var basename = rwt.define( name, clazz, false ); clazz.name = clazz.classname = name; clazz.basename = basename; this.__registry[ name ] = clazz; // Attach toString if( !clazz.hasOwnProperty( "toString" ) ) { clazz.toString = this.genericToString; } if( config.extend ) { var Helper = function() {}; Helper.prototype = config.extend.prototype; var proto = new Helper(); clazz.prototype = proto; proto.name = proto.classname = name; proto.basename = basename; config.construct.base = clazz.superclass = config.extend; config.construct.self = proto.constructor = clazz; if( config.destruct ) { clazz.$$destructor = config.destruct; } var that = this; clazz.$$initializer = function() { if( config.properties ) { that.__addProperties( clazz, config.properties, true ); } if( config.members ) { that.__addMembers( clazz, config.members, true, true, false ); } if( config.events ) { that.__addEvents( clazz, config.events, true ); } if( config.include ) { for( var i = 0, l = config.include.length; i < l; i++ ) { that.__addMixin( clazz, config.include[ i ], false ); } } }; } if( config.defer ) { this.__initializeClass( clazz ); config.defer.self = clazz; config.defer( clazz, clazz.prototype, { add : function( name, config ) { var properties = {}; properties[ name ] = config; rwt.qx.Class.__addProperties( clazz, properties, true ); } } ); } } catch( ex ) { // Use alert here since ErrorHandler.js might not be parsed yet. In case of a class loader // error, this is the only way to be sure the user sees the message. alert( "Error loading class " + name + ": " + ( ex.message ? ex.message : ex ) ); this._stopLoading = true; throw ex; } }, /** * Whether the given class exists * * @param name {String} class name to check * @return {Boolean} true if class exists */ isDefined : function( name ) { return this.__registry[ name ] !== undefined; }, /** * Find a class by its name * * @type static * @param name {String} class name to resolve * @return {Class} the class */ getByName : function( name ) { return this.__registry[ name ]; }, /** * Include all features of the given mixin into the class. The mixin must * not include any methods or properties that are already available in the * class. This would only be possible using the {@link #patch} method. * * @param clazz {Class} An existing class which should be modified by including the mixin. * @param mixin {Mixin} The mixin to be included. */ include : function( clazz, mixin ) { rwt.qx.Class.__addMixin( clazz, mixin, false ); }, /** * Include all features of the given mixin into the class. The mixin may * include features which are already defined in the target class. Existing * features of equal name will be overwritten. * Please keep in mind that this functionality is not intented for regular * use, but as a formalized way (and a last resort) in order to patch * existing classes. * * WARNING: You may break working classes and features. * * @param clazz {Class} An existing class which should be modified by including the mixin. * @param mixin {Mixin} The mixin to be included. */ patch : function( clazz, mixin ) { rwt.qx.Class.__addMixin( clazz, mixin, true ); }, /** * This method will be attached to all classes to return * a nice identifier for them. * * @internal * @return {String} The class identifier */ genericToString : function() { return "[Class " + this.classname + "]"; }, /** Stores all defined classes */ __registry : {}, /** * Attach events to the class * * @param clazz {Class} class to add the events to * @param events {Map} map of event names the class fires. * @param patch {Boolean ? false} Enable redefinition of event type? */ __addEvents : function( clazz, events ) { if( clazz.$$events ) { for( var key in events ) { clazz.$$events[ key ] = events[ key ]; } } else { clazz.$$events = events; } }, /** * Attach properties to classes * * @param clazz {Class} class to add the properties to * @param properties {Map} map of properties * @param patch {Boolean ? false} Overwrite property with the limitations of a property * which means you are able to refine but not to replace (esp. for new properties) */ __addProperties : function( clazz, properties, patch ) { var config; if( patch === undefined ) { patch = false; } var attach = !!clazz.$$propertiesAttached; for( var name in properties ) { config = properties[ name ]; // Store name into configuration config.name = name; // Add config to local registry if( !config.refine ) { if( clazz.$$properties === undefined ) { clazz.$$properties = {}; } clazz.$$properties[ name ] = config; } // Store init value to prototype. This makes it possible to // overwrite this value in derived classes. if( config.init !== undefined ) { clazz.prototype[ "__init$" + name ] = config.init; } // register event name if( config.event !== undefined ) { var event = {}; event[ config.event ] = "rwt.event.ChangeEvent"; this.__addEvents( clazz, event, patch ); } // Remember inheritable properties if( config.inheritable ) { rwt.qx.Property.$$inheritable[ name ] = true; } // If instances of this class were already created, we // need to attach the new style properties functions, directly. if( attach ) { rwt.qx.Property.attachMethods( clazz, name, config ); } // Create old style properties if( config._fast ) { rwt.qx.LegacyProperty.addFastProperty( config, clazz.prototype ); } else if( config._cached ) { rwt.qx.LegacyProperty.addCachedProperty( config, clazz.prototype ); } } }, /** * Attach members to a class * * @param clazz {Class} clazz to add members to * @param members {Map} The map of members to attach * @param patch {Boolean ? false} Enable patching of * @param base (Boolean ? true) Attach base flag to mark function as members * of this class * @param wrap {Boolean ? false} Whether the member method should be wrapped. * this is needed to allow base calls in patched mixin members. */ __addMembers : function( clazz, members, patch, base, wrap ) { var proto = clazz.prototype; var key, member; for( var i = 0, a = rwt.util.Objects.getKeys( members ), l = a.length; i < l; i++ ) { key = a[ i ]; member = members[ key ]; // Added helper stuff to functions // Hint: Could not use typeof function because RegExp objects are functions, too if( base !== false && member instanceof Function ) { if( wrap === true ) { // wrap "patched" mixin member member = this.__mixinMemberWrapper( member, proto[ key ] ); } else { // Configure extend (named base here) // Hint: proto[key] is not yet overwritten here if( proto[ key ] ) { member.base = proto[ key ]; } member.self = clazz; } } // Attach member proto[ key ] = member; } }, /** * Wraps a member function of a mixin, which is included using "patch". This * allows "base" calls in the mixin member function. * * @param member {Function} The mixin method to wrap * @param base {Function} The overwritten method * @return {Function} the wrapped mixin member */ __mixinMemberWrapper : function( member, base ) { if( base ) { return function() { var oldBase = member.base; member.base = base; var retval = member.apply( this, arguments ); member.base = oldBase; return retval; }; } else { return member; } }, /** * Include all features of the mixin into the given class (recursive). * * @param clazz {Class} A class previously defined where the mixin should be attached. * @param mixin {Mixin} Include all features of this mixin * @param patch {Boolean} Overwrite existing fields, functions and properties */ __addMixin : function( clazz, mixin, patch ) { if( mixin.$$properties ) { this.__addProperties( clazz, mixin.$$properties, patch ); } if( mixin.$$members ) { this.__addMembers( clazz, mixin.$$members, patch, patch, patch ); } if( clazz.$$includes ) { clazz.$$includes.push( mixin ); } else { clazz.$$includes = [ mixin ]; } }, /** * Returns the default constructor. * This constructor just calles the constructor of the base class. * * @return {Function} The default constructor. */ __createDefaultConstructor : function() { function defaultConstructor() { arguments.callee.base.apply( this, arguments ); } return defaultConstructor; }, __initializeClass : function( clazz ) { if( clazz.$$initializer ) { var inits = []; var target = clazz; while( target.$$initializer ) { inits.push( target ); target = target.superclass; } while( inits.length > 0 ) { target = inits.pop(); target.$$initializer(); delete target.$$initializer; } } }, /** * Generate a wrapper of the original class constructor in order to enable * some of the advanced OO features (e.g. abstract class, singleton, mixins) * * @param original {Function} the original constructor * @param name {String} name of the class */ __wrapConstructor : function( original ) { return function() { var clazz = arguments.callee; // i.e. "wrapper" rwt.qx.Class.__initializeClass( clazz ); if( !clazz.$$propertiesAttached ) { rwt.qx.Property.attach( clazz ); } original.apply( this, arguments ); if( clazz.$$includes ) { for( var i = 0, l = clazz.$$includes.length; i < l; i++ ) { if( clazz.$$includes[ i ].$$constructor ) { clazz.$$includes[ i ].$$constructor.apply( this, arguments ); } } } }; } };




© 2015 - 2025 Weber Informatics LLC | Privacy Policy