FTDictionaryClass.f90 Source File


Source Code

!
!////////////////////////////////////////////////////////////////////////
!
!      FTDictionary.f90
!      Created: January 28, 2013 2:00 PM 
!      By: David Kopriva 
!
!////////////////////////////////////////////////////////////////////////
!
!>The FTKeyObjectPairClass is for use by the FTDictionary Class and will
!>generally not be interacted with by the user.
!>
      Module FTKeyObjectPairClass
         USE FTObjectClass
         IMPLICIT NONE  
!
!    -----------------
!    Module constants:
!    -----------------
!
         INTEGER, PARAMETER, PUBLIC  :: FTDICT_KWD_STRING_LENGTH    = 64
!
!        ----------
!        Class type
!        ----------
!
         TYPE, EXTENDS(FTObject) :: FTKeyObjectPair
            CHARACTER(LEN=FTDICT_KWD_STRING_LENGTH) :: keyString
            CLASS(FTObject) , POINTER               :: valueObject => NULL()
!
!           --------            
            CONTAINS
!           --------
!          
            PROCEDURE :: initWithObjectAndKey
            PROCEDURE :: destruct         => destructFTKeyObjectPair
            PROCEDURE :: description      => FTKeyObjectPairDescription
            PROCEDURE :: printDescription => printFTKeyObjectPairDescription
            
            PROCEDURE :: key
            PROCEDURE :: object

         END TYPE FTKeyObjectPair
      
      INTERFACE release
         MODULE PROCEDURE releaseFTKeyObjectPair
      END INTERFACE  
!
!        ========       
         CONTAINS  
!        ========       
!
!@mark -
!
!//////////////////////////////////////////////////////////////////////// 
! 
         SUBROUTINE initWithObjectAndKey(self,v,key)
            IMPLICIT NONE
            CLASS(FTKeyObjectPair)      :: self
            CHARACTER(LEN=*)            :: key
            CLASS(FTObject) , POINTER   :: v
            
            CALL self % FTObject % init
            
            self % keyString   = key
            self % valueObject => v
            
            CALL self % valueObject % retain
            
         END SUBROUTINE initWithObjectAndKey
!
!//////////////////////////////////////////////////////////////////////// 
! 
         SUBROUTINE destructFTKeyObjectPair(self)
            IMPLICIT NONE
            CLASS(FTKeyObjectPair) :: self
            
            self % keyString = ""
            CALL releaseFTObject(self % valueObject)
!
!           ----------------------------------------------------
!           Call superclass destructor after processing subclass
!           specific items
!           ----------------------------------------------------
!
            CALL self % FTObject % destruct()
             
         END SUBROUTINE destructFTKeyObjectPair
!
!------------------------------------------------
!> Public, generic name: release(self)
!>
!> Call release(self) on an object to release control
!> of an object. If its reference count is zero, then 
!> it is deallocated.
!------------------------------------------------
!
!//////////////////////////////////////////////////////////////////////// 
! 
      SUBROUTINE releaseFTKeyObjectPair(self)  
         IMPLICIT NONE
         CLASS(FTKeyObjectPair) , POINTER :: self
         CLASS(FTObject)        , POINTER :: obj
         obj => self
         CALL releaseFTObject(self = obj)
         IF ( .NOT. ASSOCIATED(obj) )     THEN
            self => NULL() 
         END IF      
      END SUBROUTINE releaseFTKeyObjectPair
!
!//////////////////////////////////////////////////////////////////////// 
! 
         CHARACTER(LEN=DESCRIPTION_CHARACTER_LENGTH) FUNCTION FTKeyObjectPairDescription(self)  
            IMPLICIT NONE  
            CLASS(FTKeyObjectPair) :: self
            
            WRITE(FTKeyObjectPairDescription,*)"(" , TRIM(self % keyString)  , "," &
                                           , TRIM(self % valueObject % description()) , ")"
             
         END FUNCTION FTKeyObjectPairDescription
!
!//////////////////////////////////////////////////////////////////////// 
! 
         SUBROUTINE printFTKeyObjectPairDescription(self,iUnit)  
            IMPLICIT NONE  
            CLASS(FTKeyObjectPair) :: self
            INTEGER                :: iUnit
            
            WRITE(iUnit,*) "{"

            WRITE(iUnit,'(6x,A,A3)',ADVANCE = "NO") TRIM(self % keyString)  , " = " 
            CALL self % valueObject % printDescription(iUnit)
            
            WRITE(iUnit,*) "}"
             
         END SUBROUTINE printFTKeyObjectPairDescription    
!
!//////////////////////////////////////////////////////////////////////// 
! 
         CHARACTER(LEN=FTDICT_KWD_STRING_LENGTH) FUNCTION key(self)  
            IMPLICIT NONE  
            CLASS(FTKeyObjectPair) :: self
            key = self % keyString
         END FUNCTION key         
!
!//////////////////////////////////////////////////////////////////////// 
! 
         FUNCTION object(self)  
            IMPLICIT NONE  
            CLASS(FTKeyObjectPair)   :: self
            CLASS(FTObject), POINTER :: object
            
            object => self % valueObject
          
         END FUNCTION object    
          
      END Module FTKeyObjectPairClass  
!
!////////////////////////////////////////////////////////////////////////
!
!@mark -
!>
!>A dictionary is a special case of a hash table that stores key-value pairs. 
!>
!>It is an
!>example of what is called an ``associative container''. In the implementation of FTObjectLibrary,
!>the value can be any subclass of FTObject and the key is a character variable. The library
!>includes the base dictionary that can store and retrieve any subclass of FTObject. It also includes a
!>subclass that is designed to store and retrieve FTValue objects.
!> 
!>FTDictionary (Inherits from FTObject)
!>
!>###Initialization
!>
!>         CLASS(FTDictionary), POINTER :: dict
!>         ALLOCATE(dict)
!>         CALL dict % initWithSize(N) ! N = size of dictionary. Should be power of two
!>
!>###Adding entries
!>
!>         CLASS(FTDictionary), POINTER :: dict
!>         CLASS(FTObject)    , POINTER :: obj
!>         CHARACTER(LEN=N)             :: key
!>         obj => r                            ! r is subclass of FTObject
!>         CALL dict % addObjectForKey(obj,key)
!>
!>###Accessing entries
!>
!>         obj => dict % objectForKey(key)
!>         CALL cast(obj,v) ! v is the type of object to be extracted
!>
!>###Destruction
!>   
!>         CALL release(dict) ! Pointer
!>         call dict % destruct() ! Stack variable
!>###Accessing an object
!>
!>           TYPE(FTObject) :: obj
!>           obj => dict % objectForKey(key)
!>
!>###Converting a base class pointer to a dictionary
!>           dict =>  dictionaryFromObject(obj)
!>
!>###Getting all of the keys
!>           CHARACTER(LEN=FTDICT_KWD_STRING_LENGTH), POINTER :: keys(:)
!>           keys =>  dict % allKeys()
!>(The target of the pointer must be deallocated by the caller)
!>###Getting all of the objects
!>           CLASS(FTMutableObjectArray), POINTER :: objectArray
!>           objectArray =>  dict % allObjects() ! The array is owned by the caller.
!>(The target of the pointer must be released by the caller)
!>
      Module FTDictionaryClass
         USE FTKeyObjectPairClass
         USE FTLinkedListClass
         USE FTLinkedListIteratorClass
         USE FTMutableObjectArrayClass
         IMPLICIT NONE  
         
         TYPE, EXTENDS(FTObject) :: FTDictionary
            INTEGER                                   :: numberOfEntries
            LOGICAL                                   :: isCaseSensitive
            TYPE(FTLinkedList), DIMENSION(:), POINTER :: entries => NULL()
!
!           --------
            CONTAINS
!           --------
!
            PROCEDURE :: initWithSize
            PROCEDURE :: init
            PROCEDURE :: setCaseSensitive
            PROCEDURE :: caseSensitive
            PROCEDURE :: allKeys
            PROCEDURE :: allObjects
            PROCEDURE :: destruct => destructFTDictionary
            PROCEDURE :: addObjectForKey
            procedure :: removeObjectForKey
            PROCEDURE :: description => FTDictionaryDescription
            PROCEDURE :: printDescription => printFTDictionaryDescription
            PROCEDURE :: objectForKey
            PROCEDURE :: containsKey
            PROCEDURE :: className => dictionaryClassName
            PROCEDURE :: COUNT
         END TYPE FTDictionary
         
         INTERFACE cast
            MODULE PROCEDURE castToDictionary
         END INTERFACE cast
         
         INTERFACE release
            MODULE PROCEDURE releaseFTDictionary 
         END INTERFACE  
!
!        ========         
         CONTAINS  
!        ========  
!       
!
!//////////////////////////////////////////////////////////////////////// 
! 
         SUBROUTINE init(self)  
            IMPLICIT NONE  
            CLASS(FTDictionary) :: self
            CALL initWithSize(self,16)
         END SUBROUTINE init
!
!//////////////////////////////////////////////////////////////////////// 
! 
         SUBROUTINE initWithSize(self,sze)  
!
!           ----------------------
!           Designated initializer
!           ----------------------
!
            IMPLICIT NONE
            CLASS(FTDictionary) :: self
            INTEGER, INTENT(in) :: sze
            INTEGER             :: i
            
            CALL self % FTObject % init()
            
            self % isCaseSensitive = .true.
            self % numberOfEntries = 0
!
!           --------------------------------
!           Create the array of linked lists
!           --------------------------------
!
            ALLOCATE(self % entries(sze))
            DO i = 1, sze
               CALL self % entries(i) % init()
            END DO    
            
         END SUBROUTINE initWithSize
!
!//////////////////////////////////////////////////////////////////////// 
! 
         SUBROUTINE destructFTDictionary(self)  
            IMPLICIT NONE  
            CLASS(FTDictionary) :: self
           
            INTEGER :: i
            
            DO i = 1, SIZE(self % entries)
               CALL self % entries(i) % destruct()
            END DO

            DEALLOCATE(self % entries)
            self % entries => NULL()
!
!           ----------------------------------------------------
!           Call superclass destructor after processing subclass
!           specific items
!           ----------------------------------------------------
!
           CALL self % FTObject % destruct()
            
         END SUBROUTINE destructFTDictionary    
!
!------------------------------------------------
!> Public, generic name: release(self)
!>
!> Call release(self) on an object to release control
!> of an object. If its reference count is zero, then 
!> it is deallocated.
!------------------------------------------------
!
!//////////////////////////////////////////////////////////////////////// 
! 
      SUBROUTINE releaseFTDictionary(self)  
         IMPLICIT NONE
         TYPE(FTDictionary) , POINTER :: self
         CLASS(FTObject)    , POINTER :: obj
         
         IF(.NOT. ASSOCIATED(self)) RETURN
         
         obj => self
         CALL releaseFTObject(self = obj)
         IF ( .NOT. ASSOCIATED(obj) )     THEN
            self => NULL() 
         END IF      
      END SUBROUTINE releaseFTDictionary
!
!//////////////////////////////////////////////////////////////////////// 
! 
         SUBROUTINE setCaseSensitive(self,bool)  
            IMPLICIT NONE  
            CLASS(FTDictionary) :: self
            LOGICAL             :: bool
            self % isCaseSensitive = bool
         END SUBROUTINE setCaseSensitive  
!
!//////////////////////////////////////////////////////////////////////// 
! 
         LOGICAL FUNCTION caseSensitive(self)  
            IMPLICIT NONE  
            CLASS(FTDictionary) :: self
            caseSensitive = self % isCaseSensitive
         END FUNCTION caseSensitive      
!
!//////////////////////////////////////////////////////////////////////// 
! 
         INTEGER FUNCTION COUNT(self)  
            IMPLICIT NONE  
            CLASS(FTDictionary) :: self
            COUNT = self % numberOfEntries
         END FUNCTION COUNT    
!
!//////////////////////////////////////////////////////////////////////// 
! 
         SUBROUTINE addObjectForKey(self,object,key)
            IMPLICIT NONE  
            CLASS(FTDictionary)                 :: self
            CLASS(FTObject)       , POINTER     :: object
            CHARACTER(LEN=*)                    :: key
            CLASS(FTKeyObjectPair), POINTER     :: pair => NULL()
            CLASS(FTObject)       , POINTER     :: ptr => NULL()
            INTEGER                             :: h
            
            INTEGER, EXTERNAL                   :: b3hs_hash_key_jenkins
            
            h = b3hs_hash_key_jenkins(key,SIZE(self % entries))
            
            ALLOCATE(pair)
            CALL pair % initWithObjectAndKey(object,key)
            ptr => pair
            CALL self % entries(h) % add(ptr)
            self % numberOfEntries = self % numberOfEntries + 1
            
         END SUBROUTINE addObjectForKey

         SUBROUTINE removeObjectForKey(self,key)
            IMPLICIT NONE  
            CLASS(FTDictionary)                 :: self
            CHARACTER(LEN=*)                    :: key
!
!           ---------------
!           Local variables
!           ---------------
!
            CLASS(FTObject)       , POINTER     :: obj => NULL()
            INTEGER                             :: h
            CLASS(FTLinkedListRecord)     , POINTER :: listRecordPtr => NULL()
            CHARACTER(LEN=FTDICT_KWD_STRING_LENGTH) :: keyString
            
            INTEGER, EXTERNAL                   :: b3hs_hash_key_jenkins
            
            h = b3hs_hash_key_jenkins(key,SIZE(self % entries))

            obj => NULL()
            
            listRecordPtr => self % entries(h) % head
            DO WHILE(ASSOCIATED(listRecordPtr))
!
!              --------------------------------------------
!              The self % entries(h)'s recordObject is a FTKeyObjectPair
!              --------------------------------------------
!
               SELECT TYPE (pair => listRecordPtr % recordObject)
                  TYPE is (FTKeyObjectPair)
                     keyString = pair % key()
                     IF ( TRIM(keyString) == TRIM(key) )     THEN
                        obj => pair
                        EXIT 
                     END IF 
                  CLASS DEFAULT
               END SELECT
               listRecordPtr  => listRecordPtr % next
            END DO    

            CALL self % entries(h) % remove(obj)
            self % numberOfEntries = self % numberOfEntries - 1
            
         END SUBROUTINE removeObjectForKey
!
!//////////////////////////////////////////////////////////////////////// 
! 
         FUNCTION objectForKey(self,key)  
            IMPLICIT NONE  
            CLASS(FTDictionary)                  :: self
            CHARACTER(LEN=*)                     :: key
            CLASS(FTObject)       , POINTER      :: objectForKey
            INTEGER                              :: h
            
            INTEGER, EXTERNAL                    :: b3hs_hash_key_jenkins
           
            objectForKey => NULL()
            IF(self % COUNT() == 0)     RETURN 
            
            h = b3hs_hash_key_jenkins(key,SIZE(self % entries))
            IF ( self % entries(h) % COUNT() > 0 )     THEN
               objectForKey => objectForKeyInList(key,self % entries(h))
            END IF 
 
         END FUNCTION ObjectForKey
!
!//////////////////////////////////////////////////////////////////////// 
! 
         FUNCTION containsKey(self,key)  RESULT(r)
            IMPLICIT NONE  
            CLASS(FTDictionary)      :: self
            CHARACTER(LEN=*)         :: key
            CLASS(FTObject), POINTER :: obj
            LOGICAL                  :: r
           
            IF ( ASSOCIATED( self % objectForKey(key)) )     THEN
               r = .TRUE. 
            ELSE 
               r = .FALSE. 
            END IF 
 
         END FUNCTION containsKey
!
!//////////////////////////////////////////////////////////////////////// 
! 
         FUNCTION objectForKeyInList(key,list)  
            IMPLICIT NONE  
            CHARACTER(LEN=*)            :: key
            CLASS(FTLinkedList)         :: list
            CLASS(FTObject), POINTER    :: objectForKeyInList
            
            CLASS(FTLinkedListRecord)     , POINTER :: listRecordPtr => NULL()
            CHARACTER(LEN=FTDICT_KWD_STRING_LENGTH) :: keyString

            objectForKeyInList => NULL()
            
            listRecordPtr => list % head
            DO WHILE(ASSOCIATED(listRecordPtr))
!
!              --------------------------------------------
!              The list's recordObject is a FTKeyObjectPair
!              --------------------------------------------
!
               SELECT TYPE (pair => listRecordPtr % recordObject)
                  TYPE is (FTKeyObjectPair)
                     keyString = pair % key()
                     IF ( TRIM(keyString) == TRIM(key) )     THEN
                        objectForKeyInList => pair % object()
                        EXIT 
                     END IF 
                  CLASS DEFAULT
               END SELECT
               listRecordPtr  => listRecordPtr % next
            END DO    

         END FUNCTION objectForKeyInList
!
!//////////////////////////////////////////////////////////////////////// 
! 
         CHARACTER(LEN=DESCRIPTION_CHARACTER_LENGTH) FUNCTION FTDictionaryDescription(self)  
            IMPLICIT NONE  
            CLASS(FTDictionary) :: self
            CHARACTER(LEN=DESCRIPTION_CHARACTER_LENGTH) :: s
            
            INTEGER :: i
            FTDictionaryDescription = ""

            IF(SELF % COUNT() == 0) RETURN
            
            DO i = 1, SIZE(self % entries)
               s = self % entries(i) % description()
               IF ( LEN_TRIM(s) > 0 )     THEN
                  FTDictionaryDescription =  TRIM(FTDictionaryDescription) // &
                                             TRIM(self % entries(i) % description()) // &
                                             CHAR(13)
               END IF 
            END DO
            
         END FUNCTION FTDictionaryDescription    
!
!//////////////////////////////////////////////////////////////////////// 
! 
         SUBROUTINE printFTDictionaryDescription(self,iUnit)  
            IMPLICIT NONE  
            CLASS(FTDictionary) :: self
            INTEGER             :: iUnit
            
            INTEGER :: i

            IF(SELF % COUNT() == 0) THEN
               WRITE(iUnit,*) "Empty Dictionary"
            END IF  
            
            DO i = 1, SIZE(self % entries)
               CALL self % entries(i) % printDescription(iUnit)
            END DO
            
         END SUBROUTINE printFTDictionaryDescription
!
!//////////////////////////////////////////////////////////////////////// 
! 
         FUNCTION AllObjects(self) RESULT(objectArray)
            IMPLICIT NONE  
!
!           ---------
!           Arguments
!           ---------
!
            CLASS(FTDictionary)                  :: self
            CLASS(FTMutableObjectArray), POINTER :: objectArray
!
!           ---------------
!           Local Variables
!           ---------------
!
            INTEGER                                 :: i
            CLASS(FTLinkedListRecord)     , POINTER :: listRecordPtr => NULL()
            CLASS(FTObject)               , POINTER :: obj           => NULL()
            CHARACTER(LEN=FTDICT_KWD_STRING_LENGTH) :: keyString
!
!           --------------------------------------------
!           Allocate a pointer to the object array to be
!           returned with refCount = 1
!           --------------------------------------------
!
            ALLOCATE(objectArray)
            CALL objectArray % initWithSize(arraySize = self % COUNT())
            
            DO i = 1, SIZE(self % entries)
               listRecordPtr => self % entries(i) % head
               DO WHILE(ASSOCIATED(listRecordPtr))
!
!                 --------------------------------------------
!                 The list's recordObject is a FTKeyObjectPair
!                 --------------------------------------------
!
                  SELECT TYPE (pair => listRecordPtr % recordObject)
                     TYPE is (FTKeyObjectPair)
                        keyString = pair % key()
                        obj  => pair % object()
                        CALL objectArray % addObject(obj)
                     CLASS DEFAULT
                  END SELECT
                  listRecordPtr  => listRecordPtr % next
               END DO    
            END DO  
            
         END FUNCTION AllObjects
!
!//////////////////////////////////////////////////////////////////////// 
! 
         FUNCTION AllKeys(self) RESULT(keys)
            IMPLICIT NONE  
!
!           ---------
!           Arguments
!           ---------
!
            CLASS(FTDictionary)                              :: self
            CHARACTER(LEN=FTDICT_KWD_STRING_LENGTH), POINTER :: keys(:)
!
!           ---------------
!           Local Variables
!           ---------------
!
            INTEGER                                 :: i, c
            CLASS(FTLinkedListRecord)     , POINTER :: listRecordPtr => NULL()
            CHARACTER(LEN=FTDICT_KWD_STRING_LENGTH) :: keyString
!
!           --------------------------------------------
!           Allocate a pointer to the object array to be
!           returned with refCount = 1
!           --------------------------------------------
!
            ALLOCATE(keys(self % COUNT()))
            
            c = 1
            DO i = 1, SIZE(self % entries)
               listRecordPtr => self % entries(i) % head
               DO WHILE(ASSOCIATED(listRecordPtr))
!
!                 --------------------------------------------
!                 The list's recordObject is a FTKeyObjectPair
!                 --------------------------------------------
!
                  SELECT TYPE (pair => listRecordPtr % recordObject)
                     TYPE is (FTKeyObjectPair)
                        keyString = pair % key()
                        keys(c)   = keyString
                     CLASS DEFAULT
                  END SELECT
                  c = c + 1
                  listRecordPtr  => listRecordPtr % next
               END DO    
            END DO  
            
         END FUNCTION AllKeys
!
!//////////////////////////////////////////////////////////////////////// 
! 
      SUBROUTINE castToDictionary(obj,cast) 
!
!     -----------------------------------------------------
!     Cast the base class FTObject to the FTException class
!     -----------------------------------------------------
!
         IMPLICIT NONE  
         CLASS(FTObject)    , POINTER :: obj
         CLASS(FTDictionary), POINTER :: cast
         
         cast => NULL()
         SELECT TYPE (e => obj)
            TYPE is (FTDictionary)
               cast => e
            CLASS DEFAULT
               
         END SELECT
         
      END SUBROUTINE castToDictionary
!
!//////////////////////////////////////////////////////////////////////// 
! 
      FUNCTION dictionaryFromObject(obj) RESULT(cast)
!
!     -----------------------------------------------------
!     Cast the base class FTObject to the FTException class
!     -----------------------------------------------------
!
         IMPLICIT NONE  
         CLASS(FTObject)    , POINTER :: obj
         CLASS(FTDictionary), POINTER :: cast
         
         cast => NULL()
         SELECT TYPE (e => obj)
            TYPE is (FTDictionary)
               cast => e
            CLASS DEFAULT
               
         END SELECT
         
      END FUNCTION dictionaryFromObject
!
!//////////////////////////////////////////////////////////////////////// 
! 
!      -----------------------------------------------------------------
!> Class name returns a string with the name of the type of the object
!>
!>  ### Usage:
!>
!>        PRINT *,  obj % className()
!>        if( obj % className = "FTDictionary")
!>
      FUNCTION dictionaryClassName(self)  RESULT(s)
         IMPLICIT NONE  
         CLASS(FTDictionary)                        :: self
         CHARACTER(LEN=CLASS_NAME_CHARACTER_LENGTH) :: s
         
         s = "FTDictionary"
 
      END FUNCTION dictionaryClassName

      END Module FTDictionaryClass