#!/bin/bash
#---------------------------------------------------------------------
##
## Set of functions for working with an associative array type data
## structure.  Values can be stored and retrieved using strings as
## the index into the data structure instead of numbers.
##
## The hash data structure provided in this file allows you to store
## values into fields of a table.  The 'hash_put' function takes the
## name of the table, a field name in the table, and the value to be
## stored in the table.  The 'hash_get' function retrieves a value from
## the table given the table and field name.
##
## <pre>
## To store a value into a field of a table, use hash_put:
##
##    hash_put "myTable" "aField" "theValue"
##
## The value stored in the table can be retrieved with hash_get:
##
##    hash_get "myTable" "aField"
##
## In this example, the hash_get function would echo "theValue".
## hash_get_ref can also be used here and has the benefit of being forkless.
## </pre>
## <br />
## <p>IMPLEMENTATION NOTE</p>
## <br />
## Bash does not provide direct support for hash tables.  These
## functions are implemented by first building a variable using the
## table name and field name, then using the eval function to store
## (retrieve) value into (from) the variable.<br />
##
## The idea for the hash data structure in bash was inspired by a
## short example by Phil Howard which shows the use of hashes in bash.
## Phil Howard's original example can be found here:
##
##    http://www.codebits.com/bit.cfm?BitID=92
##
## @Copyright Copyright 2002 by the Source Mage Team
##
##
#---------------------------------------------------------------------


#---------------------------------------------------------------------
## @param table name
## @param field name
## @param returning variable
## @Type Private
## Given a table and field name, bulds the name of
## the variable into which a value will be stored.  Also changes '+',
## '-', and '.' in the table name into text since bash doesn't like
## variable names with those characters.
##
#---------------------------------------------------------------------
function hash_build_variable_name() {
  local ___TABLE="$1"
  local ___FIELD="$2"

#  debug "libhash" "hash_build_variable_name() - TABLE=$___TABLE:FIELD=$___FIELD"

  ___TABLE=${___TABLE//\+/_P_}
  ___TABLE=${___TABLE//\-/_M_}
  ___TABLE=${___TABLE//\./_D_}
  ___TABLE=${___TABLE//\:/_CLN_}
  ___TABLE=${___TABLE// /_SPC_}
  ___TABLE=${___TABLE//[/_OSB_}
  ___TABLE=${___TABLE//]/_CSB_}

  if [[ $___FIELD ]] ; then
    ___FIELD=${___FIELD//\+/_P_}
    ___FIELD=${___FIELD//\-/_M_}
    ___FIELD=${___FIELD//\./_D_}
    ___FIELD=${___FIELD//\:/_CLN_}
    ___FIELD=${___FIELD// /_SPC_}
    ___FIELD=${___FIELD//]/_CSB_}
    ___FIELD=${___FIELD//]/_OSB_}
  fi

  # If this format is changed, modify hash_get_table_fields to suite
  if [[ $___FIELD ]] ; then ___FIELD="HASH_${___TABLE}_${___FIELD}_"
  else ___FIELD="HASH_${___TABLE}_" ; fi

  eval $3=\"\$___FIELD\"
}


#---------------------------------------------------------------------
## @param field name
## @param table name
## @param returning variable
## @Type Private
## most likely reverses hash_build_field_name
##
#---------------------------------------------------------------------
function hash_unbuild_field_name() {
  local ___TABLE="$2"
  local ___FIELD=${1#$___TABLE}
  ___FIELD=${___FIELD%_*}
#  `echo "$1" | sed -n "s/^$2\(.*\)_$/\1/p"`

#  debug "libhash" "hash_unbuild_field_name() - TABLE=$___TABLE:FIELD=$___FIELD"

  ___FIELD=${___FIELD//_P_/\+}
  ___FIELD=${___FIELD//_M_/\-}
  ___FIELD=${___FIELD//_D_/\.}
  ___FIELD=${___FIELD//_CLN_/\:}
  ___FIELD=${___FIELD//_SPC_/ }
  ___FIELD=${___FIELD//_CSB_/]}
  ___FIELD=${___FIELD//_OSB_/[}

  eval $3=\"\$___FIELD\"
}


#---------------------------------------------------------------------
## @param table name
## @param field name
## @param value
##
## Saves the value in the specified table/field.
##
#---------------------------------------------------------------------
function hash_put() {
  local VARIABLE_NAME
  hash_build_variable_name "$1" "$2" VARIABLE_NAME
  eval "${VARIABLE_NAME}=\"\${3}\""
  debug "libhash" "hash_put() - VARIABLE_NAME=$VARIABLE_NAME, data=$3"
}


#---------------------------------------------------------------------
## @param table name
## @param field name
##
## @Stdout Value stored in table/field
## Echos the value stored in the table/field.  If no value was
## previously stored in the table/field, this function echos an empty
## string.
##
#---------------------------------------------------------------------
function hash_get() {
  local VARIABLE_NAME
  hash_build_variable_name "$1" "$2" VARIABLE_NAME
  echo "${!VARIABLE_NAME}"
}

#---------------------------------------------------------------------
## @param table name
## @param field name
## @param upvar name
##
## @Stdout none
## Returns the value stored in the table/field through the upvar variable
## name. If no value was previously stored in the table/field, then an
## empty string is returned.
##
#---------------------------------------------------------------------
function hash_get_ref() {
  local VARIABLE_NAME
  hash_build_variable_name "$1" "$2" VARIABLE_NAME
  eval "$3=\${!VARIABLE_NAME}"
}

#---------------------------------------------------------------------
## @param table name
## @param field name
## @param value
##
## Appends the value to the specified table/field.
##
#---------------------------------------------------------------------
function hash_append() {
  local VARIABLE_NAME
  local old_value
  local sep=${4:-" "}
  hash_build_variable_name "$1" "$2" VARIABLE_NAME
  old_value=${!VARIABLE_NAME}
  if [[ -n $old_value ]] ; then
    eval "${VARIABLE_NAME}=\"\$old_value\${sep}\${3}\""
  else
    eval "${VARIABLE_NAME}=\"\$3\""
  fi
}

#---------------------------------------------------------------------
## @param table name
##
## 'export' all the values in the table. This is useful for getting
## hash table data from cast's pass_one/two into pass_three/pass_four
## which are run through make. Essentially exporting lets us pass
## the variables through make.
##
#---------------------------------------------------------------------
function hash_export() {
  local VARIABLE_NAME
  hash_build_variable_name $1 "" VARIABLE_NAME
  # make sure the hash has something in it before trying to export it
  [[ $(eval echo '${!'$VARIABLE_NAME'*}') ]] &&
  eval 'export ${!'$VARIABLE_NAME'*}'
}

#---------------------------------------------------------------------
## @param table name
## @param field name
##
## Unsets field. Deletes value.
##
#---------------------------------------------------------------------
function hash_unset() {
  local VARIABLE_NAME
  hash_build_variable_name "$1" "$2" VARIABLE_NAME
  unset ${VARIABLE_NAME}
}

#---------------------------------------------------------------------
## @param table name
## @param field name
## @param item
##
## The reverse of hash_append, it removes an item from the field's value.
##
#---------------------------------------------------------------------
function hash_unset_part() {
  local VARIABLE_NAME
  local old_value
  local item=$3
  hash_build_variable_name "$1" "$2" VARIABLE_NAME
  old_value=${!VARIABLE_NAME}
  if [[ -n $old_value ]] && real_list_find "$old_value" "$item"; then
    real_list_remove old_value "$item"
    if [[ -n $old_value ]]; then
      upvar $VARIABLE_NAME "$old_value"
    else
      unset $VARIABLE_NAME
    fi
  fi
}

#---------------------------------------------------------------------
## @param table name
##
## Unsets all fields in a table.
##
#---------------------------------------------------------------------
function hash_reset()  {
  local TABLE_NAME
  hash_build_variable_name "$1" '' TABLE_NAME
  eval "local VARIABLES=\"\${!${TABLE_NAME}*}\""
  unset $VARIABLES

}

#---------------------------------------------------------------------
## @param table name
## @param opt delimiter
## @Stdout table data
## Outputs the entire table data, with fields separated by the
## optional delimiter. If no delimiter is give, \n will be used.
##
#---------------------------------------------------------------------
function hash_get_table()  {

  local TABLE_NAME
  hash_build_variable_name "$1" '' TABLE_NAME
  local VARIABLES i
  local separator="$2"
  separator=${separator:-$'\n'}
  eval "VARIABLES=\"\${!${TABLE_NAME}*}\""

  for i in $VARIABLES; do
    echo -n "${!i}${separator}"
  done

}


#---------------------------------------------------------------------
## @param table name
## @param opt delimiter
## @Stdout Fields in table
## Outputs all of the fields in the table , with fields separated
## by the optional delimiter. If no delimiter is give, \n wil be
## used.
##
#---------------------------------------------------------------------
function hash_get_table_fields()  {

  local TABLE_NAME
  hash_build_variable_name "$1" '' TABLE_NAME
  local VARIABLES i
  local separator="$2"
  separator=${separator:-$'\n'}
  eval "VARIABLES=\"\${!${TABLE_NAME}*}\""

  local FIELD
  for i in $VARIABLES ; do
    hash_unbuild_field_name "$i" "$TABLE_NAME" FIELD
    echo -n "${FIELD}${separator}"
  done

}

#---------------------------------------------------------------------
## @param table name
## @Stdout Print the table in some reasonably readable form
## As the name would imply, this is mainly for development use
## and is not intended for regular use.
##
#---------------------------------------------------------------------
function hash_debug_dump() {
  local TABLE_NAME
  local FIELD
  local value
  for FIELD in $(hash_get_table_fields $1); do
    hash_get_ref $1 $FIELD value
    echo "$FIELD : $value"
  done
}


#---------------------------------------------------------------------
## @License
##
## This software is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This software is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this software; if not, write to the Free Software
## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
##
#---------------------------------------------------------------------
