!     
!     $Id$
!    
! <DOCUMENTATION name="modulePakoXML">
!  This module implements a simple interface to write information
!  to a VOTable XML file.  It is in particular adapted for pako
!  (the observer's user interface to the NCS). pako uses it to
!  write XML files for the Queue and Coordinator. 
!  Data processing software can use it to write results XML files
!  for pako.
! </DOCUMENTATION>
!
! <DEV> 
! TBD: - more documentation
!      - more elaborate error handling
!          - parameter "error" (error flag) should be Optional
!            in all routines
!          - general error codes per Character variable errorC
!            are foreseen, but only in a few cases implemented,
!             e.g., in pakoXMLdictionaryAdd pakoXMLpush pakoXMLpop
!      - use linked lists for lists
!      - more fundamental "abstract" routines independant
!        of VOTable XML
!      - ...
! <\DEV> 
!
Module modulePakoXML
  !     
  Use modulePakoMessages
!
  Implicit None
  Save
  Private
  !
  ! *** Subroutines
  !
  Public :: pakoXMLsetOutputUnit
  Public :: pakoXMLsetIndent
  !
  Public :: pakoXMLwriteStartXML
  Public :: pakoXMLwriteStartElement
  Public :: pakoXMLwriteEndElement
  Public :: pakoXMLwriteElement
  !
  Public :: pakoXMLcase
  Public :: pakoXMLupper
  !     
  ! *** values representing "not set":
  !
  !
  Character(len=8),  Parameter  :: cNone = 'none'
  Integer,           Parameter  :: iNone = 0
  Real,              Parameter  :: rNone = 0.0
  Real(Kind=8),      Parameter  :: dNone = 0.0D0
  !
  ! *** Variables  for XML ***
  !
  Integer :: outputUnitXML = iNone
  Integer :: levelXML      = iNone
  Integer :: indentXML     = iNone
  !
  Integer, Parameter  :: lWord    = 128
  Integer, Parameter  :: lPhrase  = 256
  Integer, Parameter  :: lLine    = 512
  Integer, Parameter  :: lLineOut = 1024
  Character(len=lWord)    :: word    = cNone
  Character(len=lPhrase)  :: phrase  = cNone
  Character(len=lLine)    :: line    = cNone
  Character(len=lLine)    :: line2   = cNone
  Character(len=lLineOut) :: lineOut = cNone
  !
  ! *** dictionary for mixedCase keyword values:
  ! TBD: turn this into a linked list
  !
  Integer, Parameter  :: lDict   = 12
  !
  Type :: pakoXMLdictionaryT
     Character(len=ldict) :: key     = cNone
     Character(len=lDict) :: value   = cNone
  End Type pakoXMLdictionaryT
  !
  Integer, Parameter  :: nDimDict = 999
  Integer             :: nDict    = iNone
  Type (pakoXMLdictionaryT), Dimension(nDimDict) :: pakoDict
  !
  ! *** working variables and constants ***
  !     standard constants
  Real,    Parameter  :: Pi=3.141592653589793
  Real,    Parameter  :: degs=Pi/180.0
  !
  ! *** "stack" of elements
  ! TBD:  in future: replace this by a linked list!
  !
  Integer, Parameter  :: lEl   =  24
  Integer, Parameter  :: lText = 256
  !
  Type :: pakoXMLelementT
     Character(len=lEl)   ::  element  = cNone
     Character(len=lEl)   ::  name     = cNone
     Character(len=lEl)   ::  comment  = cNone
     Logical              ::  doCdata  = .False.
     Character(len=lEl)   ::  space    = cNone
     Character(len=lEl)   ::  value    = cNone
     Character(len=lEl)   ::  unit     = cNone
     Character(len=lEl)   ::  dataType = cNone
     Character(len=lText) ::  text     = cNone
  End Type pakoXMLelementT
  !
  Integer, Parameter :: nDimStack = 99
  Integer            :: nStack    = iNone
  Type (pakoXMLelementT), Dimension(nDimStack) :: stack
  Type (pakoXMLelementT)                       :: elementNone
  !
  ! *** for error messages:
  !
  Integer, Parameter :: severityI = 1
  Integer, Parameter :: severityW = 2
  Integer, Parameter :: severityE = 3
  Integer, Parameter :: severityF = 4
  !
  Integer, Parameter :: priorityI = severityI*2
  Integer, Parameter :: priorityW = severityW*2
  Integer, Parameter :: priorityE = severityE*2
  Integer, Parameter :: priorityF = severityF*2
  !
Contains
!!!     
!!!
  Subroutine pakoXMLsetOutputUnit(iUnit, error)
    Integer,  Intent(IN),  Optional :: iUnit
    Logical,  Intent(OUT), Optional :: error              ! errorFlag
    ! <DOCUMENTATION name="pakoXMLsetOutputUnit">
    ! set unit number for XML output.
    ! all XML output will be written to the file attached to iUnit
    ! </DOCUMENTATION>
    !
    if (Present(error)) error = .False.
    !
    If (Present(iUnit)) Then
       outputUnitXML = iUnit
    Else
       outputUnitXML = 11
    End If
    !
  End Subroutine pakoXMLsetOutputUnit
!!!
!!!
  Subroutine pakoXMLsetIndent(iIndent, error)
    Integer, Intent(IN),  Optional :: iIndent
    Logical, Intent(OUT), Optional :: error              ! errorFlag
    ! <DOCUMENTATION name="pakoXMLsetIndent">
    ! set indent for XML output.
    ! each nesting level of the XML hierarchy 
    ! will be indented by iIndent characters
    ! </DOCUMENTATION>
    !
    if (Present(error)) error = .False.
    !
    If (Present(iIndent)) Then
       indentXML = iIndent
    Else
       indentXML = 1
    End If
    !
  End Subroutine pakoXMLsetIndent
!!!
!!!
  Subroutine pakoXMLsetLevel(iLevel)
    Integer, Intent(IN) :: iLevel
    ! <DOCUMENTATION name="pakoXMLsetLevel">
    ! internal pakoXML routine
    ! set level of XML nesting to iLevel
    ! </DOCUMENTATION>
    !
    If (iLevel.Ge.1) Then
       levelXML = iLevel
    End If
    !
  End Subroutine pakoXMLsetLevel
!!!
!!!
  Subroutine pakoXMLwriteStartXML(                                &
       &                                       xmlVersionNum,     &
       &                                       docTypeName,       &
       &                                       dtdFile,           &
       &                                       dtdVersion,        &
       &                                       comment,           &
       &                                       space,             &
       &                                       error,             &
       &                                       errorC             &
       &                         )
    !
    Character(len=*), Intent(IN),  Optional :: xmlVersionNum      ! XML Version Number
    Character(len=*), Intent(IN)            :: docTypeName        ! document Type
    Character(len=*), Intent(IN)            :: dtdFile            ! DTD file
    Character(len=*), Intent(IN)            :: dtdVersion         ! DTD version
    Character(len=*), Intent(IN),  Optional :: comment            ! XML comment
    Character(len=*), Intent(IN),  Optional :: space              ! add space before or after
    !
    Logical,          Intent(OUT)           :: error              ! errorFlag
    Character(len=*), Intent(OUT), Optional :: errorC             ! errorCode
    !
    ! <DOCUMENTATION name="pakoXMLwriteStartXML">
    ! Write start of XML file
    ! </DOCUMENTATION>
    !     
    Type (pakoXMLelementT) :: elementToStack
    !    
    error = .False.
    If(Present(errorC)) errorC="OK"
    !
    elementToStack = elementNone
    !
    Call pakoXMLdictionaryInit(error)
    Call pakoXMLsetLevel(1)
    !
    If (Present(xmlVersionNum)) Then
       line = '<?xml version="'//                                 &
            &     xmlVersionNum(1:Len_trim(xmlVersionNum))//'"?>'
    Else
       line = '<?xml version="1.0"?>'
    End If
    Call pakoXMLwriteLine(line,error)
    !
    line = '<!DOCTYPE '//                                         &
         &     docTypeName(1:Len_trim(docTypeName))//' '//        &
         &     'SYSTEM "'//dtdFile(1:Len_trim(dtdFile))//'">'
    Call pakoXMLwriteLine(line,error)
    !
    elementToStack%element  = docTypeName(1:Len_trim(docTypeName))
    line = '<'//docTypeName(1:Len_trim(docTypeName))//' '//       &
         &   'version="'//dtdVersion(1:Len_trim(dtdVersion))//'">'
    Call pakoXMLwriteLine(line,error)
    !
    If (Present(comment)) Then
       line = '<!-- '//comment(1:Len_trim(comment))//' -->'
       Call pakoXMLwriteLine(line,error)
    End If
    !
    lineOut = ''
    Write (outputUnitXML,'(A)') lineOut(1:Len_trim(lineOut))        
    !
    Call pakoXMLpush(elementToStack,pushError=error)
    !
    Return
    !
  End Subroutine pakoXMLwriteStartXML
!!!
!!!
  Subroutine pakoXMLwriteStartElement(                            &
       &                                       element,           &
       &                                       name,              &
       &                                       comment,           &
       &                                       doCdata,           &
       &                                       doSpace,           &
       &                                       space,             &
       &                                       error,             &
       &                                       errorC             &
       &                             )
    !
    Character(len=*), Intent(IN)            :: element            ! XML element
    Character(len=*), Intent(IN),  Optional :: name               ! attribute "name"
    Character(len=*), Intent(IN),  Optional :: comment            ! XML comment
    Logical,          Intent(IN),  Optional :: doCdata            ! add start of CDATA
    Logical,          Intent(IN),  Optional :: doSpace            ! !obsolete!
    Character(len=*), Intent(IN),  Optional :: space              ! add space before or after
    !
    Logical,          Intent(OUT)           :: error              ! errorFlag
    Character(len=*), Intent(OUT), Optional :: errorC             ! errorCode
    !
    ! <DOCUMENTATION name="pakoXMLwriteStartElement">
    ! write start tag of an XML element that will have several lines
    ! of content, maybe including nested elements;
    ! information about this element is pushed on the stack.
    ! </DOCUMENTATION>
    !
    Type (pakoXMLelementT) :: elementToStack
    !    
    error  = .False.
    If(Present(errorC)) errorc="OK"
    !
    elementToStack          = elementNone
    elementToStack%element  = element
    !
    Call pakoXMLsetLevel(levelXML+1)
    !
    If (Present(space).And.                                       &
         &        index(space,'before').Ge.1) Then
       elementToStack%space = space
       Write (outputUnitXML,'(A)') ''
    End If
    !
    line = '<'//element(1:Len_trim(element))
    If (Present(name)) Then
       elementToStack%name  = name
       line = line(1:Len_trim(line))//                            &
            &      ' name="'//name   (1:Len_trim(name  ))//'"'
    End If
    line = line(1:Len_trim(line))//'>'
    If (Present(comment)) Then
       elementToStack%comment = comment
       line = line(1:Len_trim(line))//                            &
            &      ' <!-- '//comment(1:Len_trim(comment))//' -->'
    End If
    Call pakoXMLwriteLine(line,error)
    !
    If (Present(doSpace).And.doSpace) Then
       Write (outputUnitXML,'(A)') ''
    End If
    !
    If (Present(space).And.                                       &
         &        index(space,'after').Ge.1) Then
       elementToStack%space = space
       Write (outputUnitXML,'(A)') ''
    End If
    !
    If (Present(doCdata).And.doCdata) Then
       elementToStack%doCData = doCData
       Write (outputUnitXML,*) '<![CDATA['
    End If
    !
    Call pakoXMLpush(elementToStack,pushError=error)
    !
    Return
    !
  End Subroutine pakoXMLwriteStartElement
!!!
!!!
  Subroutine pakoXMLwriteEndElement(                              &
       &                                       element,           &
       &                                       name,              &
       &                                       doCdata,           &
       &                                       doSpace,           &
       &                                       space,             &
       &                                       error,             &
       &                                       errorC             &
       &                            )
    !
    Character(len=*), Intent(IN),  Optional :: element            ! XML element
    Character(len=*), Intent(IN),  Optional :: name               ! attribute "name"
    Logical,          Intent(IN),  Optional :: doCdata            ! add end of CDATA
    Logical,          Intent(IN),  Optional :: doSpace            ! !Obsolete!
    Character(len=*), Intent(IN),  Optional :: space              ! add space before or after
    !
    Logical,          Intent(OUT)           :: error              ! errorFlag
    Character(len=*), Intent(OUT), Optional :: errorC             ! errorCode
    !
    ! <DOCUMENTATION name="pakoXMLwriteEndElement">
    ! write end of an element
    !   element and name are optional and used for a consistency
    !   check against the information poped from the stack; if they 
    !   don't match, warning messages are generated; anyhow, the 
    !   information FROM THE STACK is used to write XML
    ! </DOCUMENTATION>
    !
    Character(len=128) :: errorM
    !
    Integer :: length
    !     
    Type (pakoXMLelementT) :: eFS                                 ! element From Stack
    !    
    error  = .False.
    errorM = 'OK'
    !
    eFS  = elementNone
    Call pakoXMLpop(eFS,popError=error)
    !
    If (Present(element)) Then
       If (element   (1:Len_trim(element))      .Ne.              &
            &          eFS%element(1:Len_trim(eFS%element))) Then
          error  = .True.
          errorM = 'EndElement. inconsistent element: "'          &
               &    //element(1:Len_trim(element))                &
               &    //'". I expected: "'                          &
               &    //eFS%element(1:Len_trim(eFS%element))//'"'
          Call PakoMessage(priorityE,severityE,                       &
               &                      'XMLwrite',errorM)
       End If
       If (Present(name).And.                                     &
            &           name    (1:Len_trim(name))      .Ne.      &
            &           eFS%name(1:Len_trim(eFS%name))) Then
          error  = .True.
          errorM = 'EndElement. inconsistent name: "'             &
               &    //name(1:Len_trim(name))                      &
               &    //'". I expected: "'                          & 
               &    //eFS%name(1:Len_trim(eFS%name))//'"'
          Call PakoMessage(priorityE,severityE,                       &
               &                      'XMLwrite',errorM)
       End If
    End If
    !
    If (eFS%doCdata) Then
       Write (outputUnitXML,*) ']]>'
    End If
    !
    If (Present(space).And.                                       &
         &        index(space,'before').Ge.1) Then
       Write (outputUnitXML,'(A)') ''
    End If
    !
    line = '</'//eFS%element(1:Len_trim(eFS%element))//'>'
    If (eFS%name.Ne.elementNone%name) Then
       line = line(1:Len_trim(line))//                            &
            &      ' <!-- name="'                                 &
            & //eFS%name(1:Len_trim((eFS%name)))//'" -->'
    End If
    Call pakoXMLwriteLine(line,error)
    !
    If (index(eFS%space,'after') .Ge. 1) Then
       Write (outputUnitXML,'(A)') ''
    End If
    !
    If (Present(space).And.                                       &
         &        index(space,'after').Ge.1) Then
       Write (outputUnitXML,'(A)') ''
    End If
    !
    Call pakoXMLsetLevel(levelXML-1)
    !
    If (Present(errorC)) Then
       length = Min(Len(errorM),Len(errorC))
       errorC(1:length) = errorM(1:length)
    End If
    !
    Return
    !
  End Subroutine pakoXMLwriteEndElement
!!!
!!!
  Subroutine pakoXMLwriteElement(                                 &
       &                                       element,           &
       &                                       name,              &
       &                                       value,             &
       &                                       unit,              &
       &                                       dataType,          &
       &                                       comment,           &
       &                                       text,              &
       &                                       content,           &
       &                                       space,             &
       &                                       error,             &
       &                                       errorC             &
       &                        )
    !
    Character(len=*), Intent(IN)            :: element            ! XML element
    Character(len=*), Intent(IN),  Optional :: name               ! attribute "name"
    Character(len=*), Intent(IN),  Optional :: value              ! attribute "value"
    Character(len=*), Intent(IN),  Optional :: unit               ! attribute "unit"
    Character(len=*), Intent(IN),  Optional :: dataType           ! attribute "dataType"
    Character(len=*), Intent(IN),  Optional :: comment            ! XML comment
    Character(len=*), Intent(IN),  Optional :: text               ! !OBSOLETE!
    Character(len=*), Intent(IN),  Optional :: content            ! content of element
    Character(len=*), Intent(IN),  Optional :: space              ! add space before or after
    !
    Logical,          Intent(OUT)           :: error              ! errorFlag
    Character(len=*), Intent(OUT), Optional :: errorC             ! errorCode
    !
    ! <DOCUMENTATION name="pakoXMLwriteElement">
    ! write an element that fits in one line
    ! </DOCUMENTATION>
    !
    Integer :: length
    !     
    error = .False.
    If(Present(errorC)) errorc="OK"
    !
    Call pakoXMLsetLevel(levelXML+1)
    !
    If (Present(space).And.                                       &
         &        index(space,'before').Ge.1) Then
       lineOut = ''
       Write (outputUnitXML,'(A)') lineOut(1:Len_trim(lineOut))        
    End If
    !
    line = '<'//element(1:Len_trim(element))
    !
    If (Present(name)) Then
       line = line(1:Len_trim(line))//                            &
            &               ' name="'//name(1:Len_trim(name))//'"'   
    End If
    !
    If (Present(value)) Then
       word = Adjustl(value)
       line = line(1:Len_trim(line))//                            &
            &              ' value="'//word(1:Len_trim(word))//'"'   
    End If
    !
    If (Present(unit)) Then
       line = line(1:Len_trim(line))//                            &
            &              ' unit="'//unit(1:Len_trim(unit))//'"'
    End If
    !
    If (Present(dataType)) Then
       line = line(1:Len_trim(line))//                            &
            & ' datatype="'//dataType(1:Len_trim(dataType))//'"'
    End If
    !
    line = line(1:Len_trim(line))//'>'
    !
    If (Present(content)) Then
       phrase = Adjustl(content)
       line = line(1:Len_trim(line))//phrase(1:Len_trim(phrase))
    End If
    !
    If (Present(text)) Then
       phrase = Adjustl(text)
       line = line(1:Len_trim(line))//phrase(1:Len_trim(phrase))
    End If
    !
    line = line(1:Len_trim(line))//                               &
         &           '</'//element(1:Len_trim(element))//'>'
    !
    If (Present(comment)) Then
       phrase = Adjustl(comment)
       Call pakoXMLnoir(phrase,length)
       line = line(1:Len_trim(line))//                            &
            &    ' <!-- '//phrase(1:Len_trim(phrase))//' -->'
    End If
    !
    line2 = Adjustl(line)
    !
    Call pakoXMLwriteLine(line2,error)
    !
    If (Present(space).And.                                       &
         &        index(space,'after').Ge.1) Then
       Write (outputUnitXML,'(A)') ''
    End If
    !
    Call pakoXMLsetLevel(levelXML-1)
    !
    Return
    !
  End Subroutine pakoXMLwriteElement
!!!
!!!
  Subroutine pakoXMLcase(              text,                      &
       &                               error)
    !
    Character(len=*), Intent(INOUT) :: text
    Logical,          Intent(OUT)   :: error
    !
    ! <DOCUMENTATION name="pakoXMLcase">
    ! special routine to be used by paKo:
    ! convert a keyword to mixed case according to
    ! key/value pairs in the "dictionary" pakoDict
    ! </DOCUMENTATION>
    !
    Integer :: length
    Integer :: ii
    !
    error = .False.
    !
    length = Min(Len(text),Len(word))
    word   = text(1:length)
    word   = Adjustl(text)
    Call pakoXMLupper(word)
    !
    Do ii = 1, nDict, 1
       If (word.Eq.pakoDict(ii)%key) Then
          word = pakoDict(ii)%value
          text = word
       End If
    End Do
    !
    Return
    !
  End Subroutine pakoXMLcase
!!!
!!!
  Subroutine pakoXMLdictionaryInit(                               &
       &                                       error,             &
       &                                       errorC             &
       &                          )
    !
    Logical,          Intent(OUT)           :: error              ! errorFlag
    Character(len=*), Intent(OUT), Optional :: errorC             ! errorCode
    !
    ! <DOCUMENTATION name="pakoXMLdictionaryInit">
    ! intitialize the "dictionary" pakoDict
    ! </DOCUMENTATION>
    !
    error = .False.
    If (Present(errorC)) errorC  = "OK"
    !
    nDict = 1
    !
    If (.Not.error) Call pakoXMLdictionaryAdd( 'ANTENNA'    , 'antenna'     , error)
    If (.Not.error) Call pakoXMLdictionaryAdd( 'BASIS'      , 'basis'       , error)
    If (.Not.error) Call pakoXMLdictionaryAdd( 'CALIBRATE'  , 'calibrate'   , error)
    If (.Not.error) Call pakoXMLdictionaryAdd( 'CIRCLE'     , 'circle'      , error)
    If (.Not.error) Call pakoXMLdictionaryAdd( 'CONTINUUM'  , 'Continuum'   , error)
    If (.Not.error) Call pakoXMLdictionaryAdd( 'DESCRIPTIVE', 'descriptive' , error)
    If (.Not.error) Call pakoXMLdictionaryAdd( 'DOPPLER'    , 'Doppler'     , error)
    If (.Not.error) Call pakoXMLdictionaryAdd( 'EQUATORIAL' , 'equatorial'  , error)
    If (.Not.error) Call pakoXMLdictionaryAdd( 'FIXED'      , 'fixed'       , error)
    If (.Not.error) Call pakoXMLdictionaryAdd( 'FOCUS'      , 'focus'       , error)
    If (.Not.error) Call pakoXMLdictionaryAdd( 'HADEC'      , 'haDec'       , error)
    If (.Not.error) Call pakoXMLdictionaryAdd( 'HIGH'       , 'high'        , error)
    If (.Not.error) Call pakoXMLdictionaryAdd( 'HORIZON'    , 'horizontal'  , error)
    If (.Not.error) Call pakoXMLdictionaryAdd( 'HORIZONTAL' , 'horizontal'  , error)
    If (.Not.error) Call pakoXMLdictionaryAdd( 'LINEAR'     , 'linear'      , error)
    If (.Not.error) Call pakoXMLdictionaryAdd( 'LOW'        , 'low'         , error)
    If (.Not.error) Call pakoXMLdictionaryAdd( 'NARROW'     , 'narrow'      , error)
    If (.Not.error) Call pakoXMLdictionaryAdd( 'NASMYTH'    , 'Nasmyth'     , error)
    If (.Not.error) Call pakoXMLdictionaryAdd( 'NONE'       , 'none'        , error)
    If (.Not.error) Call pakoXMLdictionaryAdd( 'ONOFF'      , 'onOff'       , error)
    If (.Not.error) Call pakoXMLdictionaryAdd( 'OTF'        , 'onTheFly'    , error)
    If (.Not.error) Call pakoXMLdictionaryAdd( 'ONTHEFLY'   , 'onTheFly'    , error)
    If (.Not.error) Call pakoXMLdictionaryAdd( 'OTFMAP'     , 'onTheFlyMap' , error)
    If (.Not.error) Call pakoXMLdictionaryAdd( 'POINTING'   , 'pointing'    , error)
    If (.Not.error) Call pakoXMLdictionaryAdd( 'PROJECTION' , 'projection'  , error)
    If (.Not.error) Call pakoXMLdictionaryAdd( 'RADIO'      , 'radio'       , error)
    If (.Not.error) Call pakoXMLdictionaryAdd( 'TIP'        , 'tip'         , error)
!TBD: maybe should allow longer keywords, e.g., horizontalTrue
    If (.Not.error) Call pakoXMLdictionaryAdd( 'TRUEHORIZON', 'horizontalT' , error)
    If (.Not.error) Call pakoXMLdictionaryAdd( 'WIDE'       , 'wide'        , error)
    If (.Not.error) Call pakoXMLdictionaryAdd( '1MHZ'       , '1MHz'        , error)
    If (.Not.error) Call pakoXMLdictionaryAdd( '4MHZ'       , '4MHz'        , error)
    If (.Not.error) Call pakoXMLdictionaryAdd( '100KHZ'     , '100kHz'      , error)
    !
!!$    If (.Not.error) Call pakoXMLdictionaryAdd( 'MERCURY'    , 'Mercury'     , error)
!!$    If (.Not.error) Call pakoXMLdictionaryAdd( 'VENUS'      , 'Venus'       , error)
!!$    !
!!$    If (.Not.error) Call pakoXMLdictionaryAdd( 'MARS'       , 'Mars'        , error)
!!$    If (.Not.error) Call pakoXMLdictionaryAdd( 'JUPITER'    , 'Jupiter'     , error)
!!$    If (.Not.error) Call pakoXMLdictionaryAdd( 'SATURN'     , 'Saturn'      , error)
!!$    If (.Not.error) Call pakoXMLdictionaryAdd( 'URANUS'     , 'Uranus'      , error)
!!$    If (.Not.error) Call pakoXMLdictionaryAdd( 'NEPTUNE'    , 'Neptune'     , error)
!!$    If (.Not.error) Call pakoXMLdictionaryAdd( 'PLUTO'      , 'Pluto'       , error)
    !
  End Subroutine pakoXMLdictionaryInit
!!!
!!!
  Subroutine pakoXMLdictionaryAdd(                                &
       &                                       key,               &
       &                                       value,             &
       &                                       error,             &
       &                                       errorC             &
       &                         )
    !
    Character(len=*), Intent(IN)            :: key                ! key to add
    Character(len=*), Intent(IN)            :: value              ! value to add
    !                 
    Logical,          Intent(OUT)           :: error              ! errorFlag
    Character(len=*), Intent(OUT), Optional :: errorC             ! errorCode
    !
    ! <DOCUMENTATION name="pakoXMLdictionaryAdd">
    ! add a key-value pair to "dictionary" pakoDict
    ! </DOCUMENTATION>
    !
    Integer lKey, lValue
    !
    error = .False.
    If (Present(errorC)) errorC  = "OK"
    !
    lKey   = Min(Len_trim(key),   lDict)
    lValue = Min(Len_trim(value), lDict)
    !
    If (nDict .Le. nDimDict) Then
       pakoDict(ndict) =                                          &
            &    pakoXMLdictionaryT(key(1:lKey),value(1:lValue))
       If (nDict .Lt. nDimDict) Then
          nDict = nDict+1
       End If
    Else
       error  = .True.
       If (Present(errorC)) errorC = "dictionary full"
    End If
    !
  End Subroutine pakoXMLdictionaryAdd
!!!
!!!
  Subroutine pakoXMLpush(                                         &
       &                                             element,     &
       &                                             pushError,   &
       &                                             pushErrorC   &
       &                )
    !
    Type (pakoXMLelementT), Intent(IN)            :: element      ! element to push on stack
    Logical,                Intent(OUT), Optional :: pushError    ! errorFlag
    Character(len=*),       Intent(OUT), Optional :: pushErrorC   ! errorCode
    !
    ! <DOCUMENTATION name="pakoXMLpush">
    ! push information about an element on the "stack" 
    ! </DOCUMENTATION>
    !
    pushError = .False.
    If (Present(pushErrorC)) pushErrorC  = "OK"
    !
    If (nStack.Lt.nDimStack) Then
       !
       nStack = nStack+1
       stack(nStack) = elementNone
       stack(nStack) = element
       !
    Else
       !
       pushError = .True.
       If (Present(pushErrorC)) pushErrorC  = "stack full"
       !
    End If
    !
    Return 
    !
  End Subroutine pakoXMLpush
!!!
!!!
  Subroutine pakoXMLpop(                                          &
       &                                             element,     &
       &                                             popError,    &
       &                                             popErrorC    &
       &               )
    !
    Type (pakoXMLelementT), Intent(OUT)           :: element      ! element to pop from stack
    Logical,                Intent(OUT)           :: popError     ! errorFlag
    Character(len=*),       Intent(OUT), Optional :: popErrorC    ! errorCode
    !
    ! <DOCUMENTATION name="pakoXMLpop">
    ! pop information about an element from the "stack"
    ! </DOCUMENTATION>
    !
    popError = .False.
    If (Present(popErrorC)) popErrorC  = "OK"
    !
    element = elementNone
    !
    If (nStack.Ge.1) Then
       !
       element = stack(nStack)
       nStack = nStack -1
       !
    Else
       !
       popError = .True.
       If (Present(popErrorC)) popErrorC  = "stack empty"
       !
    End If
    !
    Return 
    !
  End Subroutine pakoXMLpop
!!!
!!!
  Subroutine pakoXMLwriteLine(                 line,              &
       &                                       error)
    !
    Character(len=*), Intent(INOUT)         :: line               ! XML line to write 
    Logical,          Intent(OUT)           :: error              ! errorFlag
    !
    ! <DOCUMENTATION name="pakoXMLwriteLine">
    ! write one line with theproper indent to the XML output 
    ! </DOCUMENTATION>
    !
    Integer :: ii
    !
    error = .False.
    !
    lineOut = 'x'
    Do ii = 1, (levelXMl-1)*indentXML, 1
       lineOut = lineOut(1:Len_trim(lineOut)-1)//' x'
    End Do
    lineOut = lineOut(1:Len_trim(lineOut)-1)//line(1:lLine)
    !
    Write (outputUnitXML,'(A)') lineOut(1:Len_trim(lineOut))       
    !
    Return
    !
  End Subroutine pakoXMLwriteLine
!!!
!!!
  Subroutine pakoXMLnoir (CIN,NP)
    !
    ! <DOCUMENTATION name="pakoXMLnoir">
    ! eliminate unnecessary spaces. derivative of sic_noir
    ! </DOCUMENTATION>
    !
    Character(len=*) CIN
    Integer NP
    !
    Integer N,NI,I
    Logical LO,LI
    !
    LO = .False.
    NI = Len_trim(CIN)
    N  = 0
    Do I=1,NI
       LI=.True.
       If (CIN(I:I).Ne.' ' .And. CIN(I:I).Ne.'	') Then
          N = N+1
          CIN(N:N) = CIN(I:I)
       Else
          LI=.False.
          If (LO) Then
             N = N+1
             CIN(N:N) = ' '
          Endif
       Endif
       LO = LI
    End Do
    CIN(N+1:)=' '
    NP = N
    !
  End Subroutine pakoXMLnoir
!!!
!!!
  Subroutine pakoXMLupper(C)
    !
    ! <DOCUMENTATION name="pakoXMLupper">
    ! convert to upper case. derivative of sic_upper
    ! </DOCUMENTATION>
    !
    Character(len=*) C
    Integer I,IC
    !
    Do I=1,Len(C)
       If (C(I:I).Ge.'a' .And. C(I:I).Le.'z') Then
          IC=Ichar(C(I:I))+Ichar('A')-Ichar('a')
          C(I:I)=Char(IC)
       Endif
    Enddo
    !
  End Subroutine pakoXMLupper
!!!
!!!
End Module modulePakoXML
