;+ ; This is the base class for managing a section in an index file. Sections are ; started with a line: '[section_name]'. This class manages all basic i/o functions ; for a section. ; See UML for all IO Classes ; ; @file_comments ; This is the base class for managing a section in an index file. Sections are ; started with a line: '[section_name]'. This class manages all basic i/o functions ; for a section. ; See UML for all IO Classes ; ; @field lines_incr number of lines that buffers grow by ; internally as necessary. Placed here for consistency only. ; @field max_line_width maximum number of chars allowed on each line ; @field num_lines the current number of lines in this section ; @field filename full pathname to the file where this section resides ; @field lun logical unit number for file ; @field section_marker name of section ; @field line_nums pointer to an array of indicies locating each line in file (line #s zero based) ; @field lines pointer to an array holding all lines in this section ; @field section_read boolean flag for wether section is in memory or not ; @field allow_append boolean flag for wether this section allows appends ; @field pad_width boolean flag determines wether lines are padded to max width ; @field debug boolean flag for wether this class is verbose or not ; ; @private_file ;- PRO index_file_section__define ifs = { INDEX_FILE_SECTION, $ lines_incr:0L, $ max_line_width:0L, $ num_lines:0L, $ ; does not include section marker filename: string(replicate(32B,256)), $ lun:0L, $ section_marker: string(replicate(32B,6)), $ ; [section_marker] line_nums:ptr_new(), $ ; line numbers zero-based lines:ptr_new(), $ ; does not include section marker section_read:0L, $ allow_append:0L, $ pad_width:0L, $ debug:0L $ } END ;+ ; Class Constructor ; @private ;- FUNCTION INDEX_FILE_SECTION::init, lun, section, filename compile_opt idl2, hidden self.lun = lun self.section_marker = section if n_elements(filename) ne 0 then self.filename = filename self.lines_incr = 100000L self.max_line_width = 256L self.line_nums = ptr_new(/allocate_heap) self.lines = ptr_new(/allocate_heap) self.debug = 0 return, 1 END ;+ ; Class Destructor ; @private ;- PRO INDEX_FILE_SECTION::cleanup compile_opt idl2, hidden if ptr_valid(self.line_nums) then ptr_free,self.line_nums if ptr_valid(self.lines) then ptr_free,self.lines END ;+ ; Sets file name of file to be written to ; @param file_name {in}{type=string} name of index file ;- PRO INDEX_FILE_SECTION::set_file_name, file_name compile_opt idl2, hidden self.filename = file_name END ;+ ; Sets object to pad lines written with spaces to their max width ;- PRO INDEX_FILE_SECTION::pad_width_on compile_opt idl2, hidden self.pad_width = 1 END ;+ ; Sets object to NOT pad lines written with spaces to their max width ;- PRO INDEX_FILE_SECTION::pad_width_off compile_opt idl2, hidden self.pad_width = 0 END ;+ ; Creates the section in the file. ; ; @param start_line_number {in}{optional}{type=long} what line to start section on in file ; @keyword lines {in}{optional}{type=string array} array of lines to be written in section upon creation (does not include section marker). ; @keyword append {in}{optional}{type=bool} exclusive with start_line_number: if set, lines get written at end of section ; ;- PRO INDEX_FILE_SECTION::create, start_line_number, lines=lines, append=append compile_opt idl2, hidden if keyword_set(append) then begin ; we need to know how many lines currently in the file, ; HACK HACK HACK: for know determin this ;openu, self.lun, self.filename, /append openu, self.lun, self.filename tmp = '' num_lines=0 while eof(self.lun) eq 0 do begin readf, self.lun, tmp num_lines = num_lines+1 endwhile start_line_number = num_lines endif else begin ; open file and position pointer for writing openu, self.lun, self.filename self->go_to_line, start_line_number endelse printf, self.lun, '['+self.section_marker+']' if keyword_set(lines) then begin ; print them to the file for i=0,n_elements(lines)-1 do begin line = lines[i] if self.pad_width then line = self->pad_line(line) printf, self.lun, line endfor ; get memory in sync with lines written self.num_lines = n_elements(lines) *self.lines = lines ; take into account the section marker written above when ; calculating the line numbers for each line if n_elements(start_line_number) ne 0 then begin *self.line_nums = indgen(self.num_lines)+start_line_number+1 endif else begin *self.line_nums = indgen(self.num_lines)+1 endelse endif close, self.lun self.section_read = 1 END FUNCTION INDEX_FILE_SECTION::pad_line, line compile_opt idl2, hidden if strlen(line) lt self.max_line_width then begin dif = self.max_line_width - strlen(line) line = line + string(replicate(32B,dif)) endif return, line END ;+ ; Retrieves array of lines in section ; @returns array of lines in section ;- FUNCTION INDEX_FILE_SECTION::get_lines compile_opt idl2, hidden return, *self.lines END ;+ ; Retrieves the location of each section line in the file ; @returns integer array which is locatio of each section line in file ;- FUNCTION INDEX_FILE_SECTION::get_line_nums compile_opt idl2, hidden return, *self.line_nums END ;+ ; Retrieves the number of lines in this section ; @returns long integer - number of lines in the section ;- FUNCTION INDEX_FILE_SECTION::get_num_lines compile_opt idl2, hidden return, self.num_lines END ;+ ; Increments the number of lines this class believes are in the section. ; @private ;- PRO INDEX_FILE_SECTION::increment_num_lines compile_opt idl2, hidden self.num_lines = self.num_lines + 1L END ;+ ; Reads the file, locates the section, and loads all lines and metainfo ; into objects memory ; @returns 0 - failure, 1 - success ;- FUNCTION INDEX_FILE_SECTION::read_file compile_opt idl2, hidden result = 0 self.section_read = 0 openr, self.lun, self.filename num_lines_read = 0L line_nums = lonarr(self.lines_incr) line_num = 0L ;lines = strarr(string(replicate(32B,256),self.max_rows) lines = strarr(self.lines_incr) current_section = 'None' line = '' done = 0 reading_section = 0 while (eof(self.lun) ne 1) and (done eq 0) do begin readf, self.lun, line first_char = strmid(line,0,1) if (first_char eq '#') or (strlen(line) eq 0) then begin ; nothing to do with comments or blank lines endif else begin if (first_char eq '[') then begin ; begining new section if reading_section then begin ; if we've been reading a section, then the start of a new ; one means that there's nothing more for us to read done = 1 endif else begin current_section = strmid(line,1,(strlen(line)-2)) if current_section eq self.section_marker then reading_section = 1 endelse endif else begin ; we're in a section, is it the right one? if reading_section then begin ; store this line and it's location in the file if self.pad_width then line = strtrim(line, 2) if num_lines_read ge n_elements(lines) then begin ; add in another increment to these arrays 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 num_lines_read = num_lines_read + 1L if (num_lines_read lt 0) then begin ; must be too large for long integer if this happens message,'number of rows in section exceeds maximum long integer, can not continue' endif endif ; if we're in our section endelse ; if we're starting new section endelse ; if anything but a comment line line_num = line_num + 1L endwhile close, self.lun ; 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 *self.line_nums = line_nums *self.lines = lines self.num_lines = n_elements(*self.lines) if (num_lines_read eq 0) then begin message, 'no lines found in section' return, 0 endif self.section_read = 1 return, 1 END ;+ ; Advances file pointer to right before the line number parameter. To be used ; to write to a specific line in file. File must be opened beforehand and closed ; after call. This is something that idl should provide. ; ; @param line_number {in}{required}{type=long} line number to go to (0-based) ; ; @private ;- PRO INDEX_FILE_SECTION::go_to_line, line_number compile_opt idl2, hidden line='' next_line = 0L while (next_line lt line_number) do begin if (EOF(self.lun) ne 1) then begin next_line = next_line+1L readf, self.lun, line endif else begin ; we hit the end of file before we got to our line_number message, 'line number exceeds number of rows in file: '+string(line_number) endelse endwhile END ;+ ; Replaces a line already in the index with a new string. ; ; @param line_number {in}{required}{type=long} the file line number (first line, ; second line, etc. ) to replace. ; @param line {in}{required}{type=string} new line to place in section ; @param line_index {out}{optional}{type=long} index in array of lines for this section ; specified by line_number ; ;- PRO INDEX_FILE_SECTION::set_line, line_number, line, line_index compile_opt idl2, hidden if n_elements(line_index) eq 0 then begin line_index = where(line_number[0] eq *self.line_nums, cnt) if line_index eq -1 then message, "line number is not in section: "+string(line_number) endif openu, self.lun, self.filename self->go_to_line, line_number ; edit both the file and our memory to keep them 'nsync (*self.lines)[line_index] = line if self.pad_width then line = self->pad_line(line) printf, self.lun, line close, self.lun END ;+ ; Finds the file line number of a given string. If line not found, or ; line is found multiple times, error is raised. ; ; @param line {in}{required}{type=string} line that is searched for in section ; @param line_index {out}{optional}{type=long} the index at which this line ; is found in the array of section lines ; ; @returns the file line number where the param line is found ;- FUNCTION INDEX_FILE_SECTION::get_line_location, line, line_index compile_opt idl2, hidden count = 0 ind = where(*self.lines eq line, count) if count gt 1 then message, "line found more then once in section: "+line if ind eq -1 then message, "line not found in read section: "+line if n_elements(line_index) ne 0 then line_index = ind return, (*self.line_nums)[ind] END ;+ ; Retrieves the index of the param line in the array of section lines: in other words, ; tells wether this is the first, second, etc. line in this section ; ; @param line {in}{required}{type=string} line to be searched for in array of section lines ; ; @returns the index of the param line in the array of section lines. ;- FUNCTION INDEX_FILE_SECTION::get_line_index, line compile_opt idl2, hidden count = 0 ind = where(*self.lines eq line, count) if ind eq -1 then message, "line not found in read section: ", line if count lt 1 then message, "line found more then once in section: ", line return, ind END ;+ ; Replaces a line in the section with a new one. ; ; @param old_line {in}{required}{type=string} identifies the string to replace. ; must be unique. ; @param new_line {in}{required}{type=string} the new line to replace the old one ; ; @uses get_line_location ; ;- PRO INDEX_FILE_SECTION::rewrite_line, old_line, new_line compile_opt idl2, hidden ; retrive the location of this line line_index = -1 line_number = self->get_line_location(old_line, line_index) ; rewrite it! self->set_line, line_number, new_line, line_index END ;+ ; Replaces the line passed in with a blank in the index file, and removes ; this line from the objects memory. ; ; @param line_str {in}{required}{type=string} line to be deleted. Must be unique. ; ; @uses get_line_index ; @uses rewrite_line ; ;- PRO INDEX_FILE_SECTION::delete_line, line_str compile_opt idl2, hidden ind = self->get_line_index(line_str) ; replace this line in the index file with a blank self->rewrite_line, line_str, string(replicate(32B,strlen(line_str))) ; now remove this from the array of lines curr_size = self->get_num_lines() new_lines = strarr(curr_size-1) new_line_nums = lonarr(curr_size-1) new_line_ind=0 for i=0L,(curr_size-1) do begin if ind ne i then begin new_lines[new_line_ind] = (*self.lines)[i] new_line_nums[new_line_ind] = (*self.line_nums)[i] new_line_ind = new_line_ind + 1L endif endfor *self.lines = new_lines *self.line_nums = new_line_nums self.num_lines = n_elements(*self.lines) END ;+ ; Appends lines to end of section (if allowed). Keeps objects memory in sync with ; section ; ; @param lines {in}{required}{type=string array} lines to append to section ; ;- PRO INDEX_FILE_SECTION::append_lines, lines compile_opt idl2, hidden if self.allow_append eq 0 then message, "this section does not allow appending lines" openu, self.lun, self.filename, /append starting_line_num = (*self.line_nums)[self.num_lines-1] final_num_lines = self.num_lines+n_elements(lines) if final_num_lines lt 0 then begin message,'Number of rows in index file section exceeds largest long integer, can not continue' endif new_line_nums = lonarr(final_num_lines) new_lines = strarr(final_num_lines) for i=0,self.num_lines-1 do begin new_line_nums[i] = (*self.line_nums)[i] new_lines[i] = (*self.lines)[i] endfor ; if sufficiently large, use a progress bar if n_elements(lines) gt 1000 then progress_bar=1 else progress_bar=0 if progress_bar then begin total_bar = '__________' step_size = fix(n_elements(lines)/10) step = 0 print, "Writing rows to index file:" print, total_bar endif for i=0,n_elements(lines)-1 do begin ; write new lines to file if self.pad_width then file_line=self->pad_line(lines[i]) else file_line=lines[i] printf, self.lun, file_line ; keep this object in sync with the file new_lines[self.num_lines+i] = lines[i] new_line_nums[self.num_lines+i] = starting_line_num+i ; update the progress bar if progress_bar then begin if step eq step_size then begin step = 0 print, format='("X",$)' endif else begin step += 1 endelse endif endfor ; terminate progress bar if progress_bar then print, format='(/)' *self.lines = new_lines *self.line_nums = new_line_nums self.num_lines = n_elements(*self.lines) close, self.lun END ;+ ; Makes object verbose ;- PRO INDEX_FILE_SECTION::set_debug_on compile_opt idl2 self.debug = 1 END ;+ ; Makes object quiet ;- PRO INDEX_FILE_SECTION::set_debug_off compile_opt idl2 self.debug = 0 END ;+ ; Has the section been read? ; @returns 0 - no, 1 - yes ;- FUNCTION INDEX_FILE_SECTION::is_section_read compile_opt idl2 return, self.section_read END