Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --[[
- class.lua
- version: 2.05
- 2011-11-05
- update by Kernell (Multi Theft Auto adaptation, new syntax)
- ]]
- --[[
- class.lua
- version: 2.04
- 2009-05-20
- update by Setuper
- ]]
- -- PRIVATE
- --[[
- Define unique value for identifying ambiguous base objects and inherited
- attributes. Ambiguous values are normally removed from classes and objects,
- but if keep_ambiguous == true they are left there and the ambiguous value
- is made to behave in a way useful for debugging.
- ]]
- local ambiguous = { __type = 'ambiguous' };
- local remove_ambiguous;
- if keep_ambiguous then
- -- Make ambiguous complain about everything except tostring()
- local function invalid( operation )
- return function()
- error( 'Invalid ' .. operation .. ' on ambiguous' );
- end
- end
- local ambiguous_mt =
- {
- __add = invalid( 'addition' ),
- __sub = invalid( 'substraction' ),
- __mul = invalid( 'multiplication' ),
- __div = invalid( 'division' ),
- __mod = invalid( 'modulus operation' ),
- __pow = invalid( 'exponentiation' ),
- __unm = invalid( 'unary minus' ),
- __concat = invalid( 'concatenation' ),
- __len = invalid( 'length operation' ),
- __eq = invalid( 'equality comparison' ),
- __lt = invalid( 'less than' ),
- __le = invalid( 'less or equal' ),
- __index = invalid( 'indexing' ),
- __newindex = invalid( 'new indexing' ),
- __call = invalid( 'call' ),
- __tostring = function() return 'ambiguous' end,
- __tonumber = invalid( 'conversion to number' )
- };
- setmetatable( ambiguous, ambiguous_mt );
- -- Don't remove ambiguous values from classes and objects
- remove_ambiguous = function() end;
- else
- -- Remove ambiguous values from classes and objects
- remove_ambiguous = function( t )
- for k, v in pairs( t ) do
- if v == ambiguous then
- t[ k ] = nil;
- end
- end
- end
- end
- --[[
- Reserved attribute names.
- ]]
- local reserved =
- {
- __index = true;
- __newindex = true;
- __type = true;
- __class = true;
- __bases = true;
- __inherited = true;
- __from = true;
- __virtual = true;
- __user_init = true;
- __name = true;
- __initialized = true;
- }
- --[[
- Some special user-set attributes are renamed.
- ]]
- local rename =
- {
- __init = '__user_init';
- __set = '__user_set';
- __get = '__user_get';
- };
- --[[
- The metatable of all classes, containing:
- To be used by the classes:
- __call() for creating instances
- __init() default constructor
- is_a() for checking object and class types
- implements() for checking interface support
- For internal use:
- __newindex() for controlling class population
- ]]
- local class_mt = {};
- class_mt.__index = class_mt;
- --[[
- This controls class population.
- Here 'self' is a class being populated by inheritance or by the user.
- ]]
- function class_mt:__newindex( name, value )
- if rename[ name ] then name = rename[ name ] end -- Rename special user-set attributes
- if name == '__user_get' then -- __user_get() needs an __index() handler
- self.__index = value and function( obj, k )
- local v = self[ k ];
- if v == nil and not reserved[ k ] then
- v = value( obj, k );
- end
- return v;
- end or self;
- elseif name == '__user_set' then -- __user_set() needs a __newindex() handler
- self.__newindex = value and function( obj, k, v )
- if reserved[ k ] or not value( obj, k, v ) then
- rawset( obj, k, v );
- end
- end or nil;
- end
- rawset( self, name, value ); -- Assign the attribute
- end
- --[[
- This function creates an object of a certain class and calls itself
- recursively to create one child object for each base class. Base objects
- of unnamed base classes are accessed by using the base class as an index
- into the object, base objects of named base classes are accessed as fields
- of the object with the names of their respective base classes.
- Classes derived in virtual mode will create only a single base object.
- Unambiguous grandchildren are inherited by the parent if they do not
- collide with direct children.
- ]]
- local function build( class, virtual_objs, is_virtual )
- virtual_objs = virtual_objs or {};
- if is_virtual and virtual_objs[ class ] then
- return virtual_objs[ class ];
- end
- local obj = { __type = 'object' };
- local inherited = {}
- for i, base in ipairs( class.__bases ) do
- local child = build( base, virtual_objs, class.__virtual[ base ] );
- obj[ base.__name ] = child;
- for c, grandchild in pairs( child ) do
- if not inherited[c] then
- inherited[ c ] = grandchild;
- elseif inherited[ c ] ~= grandchild then
- inherited[ c ] = ambiguous;
- end
- end
- end
- for k, v in pairs( inherited ) do
- if not obj[ k ] then
- obj[ k ] = v;
- end
- end
- remove_ambiguous( obj );
- setmetatable( obj, class );
- if is_virtual then virtual_objs[ class ] = obj end
- return obj;
- end
- --[[
- The __call() operator creates an instance of the class and initializes it.
- ]]
- function class_mt:__call( ... )
- local this = build( self );
- this:__init( ... );
- return this;
- end
- function class_mt:__init( ...)
- if self.__initialized then
- return;
- end
- if self[ self.__name ] then
- self[ self.__name ]( self, ... );
- end
- for _, base in ipairs( self.__bases ) do
- self[ base.__name ]:__init( ... );
- end
- self.__initialized = true;
- end
- --[[
- The implements() method checks that an object or class supports the
- interface of a target class. This means it can be passed as an argument to
- any function that expects the target class. We consider only functions
- and callable objects to be part of the interface of a class.
- ]]
- function class_mt:implements( class )
- local function is_callable( v )
- if v == ambiguous then return false end
- if type( v ) == 'function' then return true end
- local mt = getmetatable( v );
- return mt and type( mt.__call ) == 'function';
- end
- for k, v in pairs( class ) do
- if not reserved[ k ] and is_callable( v ) and not is_callable( self[ k ] ) then
- return false;
- end
- end
- return true;
- end
- --[[
- The is_a() method checks the type of an object or class starting from
- its class and following the derivation chain upwards looking for
- the target class. If the target class is found, it checks that its
- interface is supported (this may fail in multiple inheritance because
- of ambiguities).
- ]]
- function class_mt:is_a( class )
- if self.__class == class then return true end
- local function find( target, classlist )
- for i, class in ipairs( classlist ) do
- if class == target or find( target, class.__bases ) then
- return true;
- end
- end
- return false;
- end
- if not find( class, self.__bases ) then return false end
- return self:implements( class );
- end
- -- PUBLIC
- --[[
- Utility type and interface checking functions
- ]]
- function typeof( value )
- return type( value ) =='table' and value.__type or type( value );
- end
- function classof( value )
- return type( value ) == 'table' and value.__class or nil;
- end
- function classname( value )
- if not classof( value ) then return nil end
- local name = value.__name;
- return type( name ) == 'string' and name or nil;
- end
- function implements( value, class )
- return classof( value ) and value:implements( class ) or false
- end
- function is_a( value, class )
- return classof( value ) and value:is_a( class ) or false
- end
- --[[
- Use a table to control class creation and naming.
- ]]
- class = {};
- local mt = {};
- setmetatable( class, mt );
- function mt:__newindex()
- return nil;
- end
- --[[
- Create a named or unnamed class by calling class([name, ] ...).
- Arguments are an optional string to set the class name and the classes or
- virtual classes to be derived from.
- ]]
- function mt:__call( name )
- _G[ name ] = make_class( self, name );
- return function( ... )
- _G[ name ] = make_class( self, name, ... );
- end
- end
- function make_class( this, ... )
- local arg, last = { ... }, {};
- local argn = #arg;
- if argn ~= 0 then
- local l = arg[ argn ];
- if typeof( l ) == "table" then
- last = l;
- table.remove( arg, argn );
- end
- end
- local c =
- {
- __type = 'class',
- __bases = {},
- __virtual = {}
- };
- c.__class = c;
- c.__index = c;
- if type( arg[ 1 ] ) == 'string' then
- c.__name = arg [ 1 ];
- table.remove( arg, 1 );
- else
- c.__name = tostring( c );
- end
- local inherited = {};
- local from = {};
- for i, base in ipairs( arg ) do
- local basetype = typeof( base );
- local is_virtual = basetype == 'virtual_class';
- assert( basetype == 'class' or is_virtual, 'Base ' .. i .. ' is not a class or virtual class' );
- if is_virtual then
- base = base.__classl;
- end
- assert( c.__virtual[ base ] == nil, 'Base ' .. i .. ' is duplicated' );
- c.__bases[ i ] = base;
- c.__virtual[ base ] = is_virtual;
- for k, v in pairs( base ) do
- if not reserved[k] and v ~= ambiguous and inherited[k] ~= ambiguous then
- local new_from;
- local base_inherited = base.__inherited[ k ];
- if base_inherited then
- if base_inherited ~= v then
- base.__inherited[ k ] = nil;
- base.__from[ k ] = nil;
- else
- new_from = base.__from[ k ];
- end
- end
- new_from = new_from or { class = base, virtual = is_virtual }
- local current_from = from[ k ];
- if not current_from then
- from[ k ] = new_from;
- if type( v ) == 'function' then
- local origin = new_from.class;
- inherited[ k ] = function( this, ... )
- return origin[ k ]( this[ origin.__name ], ... );
- end
- else
- inherited[ k ] = v;
- end
- elseif current_from.class ~= new_from.class or not current_from.virtual or not new_from.virtual then
- inherited[ k ] = ambiguous;
- from[ k ] = nil;
- end
- end
- end
- end
- remove_ambiguous( inherited );
- setmetatable( c, class_mt );
- for k, v in pairs( inherited ) do c[ k ] = v end
- c.__inherited = inherited;
- c.__from = from;
- for k, v in pairs( last ) do c[ k ] = v end
- return c;
- end
- function mt:__index( name )
- return function( ... )
- local c = class( name )( ... );
- _G[ name ] = c;
- return c;
- end
- end
- --[[
- Wrap a class for virtualization of the class.
- ]]
- function virtual( class )
- assert( typeof( class ) == 'class', 'Argument is not a class' );
- return { __type = 'virtual_class', __class = class };
- end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement