;+ ; This virtual class extends the index_file_section class, but is extended by ; the line_index_section and cntm_index_section to manage the rows section of ; spectral line index files and continuum index files, respectively. ; ; @file_comments ; This virtual class extends the index_file_section class, but is extended by ; the line_index_section and cntm_index_section to manage the rows section of ; spectral line index files and continuum index files, respectively. ; ; @field rows pointer to array of structures mirroring index rows section ; @field frmt pointer to 3xN array of formats for printing rows section ; @field frmt_quiet pointer to array of integers determining which columns are ; used for 'quiet' listing ; @field float_format string representing how all floats are to be printed ; @field format_string string used for reading index lines into row structures. ; determined by frmt array ; @field format_header top row to be printed in listings ; @field format_string_quiet less verbose equivalient of format_string ; @field format_header_quiet less verbose equivalent of format_header ; ; @inherits index_file_section ; ; @private_file ;- PRO rows_index_section__define ris = { ROWS_INDEX_SECTION, $ inherits index_file_section, $ rows:ptr_new(), $ frmt:ptr_new(), $ frmt_quiet:ptr_new(), $ frmt_user:ptr_new(), $ float_format:string(replicate(32B,128)), $ format_string:string(replicate(32B,1028)), $ format_header:string(replicate(32B,1028)), $ format_string_quiet:string(replicate(32B,1028)), $ format_header_quiet:string(replicate(32B,1028)), $ format_string_user:string(replicate(32B,1028)), $ format_header_user:string(replicate(32B,1028)), $ more_format:0 $ } END ;+ ; Class Constructor ; @private ;- FUNCTION ROWS_INDEX_SECTION::init, lun, filename if n_elements(filename) ne 0 then begin r = self->INDEX_FILE_SECTION::init(lun, "rows", filename) endif else begin r = self->INDEX_FILE_SECTION::init(lun, "rows") endelse if r eq 1 then begin self.rows = ptr_new(/allocate_heap) self.frmt = ptr_new(/allocate_heap) self.frmt_quiet = ptr_new(/allocate_heap) self.frmt_user = ptr_new(/allocate_heap) endif ; rows section are the last sections in the file self.allow_append = 1 ; set printing to be in the 'more' format self.more_format = 1 return, r END ;+ ; Class Destructor ; @private ;- PRO ROWS_INDEX_SECTION::cleanup compile_opt idl2, hidden self->INDEX_FILE_SECTION::cleanup if ptr_valid(self.rows) then ptr_free, self.rows if ptr_valid(self.frmt) then ptr_free, self.frmt if ptr_valid(self.frmt_quiet) then ptr_free, self.frmt_quiet if ptr_valid(self.frmt_user) then ptr_free, self.frmt_user END ;+ ; Creates rows section, but writes no actual rows to it, just the section marker, ; and the format_header ("#INDX FILES etc.."). ;- PRO ROWS_INDEX_SECTION::create compile_opt idl2, hidden self->INDEX_FILE_SECTION::create, lines=self.format_header, /append END ;+ ; Retrieves the arrays of structures that mirror the lines in the row section ; @returns arrays of structures that mirror the lines in the row section ;- FUNCTION ROWS_INDEX_SECTION::get_rows if self.section_read eq 0 then message, "cannot get_rows, must read section first" return, *self.rows END ;+ ; Retrieves the number of rows in the rows section, not counting the marker ; and the format header ; @returns number of rows in the rows section ;- FUNCTION ROWS_INDEX_SECTION::get_num_rows ; there is the section marker and the header to take into account ;return, self.num_lines - 2 if ptr_valid(self.rows) then begin return, n_elements(*self.rows) endif else begin return, 0 endelse END ;+ ; Reads the rows section into memory, then converts all lines (array of strings) ; into row structures ; @uses INDEX_FILE_SECTION::read_file() ;- FUNCTION ROWS_INDEX_SECTION::read_file compile_opt idl2, hidden result = self->INDEX_FILE_SECTION::read_file() ; convert each line to a structure if result ne -1 then begin row_info = self->get_row_info_strct() rows = replicate(row_info, n_elements(*self.lines)) reads,*self.lines, rows, format=self.format_string ; get rid of annoying white space from file rows.project = strtrim(rows.project,2) rows.file = strtrim(rows.file,2) rows.source = strtrim(rows.source,2) rows.procedure = strtrim(rows.procedure,2) rows.polarization = strtrim(rows.polarization,2) rows.cal_state = strtrim(rows.cal_state,2) rows.sig_state = strtrim(rows.sig_state,2) *self.rows = rows endif return, result END ;+ ; Uses format array to create format strings for printing index file and listings. ; Results get stored in object fields: format_string, format_header, format_string_quiet, ; and format_header_quiet ; @private ;- PRO ROWS_INDEX_SECTION::create_formats compile_opt idl2 frmt = *self.frmt frmt_quiet = *self.frmt_quiet ; how many keywords? sz = size(frmt) frmt_len = sz[2] ; build the format strings used for reading/writing index file header_keywords = strarr(frmt_len) data_format='(' header_format='(' for i=0,frmt_len-1 do begin if (i ne 0) then begin data_format += ',1x,' header_format += ',1x,' endif data_format += frmt[0,i] header_format += frmt[2,i] header_keywords[i] = frmt[1,i] endfor data_format+=')' header_format+=')' header = string(header_keywords,format=header_format) ; save off the format strings self.format_string = data_format self.format_header = header ; build the format strings used for listing index contents quietly header_keywords = strarr(n_elements(frmt_quiet)) data_format='(' header_format='(' for i=0,n_elements(frmt_quiet)-1 do begin if (i ne 0) then begin data_format += ',1x,' header_format += ',1x,' endif data_format += frmt[0,frmt_quiet[i]] header_format += frmt[2,frmt_quiet[i]] header_keywords[i] = frmt[1,frmt_quiet[i]] endfor data_format+=')' header_format+=')' header = string(header_keywords,format=header_format) ; save off these format strings self.format_string_quiet = data_format self.format_header_quiet = header END ;+ ; Uses format array to create the user specified format strings for ; printing index file and listings. ; Results get stored in object fields: format_string_user, ; and format_header_user ; @private ;- PRO ROWS_INDEX_SECTION::create_user_formats compile_opt idl2 if ptr_valid(self.frmt_user) ne 1 then begin message, "Cannot create user format: no keywords specified yet. Pointer not valid", /info return endif if n_elements(*self.frmt_user) eq 0 then begin message, "Cannot create user format: no keywords specified yet. No elements", /info return endif frmt_user = *self.frmt_user ; build the format strings used for listing index contents quietly header_keywords = strarr(n_elements(*self.frmt_user)) data_format='(' header_format='(' for i=0,n_elements(frmt_user)-1 do begin if (i ne 0) then begin data_format += ',1x,' header_format += ',1x,' endif data_format += (*self.frmt)[0,frmt_user[i]] header_format += (*self.frmt)[2,frmt_user[i]] header_keywords[i] = (*self.frmt)[1,frmt_user[i]] endfor data_format+=')' header_format+=')' header = string(header_keywords,format=header_format) ; save off these format strings self.format_string_user = data_format self.format_header_user = header END ;+ ; Prints the rows section of the index file for those rows specified ; @keyword rows the row numbers which are to be printed ; @keyword verbose set to true and ALL info on each row is printed, otherwise just select columns printed ;- PRO ROWS_INDEX_SECTION::list, rows=rows, verbose=verbose, columns=columns, _EXTRA=ex ;paz=paz, pel=pel compile_opt idl2 if (self.section_read) eq 0 then begin message, 'cannot list rows until index file is loaded. Use read_file method' return endif if keyword_set(verbose) then verbose = 1 else verbose = 0 if (n_elements(rows) eq 0) then rows=indgen(n_elements(*self.rows)) ; columsn keyword overides verbosity if n_elements(columns) ne 0 then begin self->list_given_columns, rows=rows, columns return endif if verbose then begin ; print using 'more' format if self.more_format then openw, out, '/dev/tty', /get_lun, /more ; print everything row_info = *self.rows self->print_line, self.format_header, out for i = 0L, (n_elements(rows)-1) do begin row_index = rows[i] self->print_line, row_info[row_index], out, format=self.format_string endfor if self.more_format then free_lun, out endif else begin ; print only a min of info, and any requested extras self->list_quiet, rows, _EXTRA=ex endelse END PRO ROWS_INDEX_SECTION::list_given_columns, rows=rows, columns compile_opt idl2 if (self.section_read) eq 0 then begin message, 'cannot list rows until index file is loaded. Use read_file method' return endif if (n_elements(columns) eq 0) then $ message, "must specify columns to print on listing" if (n_elements(rows) eq 0) then rows=indgen(n_elements(*self.rows)) ; get the indicies for each column named col_indicies = lonarr(n_elements(columns)) for i=0,n_elements(columns)-1 do begin col_indicies[i] = self->get_format_index(strupcase(columns[i])) if col_indicies[i] eq -1 then $ message, "column was not found, cannot list: "+columns[i] endfor ; build the format strings used for listing index header_keywords = strarr(n_elements(col_indicies)) data_format='(' header_format='(' for i=0,n_elements(col_indicies)-1 do begin if (i ne 0) then begin data_format += ',1x,' header_format += ',1x,' endif data_format += (*self.frmt)[0,col_indicies[i]] header_format += (*self.frmt)[2,col_indicies[i]] header_keywords[i] = (*self.frmt)[1,col_indicies[i]] endfor data_format+=')' header_format+=')' header = string(header_keywords,format=header_format) ; print using 'more' format if self.more_format then openw, out, '/dev/tty', /get_lun, /more row_info = *self.rows ; print the header self->print_line, header, out for i = 0L, (n_elements(rows)-1) do begin ; get the next row r = row_info[rows[i]] ; append default values line = '' for j=0,n_elements(col_indicies)-1 do begin if (j ne 0) then line += ' ' line += string(r.(col_indicies[j]),format='('+(*self.frmt)[0,col_indicies[j]]+')') endfor ; print the row self->print_line, line, out endfor if self.more_format then free_lun, out END PRO ROWS_INDEX_SECTION::list_user, rows=rows compile_opt idl2 if (self.section_read) eq 0 then begin message, 'cannot list rows until index file is loaded. Use read_file method' return endif if ptr_valid(self.frmt_user) eq 0 then begin message, "user must specify columns for listing first", /info return endif if (n_elements(rows) eq 0) then rows=indgen(n_elements(*self.rows)) ; print using 'more' format if self.more_format then openw, out, '/dev/tty', /get_lun, /more row_info = *self.rows ; print the header self->print_line, self.format_header_user, out for i = 0L, (n_elements(rows)-1) do begin ; get the next row r = row_info[rows[i]] ; append default values line = '' for j=0,n_elements(*self.frmt_user)-1 do begin if (j ne 0) then line += ' ' line += string(r.((*self.frmt_user)[j]),format='('+(*self.frmt)[0,(*self.frmt_user)[j]]+')') endfor ; print the row self->print_line, line, out endfor if self.more_format then free_lun, out END ;+ ; Returns index of format specification according to header name ; @param frmt_header {in}{required}{type=string} header keyword: must match exactly with what list prints. ; @private ;- FUNCTION ROWS_INDEX_SECTION::get_frmt_index, frmt_header compile_opt idl2 ind = -1 frmt = *self.frmt sz = size(frmt) for i=0,sz[2]-1 do begin if (strtrim(frmt[1,i],2) eq strtrim(frmt_header,2)) then ind = i endfor return, ind END ;+ ; Uses the objects format array to build a string for one formated header word ; @param frmt_header {in}{required}{type=string} header keyword: must match exactly with what list prints. ; @private ;- FUNCTION ROWS_INDEX_SECTION::get_frmt_header_keyword, frmt_header compile_opt idl2 i = self->get_frmt_index(frmt_header) if (i eq -1) then message, "Cannot find format for header: "+frmt_header return, string((*self.frmt)[1,i],format='('+(*self.frmt)[2,i]+')') END ;+ ; Uses the objects format array to build a formated string of a value from the index file ; @param frmt_header {in}{required}{type=string} header keyword: must match exactly with what list prints. ; @param row {in}{required}{type=struct} structure reflecting one row from the index file ; @private ;- FUNCTION ROWS_INDEX_SECTION::get_frmt_row_value, frmt_header, row compile_opt idl2 i = self->get_frmt_index(frmt_header) if (i eq -1) then message, "Cannot find format for header: "+frmt_header return, string(row.(i),format='('+(*self.frmt)[0,i]+')') END PRO ROWS_INDEX_SECTION::write_rows, row_strcts compile_opt idl2, hidden ;@line_row_info row_strct = self->get_row_info_strct() lines = strarr(n_elements(row_strcts)) ; translate the structs to string lines for i=0,n_elements(lines)-1 do begin lines[i] = string(row_strcts[i],format=self.format_string) endfor ; write the string lines to file self->append_lines, lines ; update the structures in memory new_num_rows = n_elements(*self.rows)+n_elements(row_strcts) new_rows = replicate(row_strct,new_num_rows) for i=0, n_elements(*self.rows)-1 do begin new_rows[i] = (*self.rows)[i] endfor for i=0, n_elements(row_strcts)-1 do begin new_rows[i+n_elements(rows_strcts)] = row_strcts[i] endfor *self.rows = new_rows END ;+ ; Retrieves the line number in the file of a row with the given index number ; ; @param index_num {in}{required}{type=long} index number of row for which the line number is returned ; @param row_index {out}{optional}{type=long} the index into the array of lines this index number is found ; @returns the line number (zero based) in the file where this index number is located ;- FUNCTION ROWS_INDEX_SECTION::get_line_number, index_num, row_index compile_opt idl2, hidden indicies = (*self.rows).index cnt = 0 row_index = where(indicies eq index_num, cnt) if cnt gt 1 then message, "index numbers must be unique: "+string(index_num) ; return the file line number of the index found if cnt eq 0 then begin return, -1 endif else begin return, (*self.line_nums)[row_index] endelse END ;+ ; Overwrites a row in the index with a new one ; ; @param index_num {in}{required}{type=long} index number of row which is to be overwritten ; @param row {in}{required}{type=struct} new row to write in index file at index_num ; ; @uses get_line_number ;- PRO ROWS_INDEX_SECTION::overwrite_row, index_num, row compile_opt idl2, hidden line_number = self->get_line_number(index_num,row_index) if line_number eq -1 then message, "cannot overwrite row, index not found: "+string(index_num) new_line = string(row,format=self.format_string) ; write the new line to file and keep memory in sync self->set_line, line_number, new_line (*self.lines)[row_index] = new_line (*self.rows)[row_index] = row END ;+ ; Overwirtes a specific value in a row within the index file ; ; @param index_num {in}{required}{type=long} index number of row which is to be overwritten ; @param column_name {in}{required}{type=string} column in row which is to be overwritten ; @param value {in}{required}{type=varies} value to place in row. ; ; @uses overwrite_row ;- PRO ROWS_INDEX_SECTION::overwrite_row_column, index_num, column_name, value compile_opt idl2, hidden ; get the row index of the given file index number indicies = (*self.rows).index cnt = 0 row_index = where(indicies eq index_num, cnt) if cnt eq 0 then message, "index number not in index file: "+string(index_num) if cnt gt 1 then message, "index numbers in index file must be unique: "+string(index_num) ; get a copy of the row we are going to write to, and the index of it's tag row = (*self.rows)[row_index] column_name = strtrim(strupcase(column_name),2) tag_index = where(column_name eq tag_names(row)) if tag_index eq -1 then message, "row does not contain tag: "+column_name ; set the value in the column of the row row.(tag_index) = value ; now overwrite the old colum with this new one self->overwrite_row, row.index, row END ;+ ; Sets the object to print rows using the interactive 'more' format ;- PRO ROWS_INDEX_SECTION::set_more_format_on compile_opt idl2, hidden self.more_format = 1 END ;+ ; Sets the object NOT to print rows using the interactive 'more' format ;- PRO ROWS_INDEX_SECTION::set_more_format_off compile_opt idl2, hidden self.more_format = 0 END ;+ ; Prints a line either to stdout, or using the interactive 'more' format ;- PRO ROWS_INDEX_SECTION::print_line, line, lun, _EXTRA=ex compile_opt idl2, hidden if self.more_format then begin if n_elements(lun) eq 0 then message, "to print in more format, lun must be provided" printf, lun, line, _EXTRA=ex endif else begin print, line, _EXTRA=ex endelse END ;+ ; Prints the available columns for list; these are also the valid search keywords ;- PRO ROWS_INDEX_SECTION::list_available_columns compile_opt idl2, hidden ; how many keywords? sz = size(*self.frmt) frmt_len = sz[2] if self.more_format then openw, out, '/dev/tty', /get_lun, /more ; print each keyword, in more format, if set for i=0,frmt_len-1 do begin column_name = (*self.frmt)[1,i] ; don't print the comment marker at the begining of INDEX if strmid(column_name,0,1) eq '#' then begin column_name = strmid(column_name,1,strlen(column_name)-1) endif self->print_line, column_name, out endfor if self.more_format then free_lun, out END ;+ ; Prints the columns currently selected for the user specified listing ;- PRO ROWS_INDEX_SECTION::list_user_columns compile_opt idl2, hidden print, self.format_header_user END ;+ ; Extracts just the keywords from the format array ; @returns keywords from the format array ;- FUNCTION ROWS_INDEX_SECTION::get_format_keywords compile_opt idl2, hidden ; how many keywords in format? sz = size(*self.frmt) nkeys = sz[2] return, (*self.frmt)[1,0:(nkeys-1)] END ;+ ; Find the index for a keyword in the format array ; @ returns the index for the given keyword; -1 if not found ;- FUNCTION ROWS_INDEX_SECTION::get_format_index, keyword compile_opt idl2, hidden cnt = 0 index = where(keyword eq self->get_format_keywords(), cnt) ; special case: #INDEX if keyword eq "INDEX" then index = 0 return, index END ;+ ; Adds a column to the list of user columns ;- PRO ROWS_INDEX_SECTION::add_user_column, column compile_opt idl2, hidden index = self->get_format_index(column) if index eq -1 then $ message, "keyword: "+column+" does not exist, cannot be added" if ptr_valid(self.frmt_user) then begin if n_elements(*self.frmt_user) ne 0 then begin *self.frmt_user = [*self.frmt_user, index] endif else begin *self.frmt_user = [index] endelse endif else begin *self.frmt_user = [index] endelse self->create_user_formats END ;+ ; Sets what columns should be used for user listing ; @param columns {in}{required}{type=string array} array of columns to print on list command ;- PRO ROWS_INDEX_SECTION::set_user_columns, columns compile_opt idl2, hidden ; we are resetting this, so clear it if ptr_valid(self.frmt_user) then ptr_free, self.frmt_user self.frmt_user = ptr_new(/allocate_heap) for i=0,n_elements(columns)-1 do begin self->add_user_column, columns[i] endfor END ;+ ; Returns the available columns for list; these are also the valid search keywords ;- FUNCTION ROWS_INDEX_SECTION::get_available_columns compile_opt idl2, hidden ; how many keywords? sz = size(*self.frmt) frmt_len = sz[2] cols = strarr(frmt_len) ; print each keyword, in more format, if set for i=0,frmt_len-1 do begin column_name = (*self.frmt)[1,i] ; don't print the comment marker at the begining of INDEX if strmid(column_name,0,1) eq '#' then begin column_name = strmid(column_name,1,strlen(column_name)-1) endif cols[i] = column_name endfor return, cols END ;+ ; For reading into memory the new rows that may have been written to the ; index file ( by hand, or by an online process ). ; Jumps to previous last line, and reads new rows. ; @param num_new_lines {out}{optional}{type=long} number of new lines found in file ;- PRO ROWS_INDEX_SECTION::read_new_rows, num_new_lines compile_opt idl2, hidden num_new_lines = 0 ; see if we should just read it if self.section_read eq 0 then begin self->read_file return endif ; read file untill we get to the 'rows' section openr, self.lun, self.filename line_num = 0L line = '' section = "[" + self.section_marker + "]" while strtrim(line) ne section and eof(self.lun) ne 1 do begin readf, self.lun, line line_num += 1L endwhile if strtrim(line) ne section then begin message, "file does not contain rows section marker: "+self.filename return endif if self.debug then print, "num lines up to [rows]: ", line_num ; read in the previous number of rows, + the header line = '' num_lines_read = 0L while num_lines_read lt n_elements(*self.rows)+1 do begin if eof(self.lun) then begin message, "got to end of file before number of lines: "+string(self.num_lines) endif readf, self.lun, line line_num += 1L num_lines_read += 1L endwhile if self.debug then print, "num lines up to previous lines: ", line_num ; now read in these new lines line = '' num_lines_read = 0L lines = strarr(self.lines_incr) line_nums = lonarr(self.lines_incr) while eof(self.lun) ne 1 do begin readf, self.lun, line if num_lines_read ge n_elements(lines) then begin lines = [lines,strarr(self.lines_incr)] line_nums = [line_nums,lonarr(self.lines_incr)] endif lines[num_lines_read] = line line_nums[num_lines_read]=line_num line_num += 1L num_lines_read += 1L if num_lines_read lt 0 then begin message,'Number of index file rows exceeds largest long integer, can not continue.' endif endwhile ; free unused portions of arrays if (num_lines_read gt 0) then begin lines = lines[0:(num_lines_read-1)] line_nums = line_nums[0:(num_lines_read-1)] endif close, self.lun if self.debug then print, "total lines read: ", line_num if self.debug then print, "new lines read: ", num_lines_read num_new_lines = num_lines_read ; convert the new lines to new row structures rows = make_array(num_lines_read,value=self->get_row_info_strct()) reads,lines, rows, format=self.format_string ; get rid of annoying white space from file rows.project = strtrim(rows.project,2) rows.file = strtrim(rows.file,2) rows.source = strtrim(rows.source,2) rows.procedure = strtrim(rows.procedure,2) rows.polarization = strtrim(rows.polarization,2) rows.cal_state = strtrim(rows.cal_state,2) rows.sig_state = strtrim(rows.sig_state,2) ; update the objects memory *self.line_nums = [*self.line_nums,line_nums] *self.lines = [*self.lines,lines] self.num_lines += num_lines_read *self.rows = [*self.rows,rows] END