// Created on: 2002-02-08
// Created by: Alexander GRIGORIEV
// Copyright (c) 2002-2014 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.

#include <LDOM_CharReference.hxx>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

//   Uncomment this line if you want that your XML files contain codes 0xc0-0xff
//   as defined in Latin-1 code set. Otherwise these codes are written
//   numerically as &#x..;
// #define LDOM_ALLOW_LATIN_1

namespace
{
const int NORMAL_C  = 0;
const int CHAR_REF  = -1;
const int ENTI_AMP  = 1;
const int ENTI_LT   = 2;
const int ENTI_GT   = 3;
const int ENTI_QUOT = 4;

// const int ENTI_APOS = 5;

struct entityRef
{
  const char* name;
  int         length;

  entityRef(const char* aName, const int aLen)
      : name(aName),
        length(aLen)
  {
  }
};
} // namespace

//=======================================================================
// function : Decode
// purpose  : Convertes entity and character references on input
//           Always returns the same string (shortened after replacements)
//=======================================================================

char* LDOM_CharReference::Decode(char* theSrc, Standard_Integer& theLen)
{
#define IS_EQUAL(_ptr, _string) (!memcmp(_ptr, _string, sizeof(_string) - 1))

  char *           aSrcPtr = theSrc, *aDstPtr = theSrc;
  Standard_Integer anIncrCount = 0;
  for (;;)
  {
    char* aPtr = strchr(aSrcPtr, '&');
    if (aPtr == NULL)
    {
      //        End of the loop
      aPtr = strchr(aSrcPtr, '\0');
      if (anIncrCount == 0)
        theLen = (Standard_Integer)(aPtr - theSrc);
      else
      {
        Standard_Integer aByteCount = (Standard_Integer)(aPtr - aSrcPtr);
        memmove(aDstPtr, aSrcPtr, aByteCount + 1);
        theLen = (Standard_Integer)(aDstPtr - theSrc) + aByteCount;
      }
      break;
    }
    Standard_Integer aByteCount = (Standard_Integer)(aPtr - aSrcPtr);
    if (aByteCount > 0 && aDstPtr != aSrcPtr)
      memmove(aDstPtr, aSrcPtr, aByteCount);
    aSrcPtr = aPtr;
    if (aSrcPtr[1] == '#')
    {
      unsigned long aChar;
      char*         aNewPtr;
      aDstPtr = aSrcPtr - anIncrCount + 1;
      if (aSrcPtr[2] == 'x')
        aChar = strtoul(&aSrcPtr[3], &aNewPtr, 16); // hex encoding
      else
        aChar = strtoul(&aSrcPtr[2], &aNewPtr, 10); // decimal encoding
      if (aNewPtr[0] != ';' || aChar == 0 || aChar > 255UL)
        //      Error reading an XML string
        return NULL;
      aDstPtr[-1] = (char)aChar;
      anIncrCount += (Standard_Integer)(aNewPtr - aSrcPtr);
      aSrcPtr = &aNewPtr[1];
    }
    else if (IS_EQUAL(aSrcPtr + 1, "amp;"))
    {
      aDstPtr     = aSrcPtr - anIncrCount + 1;
      aDstPtr[-1] = '&';
      anIncrCount += 4;
      aSrcPtr += 5;
    }
    else if (IS_EQUAL(aSrcPtr + 1, "lt;"))
    {
      aDstPtr     = aSrcPtr - anIncrCount + 1;
      aDstPtr[-1] = '<';
      anIncrCount += 3;
      aSrcPtr += 4;
    }
    else if (IS_EQUAL(aSrcPtr + 1, "gt;"))
    {
      aDstPtr     = aSrcPtr - anIncrCount + 1;
      aDstPtr[-1] = '>';
      anIncrCount += 3;
      aSrcPtr += 4;
    }
    else if (IS_EQUAL(aSrcPtr + 1, "quot;"))
    {
      aDstPtr     = aSrcPtr - anIncrCount + 1;
      aDstPtr[-1] = '\"';
      anIncrCount += 5;
      aSrcPtr += 6;
    }
    else if (IS_EQUAL(aSrcPtr + 1, "apos;"))
    {
      aDstPtr     = aSrcPtr - anIncrCount + 1;
      aDstPtr[-1] = '\'';
      anIncrCount += 5;
      aSrcPtr += 6;
    }
    else
    {
      aDstPtr    = aSrcPtr - anIncrCount;
      *aDstPtr++ = *aSrcPtr++;
      continue;
    }
  }
  return theSrc;
}

//=======================================================================
// function : Encode
// purpose  : This method takes the input string theSrc and returns:
//              - the pointer equal to theSrc if there are no replacements, or
//              - the pointer to a newly allocated string with replacements
//           The output parameter theLen is assigned to the length of
//           the returned string (whatever the case)
//=======================================================================

char* LDOM_CharReference::Encode(const char*            theSrc,
                                 Standard_Integer&      theLen,
                                 const Standard_Boolean isAttribute)
{
  // Initialising the constants
  static const struct entityRef entity_ref[6] = {entityRef(NULL, 0),
                                                 entityRef("&amp;", 5),
                                                 entityRef("&lt;", 4),
                                                 entityRef("&gt;", 4),
                                                 entityRef("&quot;", 6),
                                                 entityRef("&apos;", 6)};

  const char *     endSrc, *ptrSrc = theSrc;
  char*            aDest  = (char*)theSrc;
  Standard_Integer aCount = 0;
  //    Analyse if there is a non-standard character in the string
  for (;;)
  {
    const unsigned int iSrc = (unsigned int)*(const unsigned char*)ptrSrc;
    if (iSrc == 0)
    {
      endSrc = ptrSrc;
      break;
    }
    if (myTab[iSrc] != NORMAL_C)
      if (isAttribute || myTab[iSrc] != ENTI_QUOT)
        aCount++;
    ptrSrc++;
  }
  //    If there are such, copy the string with replacements
  if (!aCount)
    theLen = (Standard_Integer)(endSrc - theSrc);
  else
  {
    char* ptrDest = new char[(endSrc - theSrc) + aCount * 5 + 1];
    aDest         = ptrDest;
    for (ptrSrc = theSrc; ptrSrc < endSrc; ptrSrc++)
    {
      const unsigned int iSrc  = (unsigned int)*(const unsigned char*)ptrSrc;
      const int          aCode = myTab[iSrc];
      if (aCode == NORMAL_C) // normal (regular) character
        *ptrDest++ = *ptrSrc;
      else if (aCode == CHAR_REF)
      { // character reference
        sprintf(ptrDest, "&#x%02x;", iSrc);
        ptrDest += 6;
      }
      else // predefined entity reference
        if (isAttribute == Standard_False && aCode == ENTI_QUOT)
          *ptrDest++ = *ptrSrc;
        else
        {
          memcpy(ptrDest, entity_ref[aCode].name, entity_ref[aCode].length + 1);
          ptrDest += entity_ref[aCode].length;
        }
    }
    theLen   = (Standard_Integer)(ptrDest - aDest);
    *ptrDest = '\0';
  }
  return aDest;
}

int LDOM_CharReference::myTab[256] = {
  NORMAL_C,  // 000
  CHAR_REF,  // 001
  CHAR_REF,  // 002
  CHAR_REF,  // 003
  CHAR_REF,  // 004
  CHAR_REF,  // 005
  CHAR_REF,  // 006
  CHAR_REF,  // 007
  CHAR_REF,  // 008
  NORMAL_C,  // 009  TAB
  NORMAL_C,  // 00a  LF
  CHAR_REF,  // 00b
  CHAR_REF,  // 00c
  NORMAL_C,  // 00d  CR
  CHAR_REF,  // 00e
  CHAR_REF,  // 00f
  CHAR_REF,  // 010
  CHAR_REF,  // 011
  CHAR_REF,  // 012
  CHAR_REF,  // 013
  CHAR_REF,  // 014
  CHAR_REF,  // 015
  CHAR_REF,  // 016
  CHAR_REF,  // 017
  CHAR_REF,  // 018
  CHAR_REF,  // 019
  CHAR_REF,  // 01a
  CHAR_REF,  // 01b
  CHAR_REF,  // 01c
  CHAR_REF,  // 01d
  CHAR_REF,  // 01e
  CHAR_REF,  // 01f
  NORMAL_C,  // 020:
  NORMAL_C,  // 021: !
  ENTI_QUOT, // 022: "
  NORMAL_C,  // 023: #
  NORMAL_C,  // 024: $
  NORMAL_C,  // 025: %
  ENTI_AMP,  // 026: &
             //  ENTI_APOS,    // 027: '   Here we do never use apostrophe as delimiter
  NORMAL_C,  // 027: '
  NORMAL_C,  // 028: (
  NORMAL_C,  // 029: )
  NORMAL_C,  // 02a: *
  NORMAL_C,  // 02b: +
  NORMAL_C,  // 02c: ,
  NORMAL_C,  // 02d: -
  NORMAL_C,  // 02e: .
  NORMAL_C,  // 02f: /
  NORMAL_C,  // 030: 0
  NORMAL_C,  // 031: 1
  NORMAL_C,  // 032: 2
  NORMAL_C,  // 033: 3
  NORMAL_C,  // 034: 4
  NORMAL_C,  // 035: 5
  NORMAL_C,  // 036: 6
  NORMAL_C,  // 037: 7
  NORMAL_C,  // 038: 8
  NORMAL_C,  // 039: 9
  NORMAL_C,  // 03a: :
  NORMAL_C,  // 03b: ;
  ENTI_LT,   // 03c: <
  NORMAL_C,  // 03d: =
  ENTI_GT,   // 03e: >
  NORMAL_C,  // 03f: ?
  NORMAL_C,  // 040: @
  NORMAL_C,  // 041: A
  NORMAL_C,  // 042: B
  NORMAL_C,  // 043: C
  NORMAL_C,  // 044: D
  NORMAL_C,  // 045: E
  NORMAL_C,  // 046: F
  NORMAL_C,  // 047: G
  NORMAL_C,  // 048: H
  NORMAL_C,  // 049: I
  NORMAL_C,  // 04a: J
  NORMAL_C,  // 04b: K
  NORMAL_C,  // 04c: L
  NORMAL_C,  // 04d: M
  NORMAL_C,  // 04e: N
  NORMAL_C,  // 04f: O
  NORMAL_C,  // 050: P
  NORMAL_C,  // 051: Q
  NORMAL_C,  // 052: R
  NORMAL_C,  // 053: S
  NORMAL_C,  // 054: T
  NORMAL_C,  // 055: U
  NORMAL_C,  // 056: V
  NORMAL_C,  // 057: W
  NORMAL_C,  // 058: X
  NORMAL_C,  // 059: Y
  NORMAL_C,  // 05a: Z
  NORMAL_C,  // 05b: [
  NORMAL_C,  /* 05c: \	*/
  NORMAL_C,  // 05d: ]
  NORMAL_C,  // 05e: ^
  NORMAL_C,  // 05f: _
  NORMAL_C,  // 060: `
  NORMAL_C,  // 061: a
  NORMAL_C,  // 062: b
  NORMAL_C,  // 063: c
  NORMAL_C,  // 064: d
  NORMAL_C,  // 065: e
  NORMAL_C,  // 066: f
  NORMAL_C,  // 067: g
  NORMAL_C,  // 068: h
  NORMAL_C,  // 069: i
  NORMAL_C,  // 06a: j
  NORMAL_C,  // 06b: k
  NORMAL_C,  // 06c: l
  NORMAL_C,  // 06d: m
  NORMAL_C,  // 06e: n
  NORMAL_C,  // 06f: o
  NORMAL_C,  // 070: p
  NORMAL_C,  // 071: q
  NORMAL_C,  // 072: r
  NORMAL_C,  // 073: s
  NORMAL_C,  // 074: t
  NORMAL_C,  // 075: u
  NORMAL_C,  // 076: v
  NORMAL_C,  // 077: w
  NORMAL_C,  // 078: x
  NORMAL_C,  // 079: y
  NORMAL_C,  // 07a: z
  NORMAL_C,  // 07b: {
  NORMAL_C,  // 07c: |
  NORMAL_C,  // 07d: }
  NORMAL_C,  // 07e: ~
  NORMAL_C,  // 07f: 
  CHAR_REF,  // 080
  CHAR_REF,  // 081
  CHAR_REF,  // 082
  CHAR_REF,  // 083
  CHAR_REF,  // 084
  CHAR_REF,  // 085
  CHAR_REF,  // 086
  CHAR_REF,  // 087
  CHAR_REF,  // 088
  CHAR_REF,  // 089
  CHAR_REF,  // 08a
  CHAR_REF,  // 08b
  CHAR_REF,  // 08c
  CHAR_REF,  // 08d
  CHAR_REF,  // 08e
  CHAR_REF,  // 08f
  CHAR_REF,  // 090
  CHAR_REF,  // 091
  CHAR_REF,  // 092
  CHAR_REF,  // 093
  CHAR_REF,  // 094
  CHAR_REF,  // 095
  CHAR_REF,  // 096
  CHAR_REF,  // 097
  CHAR_REF,  // 098
  CHAR_REF,  // 099
  CHAR_REF,  // 09a
  CHAR_REF,  // 09b
  CHAR_REF,  // 09c
  CHAR_REF,  // 09d
  CHAR_REF,  // 09e
  CHAR_REF,  // 09f
  CHAR_REF,  // 0a0
  CHAR_REF,  // 0a1
  CHAR_REF,  // 0a2
  CHAR_REF,  // 0a3
  CHAR_REF,  // 0a4
  CHAR_REF,  // 0a5
  CHAR_REF,  // 0a6
  CHAR_REF,  // 0a7
  CHAR_REF,  // 0a8
  CHAR_REF,  // 0a9
  CHAR_REF,  // 0aa
  CHAR_REF,  // 0ab
  CHAR_REF,  // 0ac
  CHAR_REF,  // 0ad
  CHAR_REF,  // 0ae
  CHAR_REF,  // 0af
  CHAR_REF,  // 0b0
  CHAR_REF,  // 0b1
  CHAR_REF,  // 0b2
  CHAR_REF,  // 0b3
  CHAR_REF,  // 0b4
  CHAR_REF,  // 0b5
  CHAR_REF,  // 0b6
  CHAR_REF,  // 0b7
  CHAR_REF,  // 0b8
  CHAR_REF,  // 0b9
  CHAR_REF,  // 0ba
  CHAR_REF,  // 0bb
  CHAR_REF,  // 0bc
  CHAR_REF,  // 0bd
  CHAR_REF,  // 0be
  CHAR_REF,  // 0bf
#ifdef LDOM_ALLOW_LATIN_1
  NORMAL_C, // 0c0
  NORMAL_C, // 0c1
  NORMAL_C, // 0c2
  NORMAL_C, // 0c3
  NORMAL_C, // 0c4
  NORMAL_C, // 0c5
  NORMAL_C, // 0c6
  NORMAL_C, // 0c7
  NORMAL_C, // 0c8
  NORMAL_C, // 0c9
  NORMAL_C, // 0ca
  NORMAL_C, // 0cb
  NORMAL_C, // 0cc
  NORMAL_C, // 0cd
  NORMAL_C, // 0ce
  NORMAL_C, // 0cf
  NORMAL_C, // 0d0
  NORMAL_C, // 0d1
  NORMAL_C, // 0d2
  NORMAL_C, // 0d3
  NORMAL_C, // 0d4
  NORMAL_C, // 0d5
  NORMAL_C, // 0d6
            //  CHAR_REF,     // 0d7
  NORMAL_C, // 0d7
  NORMAL_C, // 0d8
  NORMAL_C, // 0d9
  NORMAL_C, // 0da
  NORMAL_C, // 0db
  NORMAL_C, // 0dc
  NORMAL_C, // 0dd
  NORMAL_C, // 0de
  NORMAL_C, // 0df
  NORMAL_C, // 0e0
  NORMAL_C, // 0e1
  NORMAL_C, // 0e2
  NORMAL_C, // 0e3
  NORMAL_C, // 0e4
  NORMAL_C, // 0e5
  NORMAL_C, // 0e6
  NORMAL_C, // 0e7
  NORMAL_C, // 0e8
  NORMAL_C, // 0e9
  NORMAL_C, // 0ea
  NORMAL_C, // 0eb
  NORMAL_C, // 0ec
  NORMAL_C, // 0ed
  NORMAL_C, // 0ee
  NORMAL_C, // 0ef
  NORMAL_C, // 0f0
  NORMAL_C, // 0f1
  NORMAL_C, // 0f2
  NORMAL_C, // 0f3
  NORMAL_C, // 0f4
  NORMAL_C, // 0f5
  NORMAL_C, // 0f6
            //  CHAR_REF,     // 0f7
  NORMAL_C, // 0f7
  NORMAL_C, // 0f8
  NORMAL_C, // 0f9
  NORMAL_C, // 0fa
  NORMAL_C, // 0fb
  NORMAL_C, // 0fc
  NORMAL_C, // 0fd
  NORMAL_C, // 0fe
  NORMAL_C  // 0ff
#else
  CHAR_REF, // 0c0
  CHAR_REF, // 0c1
  CHAR_REF, // 0c2
  CHAR_REF, // 0c3
  CHAR_REF, // 0c4
  CHAR_REF, // 0c5
  CHAR_REF, // 0c6
  CHAR_REF, // 0c7
  CHAR_REF, // 0c8
  CHAR_REF, // 0c9
  CHAR_REF, // 0ca
  CHAR_REF, // 0cb
  CHAR_REF, // 0cc
  CHAR_REF, // 0cd
  CHAR_REF, // 0ce
  CHAR_REF, // 0cf
  CHAR_REF, // 0d0
  CHAR_REF, // 0d1
  CHAR_REF, // 0d2
  CHAR_REF, // 0d3
  CHAR_REF, // 0d4
  CHAR_REF, // 0d5
  CHAR_REF, // 0d6
  CHAR_REF, // 0d7
  CHAR_REF, // 0d8
  CHAR_REF, // 0d9
  CHAR_REF, // 0da
  CHAR_REF, // 0db
  CHAR_REF, // 0dc
  CHAR_REF, // 0dd
  CHAR_REF, // 0de
  CHAR_REF, // 0df
  CHAR_REF, // 0e0
  CHAR_REF, // 0e1
  CHAR_REF, // 0e2
  CHAR_REF, // 0e3
  CHAR_REF, // 0e4
  CHAR_REF, // 0e5
  CHAR_REF, // 0e6
  CHAR_REF, // 0e7
  CHAR_REF, // 0e8
  CHAR_REF, // 0e9
  CHAR_REF, // 0ea
  CHAR_REF, // 0eb
  CHAR_REF, // 0ec
  CHAR_REF, // 0ed
  CHAR_REF, // 0ee
  CHAR_REF, // 0ef
  CHAR_REF, // 0f0
  CHAR_REF, // 0f1
  CHAR_REF, // 0f2
  CHAR_REF, // 0f3
  CHAR_REF, // 0f4
  CHAR_REF, // 0f5
  CHAR_REF, // 0f6
  CHAR_REF, // 0f7
  CHAR_REF, // 0f8
  CHAR_REF, // 0f9
  CHAR_REF, // 0fa
  CHAR_REF, // 0fb
  CHAR_REF, // 0fc
  CHAR_REF, // 0fd
  CHAR_REF, // 0fe
  CHAR_REF  // 0ff
#endif // LDOM_ALLOW_LATIN_1
};
