;+
; Project : HESSI
;
; Name : ADD_METHOD
;
; Purpose : Dynamically add a method to a class (and it's objects)
;
; Category : utility objects
;
; Explanation : Based on method name (e.g. CLONE), looks for a class
; definition file (e.g. CLONE__DEFINE.PRO). It then makes a
; temporary copy of this file, and replaces all instances
; of CLASS::METHOD with OBJ::METHOD where OBJ is the
; class name that you want to add METHOD to. It then
; compiles this temporary file.
;
; Syntax : IDL> add_method,method,object
;
; Examples : IDL> add_method,'clone',map_object
; or
; IDL> add_method,'gen::getprop',map_object
;
; Inputs : METHOD = string method name
; : OBJECT = object reference (or class name)
;
; Outputs : None
;
; Keywords : ERR = error string
; VERBOSE = set for output messages
;
; Restrictions: METHOD must have a METHOD__DEFINE.PRO in !path
; If user enters method such as, for example, GEN::GETPROP
; the method name is the part after the ::
; If :: is not present, then the input is assumed to be a
; class and all it's methods are added.
; Added method(s) will only apply during current IDL session
;
; Side Effects: New method will override existing method of target object
;
; History : Written: 9 July 2000, D. Zarro (EIT/GSFC)
; Modified: 24 August 2000, Zarro (EIT/GSFC) - vectorized
; Modified: 3 Sept 2001, Zarro (EITI/GSFC) - added option
; to add specific method via: class::method_name
; Modified: 4 May 2002, Zarro (EER/GSFC) - switched to
; using CD instead of adding temporary path when using
; RESOLVE_ROUTINE. IDL 6.0 has a problem with this.
;
; Contact : dzarro@solar.stanford.edu
;-
pro add_method,method,object,err=err,verbose=verbose,override=override
err=''
verbose=keyword_set(verbose)
;-- input checks
syntax='add_method,method,object'
if size(method,/tname) ne 'STRING' then begin
err='First input argument must be a string method name'
message,err,/cont
pr_syntax,syntax
return
endif
;--check method input
method=trim(method)
cc_pos=strpos(method,'::')
all_methods=cc_pos eq -1
if all_methods then begin
method_name=trim(method)
method_class=trim(method)
endif else begin
method_name=strmid(method,cc_pos+2,strlen(method))
method_class=strmid(method,0,cc_pos)
endelse
;-- check for METHOD__DEFINE
method_define=method_class+'__define.pro'
if (not have_proc(method_define,out=method_file)) then begin
err='Could not locate - '+method_define
message,err,/cont
return
endif
np=n_elements(object)
otype=size(object,/tname)
if (otype ne 'STRING') and (otype ne 'OBJREF') then begin
err='Second input argument must be an object reference or class name'
message,err,/cont
return
endif
;-- loop thru each object to determine valid class names
class_names=strarr(np)
if otype eq 'OBJREF' then begin
for i=0,np-1 do begin
if obj_valid(object[i]) then class_names[i]=strlowcase(obj_class(object[i]))
endfor
endif else class_names=strlowcase(trim(object))
chk=valid_class(class_names,index,count=count)
if count eq 0 then return
valid_class_names=get_uniq(class_names[index])
;-- check if method has been added to these class names
np=n_elements(valid_class_names)
if not keyword_set(override) then begin
for i=0,np-1 do begin
added=is_method_added(method_name,valid_class_names[i])
if (not added) then not_added=append_arr(not_added,valid_class_names[i])
endfor
if exist(not_added) then valid_class_names=not_added else begin
if verbose then message,'Method "'+method_name+'" already added to "'+arr2str(valid_class_names)+'" class',/cont
return
endelse
np=n_elements(valid_class_names)
endif
;-- read the method file into memory and replace all function/pro calls
; as follows:
;
; METHOD::METHOD_NAME -> METHOD_CLASS::METHOD_NAME.
;
; However, we don't replace any INIT or CLEANUP methods so as not to
; interfere with these methods in the original target object.
;
marray=rd_ascii(method_file)
if all_methods then begin
smethod=strlowcase(method_name)
method_calls=smethod+'::'
endif else begin
smethod=strlowcase(method_class)
method_calls=strlowcase(method)
endelse
method_class=strlowcase(method_class)
init_call=method_class+'::init'
;init_call_arg=method_class+'::init '
cleanup_call=method_class+'::cleanup'
tarray=strlowcase(marray)
chk=(strpos(tarray,method_calls) gt -1) and $
(strpos(tarray,init_call) eq -1) and $
; (strpos(tarray,init_call_arg) eq -1) and $
(strpos(tarray,cleanup_call) eq -1)
ok=where(chk,count)
if count eq 0 then begin
err='Could not locate any '+method_name+' method calls in '+method_file
message,err,/cont
return
endif
temp=tarray[ok]
;-- now do the replace
np=n_elements(valid_class_names)
for i=0,np-1 do begin
class_name=valid_class_names[i]
array=marray
if verbose then message,'Adding "'+method_name+'" method(s) to '+class_name,/cont
init_call=class_name+'::init'
cleanup_call=class_name+'::cleanup'
if all_methods then mtemp='' else mtemp=method_name
temp=str_replace(temp,method_calls,class_name+'::'+mtemp)
array[ok]=temp
;-- write to a temporary file
temp_dir=get_temp_dir()
temp_name=method_name+'.pro'
temp_file=mk_temp_file(temp_name,direct=temp_dir,/random)
break_file,temp_file,dsk,dir,pro_name
new_array=append_arr(array,['pro '+pro_name,'end'],/no_copy)
file_append,temp_file,new_array,/new
;-- compile new added method
cd,temp_dir,current=current
qsave=!quiet
!quiet=1
resolve_routine,pro_name,/either
!quiet=qsave
cd,current
error=0
catch,error
if error ne 0 then begin
catch,/cancel
cd,current
endif
;-- cleanup
file_delete,temp_file,/quiet
endfor
;-- compile original method file to get it's methods
obj_compile,method_class,/quiet
return & end