Viewing contents of file '../idllib/astron/contrib/varosi/vlib/allpro/copy_struct.pro'
;+
; NAME:
;	copy_struct
; PURPOSE:
; 	Copy all fields with matching tag names (except for "except_Tags")
;	from one structure array to another structure array of different type.
;	This allows copying of tag values when equating the structures of
;	different types is not allowed, or when not all tags are to be copied.
;	Can also recursively copy from/to structures nested within structures.
;	Note that the number of elements in the output structure array
;	is automatically adjusted to equal the length of input structure array.
;	If this not desired then use pro copy_struct_inx which allows
;	specifying via subscripts which elements are copied where in the arrays.
; CATEGORY:
;			Structures
; CALLING EXAMPLES:
;
;	copy_struct, struct_From, struct_To, NT_copied
;	copy_struct, struct_From, struct_To, EXCEPT=["image","misc"]
;	copy_struct, struct_From, struct_To, /RECUR_TANDEM
;
; INPUTS:
;	struct_From = structure array to copy from.
;	struct_To = structure array to copy values to.
;
; KEYWORDS:
;
;	EXCEPT_TAGS = string array of tag names to ignore (to NOT copy).
;		Used at all levels of recursion.
;
;	SELECT_TAGS = tag names to copy (takes priority over EXCEPT).
;		This keyword is not passed to recursive calls in order
;		to avoid the confusion of not copying tags in sub-structures.
;
;	/RECUR_FROM = search for sub-structures in struct_From, and then
;		call copy_struct recursively for those nested structures.
;
;	/RECUR_TO = search for sub-structures of struct_To, and then
;		call copy_struct recursively for those nested structures.
;
;	/RECUR_TANDEM = call copy_struct recursively for the sub-structures
;		with matching Tag names in struct_From and struct_To
;		(for use when Tag names match but sub-structure types differ).
;
; OUTPUTS:
;	struct_To = structure array to which new tag values are copied.
;	NT_copied = incremented by total # of tags copied (optional)
;
; INTERNAL:
;	Recur_Level = # of times copy_struct calls itself.
;		This argument is for internal recursive execution only.
;		The user call is 1, subsequent recursive calls increment it,
;		and the counter is decremented before returning.
;		The counter is used just to find out if argument checking
;		should be performed, and to set NT_copied = 0 first call.
; EXTERNAL CALLS:
;	pro match	(when keyword SELECT_TAGS is specified)
; PROCEDURE:
;	Match Tag names and then use corresponding Tag numbers.
; HISTORY:
;	written 1989 Frank Varosi STX @ NASA/GSFC
; 	mod Jul.90 by F.V. added option to copy sub-structures RECURSIVELY.
;	mod Aug.90 by F.V. adjust # elements in TO (output) to equal
;			# elements in FROM (input) & count # of fields copied.
;	mod Jan.91 by F.V. added Recur_Level as internal argument so that
;			argument checking done just once, to avoid confusion.
;			Checked against Except_Tags in RECUR_FROM option.
;	mod Oct.91 by F.V. added option SELECT_TAGS= selected field names.
;	mod Aug.95 by W. Landsman to fix match of a single selected tag.
;	mod Mar.97 by F.V. do not pass the SELECT_TAGS keyword in recursion.
;	F.V.1999, correctly handle copying tag arrays of different # elements.
;-

pro copy_struct, struct_From, struct_To, NT_copied, Recur_Level,            $
						EXCEPT_TAGS  = except_Tags, $
						SELECT_TAGS  = select_Tags, $
						RECUR_From   = recur_From,  $
						RECUR_TO     = recur_To,    $
						RECUR_TANDEM = recur_tandem

	if N_elements( Recur_Level ) NE 1 then Recur_Level = 0

	Ntag_from = N_tags( struct_From )
	Ntag_to = N_tags( struct_To )

	if (Recur_Level EQ 0) then begin	;check only at first user call.

		NT_copied = 0

		if (Ntag_from LE 0) OR (Ntag_to LE 0) then begin
			message,"two arguments must be structures",/INFO
			print," "
			print,"syntax:    copy_struct, struct_From, struct_To"
			print," "
			print,"keywords:	EXCEPT_TAGS= , SELECT_TAGS=,  "
			print,"		/RECUR_From,  /RECUR_TO,  /RECUR_TANDEM"
			return
		   endif

		N_from = N_elements( struct_From )
		N_to = N_elements( struct_To )

		if (N_from GT N_to) then begin

			message," # elements (" + strtrim( N_to, 2 ) + $
					") in output TO structure",/INFO
			message," increased to (" + strtrim( N_from, 2 ) + $
					") as in FROM structure",/INFO
			struct_To = [ struct_To, $
					replicate( struct_To(0), N_from-N_to ) ]

		  endif	else if (N_from LT N_to) then begin

			message," # elements (" + strtrim( N_to, 2 ) + $
					") in output TO structure",/INFO
			message," decreased to (" + strtrim( N_from, 2 ) + $
					") as in FROM structure",/INFO
			struct_To = struct_To(0:N_from-1)
		   endif
	   endif

	Recur_Level = Recur_Level + 1		;go for it...

	Tags_from = Tag_names( struct_From )
	Tags_to = Tag_names( struct_To )
	wto = indgen( Ntag_to )

;Determine which Tags are selected or excluded from copying:

	Nseltag = N_elements( select_Tags )
	Nextag = N_elements( except_Tags )

	if (Nseltag GT 0) then begin

		match, Tags_to, [strupcase( select_Tags )], mt, ms,COUNT=Ntag_to

		if (Ntag_to LE 0) then begin
			message," selected tags not found",/INFO
			return
		   endif

		Tags_to = Tags_to(mt)
		wto = wto(mt)

	  endif else if (Nextag GT 0) then begin

		except_Tags = [strupcase( except_Tags )]

		for t=0,Nextag-1 do begin
			w = where( Tags_to NE except_Tags(t), Ntag_to )
			Tags_to = Tags_to(w)
			wto = wto(w)
		  endfor
	   endif

;Now find the matching Tags and copy them...

	for t = 0, Ntag_to-1 do begin

		wf = where( Tags_from EQ Tags_to(t) , nf )

		if (nf GT 0) then begin

			from = wf(0)
			to = wto(t)

			if keyword_set( recur_tandem ) AND		$
			   ( N_tags( struct_To.(to) ) GT 0 ) AND	$
			   ( N_tags( struct_From.(from) ) GT 0 ) then begin

				struct_tmp = struct_To.(to)

				copy_struct, struct_From.(from), struct_tmp,  $
						NT_copied, Recur_Level,       $
						EXCEPT=except_Tags,           $
						/RECUR_TANDEM,                $
						RECUR_FROM = recur_From,      $
						RECUR_TO   = recur_To

				struct_To.(to) = struct_tmp

			  endif else begin

				sz_to = size(struct_To(0).(to))
				sz_from = size(struct_From(0).(from))

				if sz_to(0) eq sz_from(0) then begin

				   ndim = sz_to(0)

				   if ndim gt 0 then begin
					net = sz_to(1:ndim)
					nef = sz_from(1:ndim)
					ndim = max( abs( net - nef ) )
				    endif

				   if ndim eq 0 then begin

					struct_To.(to) = struct_From.(from)
					NT_copied = NT_copied + 1

				    endif else begin	;take apart and rebuild:

					vto = struct_To.(to)
					vfrom = struct_From.(from)
					vto(*) = 0
					L = ( net < nef )-1
					L = [ L, replicate( 0, 7 ) ]
					vto(0,0,0,0,0,0,0)   =   vfrom(	0:L(0),$
									0:L(1),$
									0:L(2),$
									0:L(3),$
									0:L(4),$
									0:L(5),$
									0:L(6) )
					struct_To.(to) = vto
					NT_copied = NT_copied + 1

				     endelse

				 endif else begin
					message,"TO and FROM arrays not same "+$
						"dimension:" + string(7b),/INFO
					print, sz_to, sz_from
					print," tag  ",Tags_to(t),"  not copied"
				  endelse
			   endelse
		  endif
	  endfor

;Handle request for recursion on FROM structure:

	if keyword_set( recur_From ) then begin

		wfrom = indgen( Ntag_from )

		if (Nextag GT 0) then begin

			for t=0,Nextag-1 do begin
			    w = where( Tags_from NE except_Tags(t), Ntag_from )
			    Tags_from = Tags_from(w)
			    wfrom = wfrom(w)
			  endfor
		   endif

		for t = 0, Ntag_from-1 do begin

		     from = wfrom(t)

		     if N_tags( struct_From.(from) ) GT 0 then begin

			copy_struct, struct_From.(from), struct_To,        $
						NT_copied, Recur_Level,    $
						EXCEPT=except_Tags,        $
						/RECUR_FROM,               $
						RECUR_TO     = recur_To,   $
						RECUR_TANDEM = recur_tandem
			endif
		  endfor
	  endif

;Handle request for recursion on TO structure:

	if keyword_set( recur_To ) then begin

		for t = 0, Ntag_to-1 do begin

		   to = wto(t)

		   if N_tags( struct_To.(to) ) GT 0 then begin

			struct_tmp = struct_To.(to)

			copy_struct, struct_From, struct_tmp,              $
						NT_copied, Recur_Level,    $
						EXCEPT=except_Tags,        $
						/RECUR_TO,                 $
						RECUR_FROM = recur_From,   $
						RECUR_TANDEM = recur_tandem
			struct_To.(to) = struct_tmp
		     endif
		  endfor
	  endif

	Recur_Level = Recur_Level - 1
end