--------------------------------------------------------
-- Some generic functions and types to work with strings
--------------------------------------------------------

create or replace package str
is

  -- A record for tokenizing strings
  type token_info is record (
    string       varchar2(4000),  -- The string we will be tokenizing
    length       integer,         -- The length of the string
    token_start  integer,         -- Start of last token
    separators    varchar2(4000)  -- Characters which can separate tokens
  );

  -- Return the first token in a string, or null if there are
  -- no more tokens
  function first_token (
    string_in     IN varchar2,
    token_rec_in  IN OUT NOCOPY token_info,
    separators_in IN varchar2 := ' '
  ) return varchar2;

  -- Get the next token in the string, or null if there are 
  -- no more tokens
  function next_token (
    token_rec_in IN OUT NOCOPY token_info
  ) return varchar2;

  -- Determine if a string has more tokens to be returned
  function has_more_tokens (
    token_rec_in IN OUT NOCOPY token_info
  ) return char;

end str;
/
show errors

create or replace package body str 
is

function first_token (
  string_in     IN varchar2,
  token_rec_in  IN OUT NOCOPY token_info,
  separators_IN IN varchar2 := ' '
) return varchar2
is
begin
  
   token_rec_in.string := string_in;
   token_rec_in.length := length(string_in);
   token_rec_in.token_start := 1;
   token_rec_in.separators := separators_in;
   
   return next_token(token_rec_in);
end first_token;


function next_token (
  token_rec_in IN OUT NOCOPY token_info
) return varchar2
is
  v_token_start integer;
begin
  
  -- Check for string end
  if token_rec_in.token_start > token_rec_in.length then
    return null;
  end if;

  -- Skip separators
  while instr(token_rec_in.separators, 
              substr(token_rec_in.string, 
                     token_rec_in.token_start, 1)) <> 0 
  loop

    token_rec_in.token_start := token_rec_in.token_start + 1;
    if token_rec_in.token_start > token_rec_in.length then
      return null;
    end if;

  end loop;

  v_token_start := token_rec_in.token_start;

  -- Skip until the next separator
  while instr(token_rec_in.separators,
              substr(token_rec_in.string, 
                     token_rec_in.token_start, 1)) = 0 
  loop
    token_rec_in.token_start := token_rec_in.token_start + 1;
    if token_rec_in.token_start > token_rec_in.length then
      return substr(token_rec_in.string, v_token_start);
    end if;
  end loop; 
    
  return substr(token_rec_in.string, v_token_start, 
                token_rec_in.token_start - v_token_start);

end next_token;  
  
function has_more_tokens (
  token_rec_in IN OUT NOCOPY token_info
) return char
is
begin
  if token_rec_in.token_start > token_rec_in.length then 
    return 'f';
  else
    return 't';
  end if;
end has_more_tokens;

end str;
/
show errors


    
  
