{ ************************************************************ }
{ Delphi VCL Extensions }
{ }
{ TsgParser class for XML parsing }
{ }
{ Copyright (c) 2002-2013 SoftGold software company }
{ }
{ ************************************************************ }

unit sgXMLParser;
{$INCLUDE SGDXF.INC}
// {$DEFINE SG_PARSER_USE_RTTI}
{$DEFINE SG_USE_DICTIONARY_BY_NAME}
{$DEFINE SG_XML_DATA_TYPE}
// {$IFDEF SG_CPUX64}
{$DEFINE SG_DATA64}
// {$ENDIF}
// {$DEFINE SG_EXPORT_WITH_STRINGS}

interface

uses
  Windows, SysUtils, Classes, Graphics, sgFunction, sgConsts, sgUnicode
{$IFNDEF SGFPC}, msxml, msxmldom {$ENDIF}
{$IFDEF SGFPC}, FPimage {$ENDIF}
{$IFDEF SGDEL_2009}, Character, Generics.Collections{$ENDIF}
{$IFDEF SG_PARSER_DEBUG}, ComCtrls{$ENDIF}
{$IFDEF SG_PARSER_USE_RTTI}, TypInfo{$ENDIF}
{$IFDEF SGDEL_XE2}
    , System.Types, System.UITypes
{$ENDIF}
    ;

const
  cnstDefCodePage = CP_UTF8;
  cnstEncoding = 'encoding';
  cnstVersion = 'version';
  cnstStandalone = 'standalone';
  cnstLineBreak = ',';
  cnstLineBreakDef = ' ';
  cnstLineBreakStyle = ';';
  cnstLineBreakText = #1;
  cnstPercentUpdateProgress: Integer = 5;
  cnstSymbolByHandle = #2;
  cnstXML = 'xml';
  cnstTrue = 'true';
  cnstFalse = 'false';
  cnstNodeValue = 'NodeValue';

type
  PString = ^string;

  TsgProgressEvent = procedure(Stage: TProgressStage; Done, Count: Integer)
    of object;

  TsgNodeType = (ntUndefined, ntElement, ntAttribute, ntText, ntCData,
    ntEntityRef, ntEntity, ntProcessingInstr, ntComment, ntDocument, ntDocType,
    ntDocFragment, ntNotation);

  TsgTagID = (idUndefined, idCX, idCY, idR, idRX, idRY, idStyle, idX1, idY1,
    idX2, idY2, idX, idY, idDX, idDY, idPoints, idWidth, idHeight, idD,
    idPathLength, idID, idHref, idTransform, idViewBox, idPreserveAspectRatio,
    idClass, idClipPath, idStroke, idStrokeWidth, idStrokeLineCap,
    idStrokeLineJoin, idStrokeMiterlimit, idStrokeDashArray, idStrokeDashOffset,
    idStrokeOpacity, idVectorEffect, idFill, idFillOpacity, idFillRule,
    idGradientUnits, idGradientTransform, idSpreadMethod, idFX, idFY,
    idStopColor, idStopOpacity, idOffset, idPatternUnits, idPatternContentUnits,
    idFont, idFontFamily, idFontStyle, idFontWeight, idFontSize, idFontVariant,
    idFontStretch, idTextAnchor, idTextDecoration, idDisplay, idVisibility,
    idOpacity);

  TsgUnitsOfMeasurements = (umUndefined, umPR, umPX, umMM, umCM, umM, umIN,
    umPT, umPC, umEM);

{$IFDEF SG_XML_DATA_TYPE}
(*$HPPEMIT '#pragma pack(push,1)  '*)
(*$HPPEMIT 'struct TsgDataValue   '*)
(*$HPPEMIT '{                     '*)
(*$HPPEMIT '  Sgconsts::TsgDataType DType;  '*)
(*$HPPEMIT '  union                 '*)
(*$HPPEMIT '  {                     '*)
{$IFDEF SG_DATA64}
(*$HPPEMIT '		struct dtPoint    '*)
(*$HPPEMIT '		{                 '*)
(*$HPPEMIT '			Types::TPoint AsPoint;   '*)
(*$HPPEMIT '		};                '*)
(*$HPPEMIT '		struct            '*)
(*$HPPEMIT '		{                 '*)
(*$HPPEMIT '			double AsDouble;   '*)
(*$HPPEMIT '		};                '*)
(*$HPPEMIT '		struct            '*)
(*$HPPEMIT '		{                 '*)
(*$HPPEMIT '			__int64 AsInt64;   '*)
(*$HPPEMIT '		};                '*)
(*$HPPEMIT '		struct            '*)
(*$HPPEMIT '		{                 '*)
(*$HPPEMIT '			unsigned __int64 AsHandle;   '*)
(*$HPPEMIT '		};                '*)
{$ELSE}
(*$HPPEMIT '		struct dtPoint    '*)
(*$HPPEMIT '		{                 '*)
(*$HPPEMIT '			Types::TPoint *AsPoint;   '*)
(*$HPPEMIT '		};                '*)
(*$HPPEMIT '		struct            '*)
(*$HPPEMIT '		{                 '*)
(*$HPPEMIT '			Double *AsDouble;   '*)
(*$HPPEMIT '		};                '*)
(*$HPPEMIT '		struct            '*)
(*$HPPEMIT '		{                 '*)
(*$HPPEMIT '			__int64 *AsInt64;   '*)
(*$HPPEMIT '		};                '*)
(*$HPPEMIT '		struct            '*)
(*$HPPEMIT '		{                 '*)
(*$HPPEMIT '			unsigned __int64 *AsHandle;   '*)
(*$HPPEMIT '		};                '*)
{$ENDIF}
(*$HPPEMIT '		struct            '*)
(*$HPPEMIT '		{                 '*)
(*$HPPEMIT '			Sgconsts::TsgColorCAD *AsColorCAD;  '*)
(*$HPPEMIT '		};                '*)
(*$HPPEMIT '		struct            '*)
(*$HPPEMIT '		{                 '*)
(*$HPPEMIT '			Sgconsts::TsgVersion *AsVersion;  '*)
(*$HPPEMIT '		};                '*)
(*$HPPEMIT '		struct            '*)
(*$HPPEMIT '		{                 '*)
(*$HPPEMIT '			Sgconsts::TFRect *AsFRect;  '*)
(*$HPPEMIT '		};                '*)
(*$HPPEMIT '		struct            '*)
(*$HPPEMIT '		{                 '*)
(*$HPPEMIT '			Sgconsts::TF2DRect *AsF2DRect;  '*)
(*$HPPEMIT '		};                '*)
(*$HPPEMIT '		struct            '*)
(*$HPPEMIT '		{                 '*)
(*$HPPEMIT '			Types::TRect *AsRect; '*)
(*$HPPEMIT '		};                '*)
(*$HPPEMIT '		struct            '*)
(*$HPPEMIT '		{                 '*)
(*$HPPEMIT '			Sgconsts::TFPoint *AsFPoint;  '*)
(*$HPPEMIT '		};                '*)
(*$HPPEMIT '		struct            '*)
(*$HPPEMIT '		{                 '*)
(*$HPPEMIT '			Sgconsts::TF2DPoint *AsF2DPoint;  '*)
(*$HPPEMIT '		};                '*)
(*$HPPEMIT '		struct            '*)
(*$HPPEMIT '		{                 '*)
(*$HPPEMIT '			System::String *AsString;  '*)
(*$HPPEMIT '		};                '*)
(*$HPPEMIT '		struct            '*)
(*$HPPEMIT '		{                 '*)
(*$HPPEMIT '			void *AsPointer;  '*)
(*$HPPEMIT '		};                '*)
(*$HPPEMIT '		struct            '*)
(*$HPPEMIT '		{                 '*)
(*$HPPEMIT '			float AsSingle; '*)
(*$HPPEMIT '		};                '*)
(*$HPPEMIT '		struct            '*)
(*$HPPEMIT '		{                 '*)
(*$HPPEMIT '			Graphics::TColor AsColor; '*)
(*$HPPEMIT '		};                '*)
(*$HPPEMIT '		struct            '*)
(*$HPPEMIT '		{                 '*)
(*$HPPEMIT '			int AsInteger;  '*)
(*$HPPEMIT '		};                '*)
(*$HPPEMIT '		struct            '*)
(*$HPPEMIT '		{                 '*)
(*$HPPEMIT '			System::Word AsWord;  '*)
(*$HPPEMIT '		};                '*)
(*$HPPEMIT '		struct            '*)
(*$HPPEMIT '		{                 '*)
(*$HPPEMIT '			System::Byte AsByte;  '*)
(*$HPPEMIT '		};                '*)
(*$HPPEMIT '		struct            '*)
(*$HPPEMIT '		{                 '*)
(*$HPPEMIT '			bool AsBool;    '*)
(*$HPPEMIT '		};                '*)
(*$HPPEMIT '		struct            '*)
(*$HPPEMIT '		{                 '*)
(*$HPPEMIT '			void *Value;    '*)
(*$HPPEMIT '		};                '*)
(*$HPPEMIT '  };                  '*)
(*$HPPEMIT '};                    '*)
(*$HPPEMIT '#pragma pack(pop)     '*)

  TsgDataValue = packed record
    DType: TsgDataType;
    case TsgDataType of
      dtUndefined:
        (Value: Pointer);
      dtBool:
        (AsBool: Boolean);
      dtByte:
        (AsByte: Byte);
      dtWord:
        (AsWord: Word);
      dtInteger:
        (AsInteger: Integer);
      dtColor:
        (AsColor: TColor);
      dtSingle:
        (AsSingle: Single);
      dtPointer:
        (AsPointer: Pointer);
      dtString:
        (AsString: PString);
      dtTF2DPoint:
        (AsF2DPoint: PF2DPoint);
      dtTFPoint:
        (AsFPoint: PFPoint);
      dtRect:
        (AsRect: PRect);
      dtTF2DRect:
        (AsF2DRect: PF2DRect);
      dtTFRect:
        (AsFRect: PFRect);
      dtVersion:
        (AsVersion: PsgVersion);
      dtColorCAD:
        (AsColorCAD: PsgColorCAD);
{$IFDEF SG_DATA64}
      dtHandle:
        (AsHandle: UInt64);
      dtInt64:
        (AsInt64: Int64);
      dtDouble:
        (AsDouble: Double);
      dtPoint:
        (AsPoint: TPoint);
{$ELSE}
      dtHandle:
        (AsHandle: PUInt64);
      dtInt64:
        (AsInt64: PInt64);
      dtDouble:
        (AsDouble: PDouble);
      dtPoint:
        (AsPoint: PPoint);
{$ENDIF}
  end;
{$EXTERNALSYM TsgDataValue}
{$ENDIF}

  TsgNodeSample = class;
  TsgParser = class;

  TsgData = class
  private
{$IFDEF SG_XML_DATA_TYPE}
    FValue: TsgDataValue;
{$ELSE}
    FValue: string;
{$ENDIF}
    procedure ClearValue;
    function GetMeasurements: TsgUnitsOfMeasurements;
    function GetValueAsBool: Boolean;
    function GetValueAsByte: Byte;
    function GetValueAsColor: TColor;
    function GetValueAsColorCAD: TsgColorCAD;
    function GetValueAsDouble: Double;
    function GetValueAsFPoint: TFPoint;
    function GetValueAsF2DPoint: TF2DPoint;
    function GetValueAsFRect: TFRect;
    function GetValueAsF2DRect: TF2DRect;
    function GetValueAsHandle: UInt64;
    function GetValueAsInt: Integer;
    function GetValueAsInt64: Int64;
    function GetValueAsPoint: TPoint;
    function GetValueAsPointer: Pointer;
    function GetValueAsRect: TRect;
    function GetValueAsVersion: TsgVersion;
    function GetValueAsWord: Word;
    function GetValueAsSingle: Single;
    function GetValueAsString: string;
    function GetValueAsText: string;
    procedure SetValueAsBool(const AValue: Boolean);
    procedure SetValueAsByte(const AValue: Byte);
    procedure SetValueAsDouble(const AValue: Double);
    procedure SetValueAsFPoint(const AValue: TFPoint);
    procedure SetValueAsF2DPoint(const AValue: TF2DPoint);
    procedure SetValueAsFRect(const AValue: TFRect);
    procedure SetValueAsF2DRect(const AValue: TF2DRect);
    procedure SetValueAsHandle(const AValue: UInt64);
    procedure SetValueAsInt(const AValue: Integer);
    procedure SetValueAsInt64(const AValue: Int64);
    procedure SetValueAsPoint(const AValue: TPoint);
    procedure SetValueAsPointer(const AValue: Pointer);
    procedure SetValueAsColor(const AValue: TColor);
    procedure SetValueAsColorCAD(const AValue: TsgColorCAD);
    procedure SetValueAsVersion(const AValue: TsgVersion);
    procedure SetValueAsSingle(const AValue: Single);
    procedure SetValueAsString(const AValue: string);
    procedure SetValueAsText(const AValue: string);
    procedure SetValueAsRect(const AValue: TRect);
    procedure SetValueAsWord(const AValue: Word);
{$IFDEF SG_XML_DATA_TYPE}
    procedure NewValueWithType(const ADataType: TsgDataType);
    procedure SetValueWithType(const ADataType: TsgDataType;
      const AValue: Pointer);
{$ENDIF}
  protected
    class function GetAsBool(const AData: TsgData): Boolean;
    {$IFDEF SG_INLINE}inline; {$ENDIF}
    class function GetAsByte(const AData: TsgData): Byte;
    {$IFDEF SG_INLINE}inline; {$ENDIF}
    class function GetAsDouble(const AData: TsgData): Double;
    {$IFDEF SG_INLINE}inline; {$ENDIF}
    class function GetAsFPoint(const AData: TsgData): TFPoint;
    {$IFDEF SG_INLINE}inline; {$ENDIF}
    class function GetAsFRect(const AData: TsgData): TFRect;
    {$IFDEF SG_INLINE}inline; {$ENDIF}
    class function GetAsF2DRect(const AData: TsgData): TF2DRect;
    {$IFDEF SG_INLINE}inline; {$ENDIF}
    class function GetAsHandle(const AData: TsgData): UInt64;
    {$IFDEF SG_INLINE}inline; {$ENDIF}
    class function GetAsInt(const AData: TsgData): Integer;
    {$IFDEF SG_INLINE}inline; {$ENDIF}
    class function GetAsInt64(const AData: TsgData): Int64;
    {$IFDEF SG_INLINE}inline; {$ENDIF}
    class function GetAsPointer(const AData: TsgData): Pointer;
    {$IFDEF SG_INLINE}inline; {$ENDIF}
    class function GetAsStr(const AData: TsgData): string;
    {$IFDEF SG_INLINE}inline; {$ENDIF}
    class function GetAsText(const AData: TsgData): string;
    {$IFDEF SG_INLINE}inline; {$ENDIF}
    class procedure SetAsBool(const AData: TsgData; const AValue: Boolean);
    {$IFDEF SG_INLINE}inline; {$ENDIF}
    class procedure SetAsByte(const AData: TsgData; const AValue: Byte);
    {$IFDEF SG_INLINE}inline; {$ENDIF}
    class procedure SetAsDouble(const AData: TsgData; const AValue: Double);
    {$IFDEF SG_INLINE}inline; {$ENDIF}
    class procedure SetAsFPoint(const AData: TsgData; const AValue: TFPoint);
    {$IFDEF SG_INLINE}inline; {$ENDIF}
    class procedure SetAsFRect(const AData: TsgData; const AValue: TFRect);
    {$IFDEF SG_INLINE}inline; {$ENDIF}
    class procedure SetAsF2DRect(const AData: TsgData; const AValue: TF2DRect);
    {$IFDEF SG_INLINE}inline; {$ENDIF}
    class procedure SetAsHandle(const AData: TsgData; const AValue: UInt64);
    {$IFDEF SG_INLINE}inline; {$ENDIF}
    class procedure SetAsInt(const AData: TsgData; const AValue: Integer);
    {$IFDEF SG_INLINE}inline; {$ENDIF}
    class procedure SetAsInt64(const AData: TsgData; const AValue: Int64);
    {$IFDEF SG_INLINE}inline; {$ENDIF}
    class procedure SetAsPointer(const AData: TsgData; const AValue: Pointer);
    {$IFDEF SG_INLINE}inline; {$ENDIF}
    class procedure SetAsStr(const AData: TsgData; const AValue: string);
    {$IFDEF SG_INLINE}inline; {$ENDIF}
    class procedure SetAsText(const AData: TsgData; const AValue: string);
    {$IFDEF SG_INLINE}inline; {$ENDIF}
    class procedure DoError(const AMessage: string);
  public
    constructor Create;
    destructor Destroy; override;
    procedure Assign(const AData: TsgData);
    function Compare(const AData: TsgData;
      const ACaseSensitivity: Boolean = True): Integer;
    function IsEqual(const AData: TsgData;
      const ACaseSensitivity: Boolean = True): Boolean;
    property Measurements: TsgUnitsOfMeasurements read GetMeasurements;
{$IFDEF SG_XML_DATA_TYPE}
    property Value: TsgDataValue read FValue;
{$ELSE}
    property Value: string read FValue write FValue;
{$ENDIF}
    property ValueAsBool: Boolean read GetValueAsBool write SetValueAsBool;
    property ValueAsByte: Byte read GetValueAsByte write SetValueAsByte;
    property ValueAsColor: TColor read GetValueAsColor write SetValueAsColor;
    property ValueAsColorCAD: TsgColorCAD read GetValueAsColorCAD
      write SetValueAsColorCAD;
    property ValueAsDouble: Double read GetValueAsDouble write SetValueAsDouble;
    property ValueAsInt: Integer read GetValueAsInt write SetValueAsInt;
    property ValueAsInt64: Int64 read GetValueAsInt64 write SetValueAsInt64;
    property ValueAsHandle: UInt64 read GetValueAsHandle write SetValueAsHandle;
    property ValueAsFPoint: TFPoint read GetValueAsFPoint
      write SetValueAsFPoint;
    property ValueAsF2DPoint: TF2DPoint read GetValueAsF2DPoint
      write SetValueAsF2DPoint;
    property ValueAsFRect: TFRect read GetValueAsFRect write SetValueAsFRect;
    property ValueAsF2DRect: TF2DRect read GetValueAsF2DRect
      write SetValueAsF2DRect;
    property ValueAsPoint: TPoint read GetValueAsPoint write SetValueAsPoint;
    property ValueAsPointer: Pointer read GetValueAsPointer
      write SetValueAsPointer;
    property ValueAsRect: TRect read GetValueAsRect write SetValueAsRect;
    property ValueAsVersion: TsgVersion read GetValueAsVersion
      write SetValueAsVersion;
    property ValueAsSingle: Single read GetValueAsSingle write SetValueAsSingle;
    property ValueAsStr: string read GetValueAsString write SetValueAsString;
    property ValueAsText: string read GetValueAsText write SetValueAsText;
    property ValueAsWord: Word read GetValueAsWord write SetValueAsWord;
  end;

  TsgNodeList = class(TsgNotificationList) // not use freelist and clearlist
  private
    FFlags: Byte;
    FSortedList: TStringList;
    procedure CreateSortedList;
    function GetCaseSensitivity: Boolean;
    function GetNode(const AIndex: Integer): TsgNodeSample;
    function GetNodeByName(const AName: string): TsgNodeSample;
    function GetQwickFind: Boolean;
    procedure SetCaseSensitivity(const AValue: Boolean);
    procedure SetQwickFind(const AValue: Boolean);
  protected
    procedure Notify(Ptr: Pointer; Action: TListNotification); override;
    procedure UpdateNode(const ANode: TsgNodeSample);
  public
    class function CreateNodeList(const AQwickFind: Boolean = False)
      : TsgNodeList;
    destructor Destroy; override;
    procedure ClearNodes;
    property CaseSensitivity: Boolean read GetCaseSensitivity
      write SetCaseSensitivity;
    property NodeByName[const AName: string]: TsgNodeSample
      read GetNodeByName; default;
    property Nodes[const AIndex: Integer]: TsgNodeSample read GetNode;
    property QwickFind: Boolean read GetQwickFind write SetQwickFind;
  end;

  TsgNodeCompareFunc = function(const ANode: TsgNodeSample;
    const ACompareChilds: Boolean = False): Integer of object;
  TsgNodeSampleClass = class of TsgNodeSample;

  TsgNodeSamplePosition = record
    X, Y: Integer;
  end;

  TsgNodeSample = class
  private
    FPosition: TsgNodeSamplePosition;
    FOwner: TsgNodeSample;
    FEntity: TObject;
    FUserData: Pointer;
    function GetCountOwner: Integer;
    function GetSpacesBySave: string;
{$IFNDEF SG_EXPORT_WITH_STRINGS}
    procedure SaveString(const AStream: TStream; const AString: string);
{$ENDIF}
  protected
    function CompareAttributes(const ANode: TsgNodeSample): Integer;
    function CompareChilds(const ANode: TsgNodeSample;
      const ACompareChilds: Boolean = False): Integer;
    procedure CheckText(const ANode: TsgNodeSample); virtual;
    function GetAttribNode(const AIndex: Integer): TsgNodeSample; virtual;
    function GetAttributeNodesCount: Integer; virtual;
    function GetChildNode(const AIndex: Integer): TsgNodeSample; virtual;
    function GetChildNodeCount: Integer; virtual;
    function GetClassID: Cardinal; virtual;
    function GetClosed: Boolean; virtual;
    function GetDataInternal(const AOwner: Pointer): Pointer; virtual;
    function GetPosition: TsgNodeSamplePosition;
    function GetFullName: string;
    function GetName: string; virtual;
    function GetNodeType: TsgNodeType; virtual;
    function GetOwnerList(const AType: TsgNodeType): TsgNodeList; virtual;
    function GetPostamble: Boolean; virtual;
    function GetPrefix: string; virtual;
    function GetSaveValueStr: string; virtual;
    function GetTextAsBool: Boolean;
    function GetTextAsDouble: Double;
    function GetTextAsFPoint: TFPoint;
    function GetTextAsFRect: TFRect;
    function GetTextAsF2DRect: TF2DRect;
    function GetTextAsHandle: UInt64;
    function GetTextAsInt: Integer;
    function GetTextAsInt64: Int64;
    function GetTextAsPointer: Pointer;
    function GetTextAsStr: string;
    function GetTextAsText: string;
    function GetTextData: TsgData; virtual;
    function GetUnitsOfMeasurements: TsgUnitsOfMeasurements;
    function GetValueAsBool: Boolean;
    function GetValueAsDouble: Double;
    function GetValueAsFPoint: TFPoint;
    function GetValueAsFRect: TFRect;
    function GetValueAsF2DRect: TF2DRect;
    function GetValueAsHandle: UInt64;
    function GetValueAsInt: Integer;
    function GetValueAsInt64: Int64;
    function GetValueAsPointer: Pointer;
    function GetValueAsStr: string;
    function GetValueAsText: string;
    function GetValueData: TsgData; virtual;
    procedure Read(const AParser: TsgParser); virtual;
    procedure ReName(const ANewName: string);
{$IFDEF SG_EXPORT_WITH_STRINGS}
    procedure SaveToStrings(const AStrings: TStrings); virtual;
{$ELSE}
    procedure SaveToStream(const AStream: TStream); virtual;
{$ENDIF}
    procedure SetClosed(const AValue: Boolean); virtual;
    procedure SetFullName(const AValue: string);
    procedure SetPosition(const AValue: TsgNodeSamplePosition);
    procedure SetName(const AValue: string); virtual;
    procedure SetNodeType(const AValue: TsgNodeType); virtual;
    procedure SetPostamble(const AValue: Boolean); virtual;
    procedure SetPrefix(const AValue: string); virtual;
    procedure SetReader(const AReader: Pointer); virtual;
    procedure SetTextAsBool(const AValue: Boolean);
    procedure SetTextAsDouble(const AValue: Double);
    procedure SetTextAsFPoint(const AValue: TFPoint);
    procedure SetTextAsFRect(const AValue: TFRect);
    procedure SetTextAsF2DRect(const AValue: TF2DRect);
    procedure SetTextAsHandle(const AValue: UInt64);
    procedure SetTextAsInt(const AValue: Integer);
    procedure SetTextAsInt64(const AValue: Int64);
    procedure SetTextAsPointer(const AValue: Pointer);
    procedure SetTextAsStr(const AValue: string);
    procedure SetTextAsText(const AValue: string);
    procedure SetValueAsBool(const AValue: Boolean);
    procedure SetValueAsDouble(const AValue: Double);
    procedure SetValueAsFPoint(const AValue: TFPoint);
    procedure SetValueAsFRect(const AValue: TFRect);
    procedure SetValueAsF2DRect(const AValue: TF2DRect);
    procedure SetValueAsHandle(const AValue: UInt64);
    procedure SetValueAsInt(const AValue: Integer);
    procedure SetValueAsInt64(const AValue: Int64);
    procedure SetValueAsPointer(const AValue: Pointer);
    procedure SetValueAsStr(const AValue: string);
    procedure SetValueAsText(const AValue: string);

    property Closed: Boolean read GetClosed write SetClosed;
    property Entity: TObject read FEntity write FEntity;
    property Postamble: Boolean read GetPostamble write SetPostamble;
    property TextAsText: string read GetTextAsText write SetTextAsText;
    property ValueAsText: string read GetValueAsText write SetValueAsText;
    property UserData: Pointer read FUserData write FUserData;
  public
    constructor Create; virtual;
    procedure Assign(const Obj: TObject); virtual;
    procedure Clear; virtual;
    function Compare(const ANode: TsgNodeSample;
      const ACompareChilds: Boolean = False): Integer; virtual;
{$IFDEF SGDEL_2009}
    function GetAttributesEnumerable: IEnumerable<TsgNodeSample>; virtual;
    function GetChildsEnumerable: IEnumerable<TsgNodeSample>; virtual;
{$ENDIF}
    function GetAttributeByTagID(const ATag: TsgTagID): TsgNodeSample;
    function GetAttributeByName(const AName: string): TsgNodeSample; virtual;
    function GetChildByName(const AName: string): TsgNodeSample; virtual;
    function GetData(const AReader, AOwner: Pointer): Pointer;
    function HasAttribute(const AName: string): Boolean;
    function HasAttributeNodes: Boolean;
    function HasChildNodes: Boolean;
    function HasPosition: Boolean;
    function IsEqual(const ANode: TsgNodeSample;
      const ACompareChilds: Boolean = False): Boolean;
    property AttributeNodes[const AIndex: Integer]: TsgNodeSample
      read GetAttribNode;
    property AttributeNodesCount: Integer read GetAttributeNodesCount;
    property ChildNodes[const AIndex: Integer]: TsgNodeSample read GetChildNode;
    property ChildNodesCount: Integer read GetChildNodeCount;
    property ClassID: Cardinal read GetClassID;
    property FullName: string read GetFullName write SetFullName;
    property Name: string read GetName write ReName;
    property NodeType: TsgNodeType read GetNodeType;
    property Owner: TsgNodeSample read FOwner;
    property Position: TsgNodeSamplePosition read GetPosition;
    property Prefix: string read GetPrefix write SetPrefix;
    property Text: string read GetTextAsStr write SetTextAsStr;
    property TextData: TsgData read GetTextData;
    property TextAsBool: Boolean read GetTextAsBool write SetTextAsBool;
    property TextAsDouble: Double read GetTextAsDouble write SetTextAsDouble;
    property TextAsInt: Integer read GetTextAsInt write SetTextAsInt;
    property TextAsInt64: Int64 read GetTextAsInt64 write SetTextAsInt64;
    property TextAsHandle: UInt64 read GetTextAsHandle write SetTextAsHandle;
    property TextAsFPoint: TFPoint read GetTextAsFPoint write SetTextAsFPoint;
    property TextAsFRect: TFRect read GetTextAsFRect write SetTextAsFRect;
    property TextAsF2DRect: TF2DRect read GetTextAsF2DRect
      write SetTextAsF2DRect;
    property TextAsStr: string read GetTextAsStr write SetTextAsStr;
    property TextAsPointer: Pointer read GetTextAsPointer
      write SetTextAsPointer;
    property UnitsOfMeasurements: TsgUnitsOfMeasurements
      read GetUnitsOfMeasurements;
    property Value: string read GetValueAsStr write SetValueAsStr;
    property ValueData: TsgData read GetValueData;
    property ValueAsBool: Boolean read GetValueAsBool write SetValueAsBool;
    property ValueAsDouble: Double read GetValueAsDouble write SetValueAsDouble;
    property ValueAsInt: Integer read GetValueAsInt write SetValueAsInt;
    property ValueAsInt64: Int64 read GetValueAsInt64 write SetValueAsInt64;
    property ValueAsHandle: UInt64 read GetValueAsHandle write SetValueAsHandle;
    property ValueAsFPoint: TFPoint read GetValueAsFPoint
      write SetValueAsFPoint;
    property ValueAsFRect: TFRect read GetValueAsFRect write SetValueAsFRect;
    property ValueAsF2DRect: TF2DRect read GetValueAsF2DRect
      write SetValueAsF2DRect;
    property ValueAsPointer: Pointer read GetValueAsPointer
      write SetValueAsPointer;
    property ValueAsStr: string read GetValueAsStr write SetValueAsStr;
  end;

  TsgNodeComment = class(TsgNodeSample)
  private
    FValue: TsgData;
  protected
    function GetNodeType: TsgNodeType; override;
    function GetValueData: TsgData; override;
    procedure Read(const AParser: TsgParser); override;
{$IFDEF SG_EXPORT_WITH_STRINGS}
    procedure SaveToStrings(const AStrings: TStrings); override;
{$ELSE}
    procedure SaveToStream(const AStream: TStream); override;
{$ENDIF}
  public
    constructor Create; override;
    destructor Destroy; override;
    procedure Assign(const Obj: TObject); override;
  end;

  TsgNodeAttrib = class(TsgNodeComment)
  private
    FPrefix: string;
{$IFNDEF SG_USE_DICTIONARY_BY_NAME}
    FName: string;
{$ELSE}
    FNameIndex: Integer;
    procedure SetFName(const AValue: string);
    function GetFName: string;
    property FName: string read GetFName write SetFName;
{$ENDIF}
  protected
    function GetName: string; override;
    function GetNodeType: TsgNodeType; override;
    function GetPrefix: string; override;
    procedure Read(const AParser: TsgParser); override;
    procedure SetName(const AValue: string); override;
    procedure SetPrefix(const AValue: string); override;
  public
    procedure Assign(const Obj: TObject); override;
  end;

  TsgNode = class(TsgNodeAttrib)
  private
    FAttributes: TsgNodeList;
    FChilds: TsgNodeList;
    FFlags: Byte;
    FText: TsgData;
    FType: TsgNodeType;
  protected
    function GetAttribNode(const AIndex: Integer): TsgNodeSample; override;
    function GetAttributeNodesCount: Integer; override;
    function GetChildNode(const AIndex: Integer): TsgNodeSample; override;
    function GetChildNodeCount: Integer; override;
    function GetClosed: Boolean; override;
    function GetNodeType: TsgNodeType; override;
    function GetOwnerList(const AType: TsgNodeType): TsgNodeList; override;
    function GetPostamble: Boolean; override;
    function GetSaveValueStr: string; override;
    function GetTextData: TsgData; override;
    function RemoveByName(const ANodes: TsgNodeList; const AName: string;
      const ADoFree: Boolean): Boolean;
    function RemoveNode(const ANodes: TsgNodeList;
      const ANode: TsgNodeSample): Boolean;
    procedure Read(const AParser: TsgParser); override;
    procedure ReadTextAndChildNodes(const AParser: TsgParser);
    procedure SetClosed(const AValue: Boolean); override;
    procedure SetNodeType(const AValue: TsgNodeType); override;
    procedure SetPostamble(const AValue: Boolean); override;
  public
    constructor Create; override;
    destructor Destroy; override;
{$IFDEF SG_PARSER_USE_RTTI}
    procedure LoadFromObject(const AObj: TObject; AClass: TClass = nil);
{$ENDIF}
{$IFDEF SGDEL_2009}
    function GetAttributesEnumerable: IEnumerable<TsgNodeSample>; override;
    function GetChildsEnumerable: IEnumerable<TsgNodeSample>; override;
{$ENDIF}
    procedure AddAttrib(const ANode: TsgNodeSample);
    function AddAttribNV(const AFullName: string; const AValue: string = '')
      : TsgNodeAttrib;
    procedure AddChild(const ANode: TsgNodeSample);
    function AddChildNV(const AFullName: string;
      const AValue: string = ''): TsgNode;
    function AddAttribs(const AAttribs: TStrings): Integer;
    procedure Assign(const Obj: TObject); override;
    procedure Clear; override;
    function GetAttributeByName(const AName: string): TsgNodeSample; override;
    function GetChildByName(const AName: string): TsgNodeSample; override;
    function GetNodeByAttribValue(const ANodeName, AName, AValue: string;
      const ACaseSensitivity: Boolean = True): TsgNodeSample;
    function RemoveAttrib(const ANode: TsgNodeSample): Boolean;
    function RemoveChild(const ANode: TsgNodeSample): Boolean;
    function RemoveAttributeByName(const AName: string;
      const ADoFree: Boolean = True): Boolean;
    function RemoveChildByName(const AName: string;
      const ADoFree: Boolean = True): Boolean;
    property Attributes: TsgNodeList read FAttributes;
    property Childs: TsgNodeList read FChilds;
    property NodeType: TsgNodeType read GetNodeType;
  end;

  TsgNodeCDATA = class(TsgNodeComment)
  protected
    function GetNodeType: TsgNodeType; override;
    procedure Read(const AParser: TsgParser); override;
  end;

  TsgNodeDocument = class(TsgNode)
  protected
    function GetClosed: Boolean; override;
    function GetNodeType: TsgNodeType; override;
{$IFDEF SG_EXPORT_WITH_STRINGS}
    procedure SaveToStrings(const AStrings: TStrings); override;
{$ELSE}
    procedure SaveToStream(const AStream: TStream); override;
{$ENDIF}
  end;

  TsgNodeDocType = class(TsgNodeDocument)
  private
    FValues: TsgNodeList;
    procedure FreeValues;
  protected
    function GetName: string; override;
    function GetNodeType: TsgNodeType; override;
    procedure Read(const AParser: TsgParser); override;
    procedure ReadValues(const AParser: TsgParser);
    procedure SetName(const AValue: string); override;
  public
    destructor Destroy; override;
    procedure Assign(const Obj: TObject); override;
    property Values: TsgNodeList read FValues;
  end;

  TsgNodeEntity = class(TsgNodeAttrib)
  protected
    function GetName: string; override;
    function GetNodeType: TsgNodeType; override;
    procedure Read(const AParser: TsgParser); override;
    function ReadNodeName(const AParser: TsgParser): string;
    procedure SetName(const AValue: string); override;
  end;

  TsgNodeHTML = class(TsgNode)
  protected
    function GetClassID: Cardinal; override;
  end;

  TsgParser = class
  private
    FBuffer: string;
    FBufferIndex: Integer;
    FBufferReaded: Integer;
    FBufferSize: Integer;
    FBufferStep: Integer;
    FDebugInfo: TList;
    FOnProgress: TsgProgressEvent;
    FRegisterNodes: TsgStringList;
    FROOT: TsgNodeList;
    FSource: TStream;
    FSourceString: string;
    FStopped: TsgObjProcBool;
    procedure BeginUpdateProgress;
    procedure EndUpdateProgress;
    procedure LoadDebugInfo;
    procedure UpdateProgress;
    function GetDebugInfo: Boolean;
    function GetXMLString: string;
    function InitFromStream: Boolean;
    function InitFromString: Boolean;
    procedure SetXMLString(const AValue: string);
    procedure SetDebugInfo(const AValue: Boolean);
  protected
    procedure Analize;
    procedure AfterLoadFromStream; virtual;
    procedure BeginAnalize;
    procedure BeforeLoadFromStream; virtual;
    function BufferCheckPos: Boolean;
    procedure BufferNext;
    function BufferValue: Char;
    function BufferValueToChar(const AChar: Char): string;
    function BufferValueToStr(const AStr: string): string;
    function CheckEncoding(var ADocument: TsgNodeDocument): Boolean;
    function CheckEntities(var ADocType: TsgNodeDocType): Boolean;
    function CheckState: Boolean;
    procedure ClearBuffer;
    function CreateNodeSample(const AFullName: string; AClass: TClass)
      : TsgNodeSample;
    function FindNodeByAttribValue(const ANode: TsgNodeSample;
      const AAttribName, AAttribValue: string; const AFoundNodes: TList = nil;
      const AIgnoreValue: Boolean = False): TsgNodeSample;
    function LoadFromBuffer(const AInitBuffer: TsgObjProcBool): Boolean;
    function ReadAttrib: TsgNodeSample;
    function ReadCData: string;
    function ReadComment: string;
    function ReadDocType: string;
    function ReadNode: TsgNodeSample;
    function ReadNodeName(var ANodeType: TsgNodeType;
      var APostamble: Boolean): string;
    function ReadText: string;
    function ReadValue: string;
    procedure SetNodePosition(const ANode: TsgNodeSample);
  public
    constructor Create; virtual;
    destructor Destroy; override;
{$IFDEF SGDEL_2009}
    class function SaveEncoding: TEncoding; static;
{$ENDIF}
    procedure Assign(const AParser: TsgParser);
    function AddDeclaration(const AVersion: string = '1.0';
      AEncoding: string = ''): Boolean;
    procedure Clear;
    function CreateRootNode(const AFullName: string = cnstXML;
      const ACreateDocument: Boolean = False): TsgNode;
    function FindNode(const AID: string; const AList: TsgNodeList)
      : TsgNodeSample;
    class function FindNodeByAttrib(const AAttribName, AAttribValue: string;
      const AList: TsgNodeList; AFoundNodes: TList = nil;
      const AIgnoreValue: Boolean = False; const AFindCount: Integer = -1)
      : TsgNodeSample;
    class function FindNodeByName(const ANodeName: string;
      const AList: TsgNodeList; AFoundNodes: TList = nil;
      const AFindCount: Integer = -1): TsgNodeSample;
    function FindEqualNode(const ABase: TsgNodeSample;
      const ANode: TsgNodeSample; ANodeCompareFunc: TsgNodeCompareFunc = nil;
      const ATree: TList = nil): TsgNodeSample;
    function LoadFromFile(const AFileName: string): Boolean;
    function LoadFromStream(const S: TStream): Boolean;
    function LoadFromString(const AString: string): Boolean;
    function SaveToFile(const AFileName: string): Boolean;
    function SaveToStream(const S: TStream): Boolean;
    function SaveToStrings(const AStrings: TStrings): Boolean;
    function SaveToString: string;
    procedure RegisterClass(const AFullName: string; const AClass: TClass);
    function Stopped: Boolean;
    procedure UnRegisterClass(const AFullName: string);
    property DebugInfo: Boolean read GetDebugInfo write SetDebugInfo;
    property OnProgress: TsgProgressEvent read FOnProgress write FOnProgress;
    property ROOT: TsgNodeList read FROOT;
    property StopRead: TsgObjProcBool read FStopped write FStopped;
    property XMLString: string read GetXMLString write SetXMLString;
  end;

  TsgFindNodeCustom = class
    function Compare(const ANode: TsgNodeSample): Boolean; virtual; abstract;
  end;

  TsgFindByAttribute = class(TsgFindNodeCustom)
    Name: string;
    Value: string;
    IgnoreValue: Boolean;
    CaseSensitivity: Boolean;
    constructor Create(const AName, AValue: string;
      const AIgnore: Boolean = False; const ACaseSensitivity: Boolean = True);
    function Compare(const ANode: TsgNodeSample): Boolean; override;
  end;

  TsgFindByNode = class(TsgFindNodeCustom)
    Name: string;
    CaseSensitivity: Boolean;
    constructor Create(const AName: string;
      const ACaseSensitivity: Boolean = True);
    function Compare(const ANode: TsgNodeSample): Boolean; override;
  end;

  TsgNodeIterator = class
  private
    FComparer: TsgFindNodeCustom;
    FROOT: TsgNodeSample;
    FRootNotes: TsgNodeList;
    FFindCount: Integer;
    FFindNodes: TList;
    FFindTree: TList;
    function BreakFind: Boolean;
    function GetFindNotAll: Boolean;
    procedure SetFindNotAll(const Value: Boolean);
  protected
    function Compare(const ANode: TsgNodeSample): Boolean;
    function FindChildNode(const ANode: TsgNodeSample): Boolean;
  public
    constructor Create(const ANode: TsgNodeSample;
      const AComparer: TsgFindNodeCustom); overload;
    constructor Create(const ANodes: TsgNodeList;
      const AComparer: TsgFindNodeCustom); overload;
    destructor Destroy; override;
    function Find(const AFindCount: Integer = -1): Integer;
    property FindNotAll: Boolean read GetFindNotAll write SetFindNotAll;
    property FindResult: TList read FFindNodes;
    property FindTree: TList read FFindTree;
  end;

  IsgResultNode = interface(IInterface)
    [cnstGUID_ResultNode]
    function GetResult: TsgNode;
    function GetOutput: TsgNode;
    function GetErrors: TsgNode;
    property Result: TsgNode read GetResult;
    property Output: TsgNode read GetOutput;
    property Errors: TsgNode read GetErrors;
  end;

function IsNodeSample(const AValue: Pointer): Boolean;
// Get and convert function

function GetXMLNodeTextAsInt(const ANode: TsgNodeSample;
  const ADefaultValue: Integer = 0): Integer;
function GetXMLNodeTextAsBool(const ANode: TsgNodeSample;
  const ADefaultValue: Boolean = False): Boolean;
function GetXMLNodeTextAsDouble(const ANode: TsgNodeSample;
  const ADefaultValue: Double = 0): Double;
function GetXMLNodeTextAsString(const ANode: TsgNodeSample;
  const ADefaultValue: string = ''): string;
function GetXMLNodeTextAsFRect(const ANode: TsgNodeSample;
  const ADefaultValue: TFRect): TFRect;

function SaveNodeToXMLString(const ANode: TsgNodeSample): string;
//
function GetAttributeBool(const AElem: TsgNodeSample; const AName: string;
  const ADefaultValue: Boolean = False): Boolean;
function GetAttributeFloat(const AElem: TsgNodeSample; const AName: string;
  const ADefaultValue: Double = 0): Double;
function GetAttributeInt(const AElem: TsgNodeSample; const AName: string;
  const ADefaultValue: Integer = 0): Integer;
function GetAttributeStr(const AElem: TsgNodeSample; const AName: string;
  const ADefaultValue: string = ''): string;

procedure CheckStringParams(const AParams: TStrings);
procedure DecodeBase64(const AString: string; AStrings: TStrings;
  var AStream: TStream);
function StrDecodeBase64(const AString: string): string;
procedure EncodeBase64(const AInStream: TStream; var AOutStream: TStream);
function StrEncodeBase64(const AString: string): string;
function GetCodePageByName(const AStr: string; var ACodePage: Integer): Boolean;
function GetCodePageName(const ACodePage: Integer): string;
{$IFDEF SGDEL_2009}
function GetEncodingCodePageName(const AEncoding: TEncoding): string;
{$ENDIF}
function GetFullText(const AStrings: TStringList): string;
function GetHandleByNode(const ANode: TsgNodeSample): string;
function GetNameByCodePage(const ACodePage: Integer; var AName: string)
  : Boolean;
function GetNodeByHandle(const AStr: string): TsgNodeSample;
function GetTagIdByName(const AName: string): TsgTagID;
function GetUnitsMeasurements(const AValue: string): TsgUnitsOfMeasurements;
function IsApostrophe(const AChar: Char): Boolean;
function IsHandleOfNode(const AStr: string): Boolean;
function IsLetter(const AChar: Char): Boolean;
function IsName(const AChar: Char): Boolean;
function IsNumber(const AChar: Char): Boolean;
function IsSpace(const AChar: Char): Boolean;
function ReplaceAnsiCodes(const AString: string): string;
procedure ReplaceWebCode(const AToAnsi: Boolean; var AString: string);
function ReplaceWebCodes(const AString: string): string;
function ScanDigitsInString(const AString: string; const ALength: Integer;
  const AIsReadExponent: Boolean; var AIndex: Integer): Boolean;
procedure SetStringsLineBreak(const AStrings: TsgStringList;
  const AText: string; ALineBreak: Char = cnstLineBreak);
procedure SetStringsText(const AStrings: TsgStringList; const AText: string);

function BoolToStr(const AValue: Boolean): string;
function FloatToString(const AValue: Double): string;
function FPointToStr(const AValue: TFPoint): string;
function HandleToStr(const AValue: UInt64): string;
function ValToStr(const ADouble: Double): string;
function StrHexToInt(const AString: string): Integer;
function StrToColor(const AString: string): Integer;
function StrToColorHex(AString: string): TColor;
function StrToBool(const AString: string; var ABool: Boolean): Boolean;
function StringToFloat(const AString: string): Double;
function StrToFPoint(const AString: string): TFPoint;
function StrToFRect(const AString: string; const AIs3D: Boolean = True): TFRect;
function StrToF2DRect(const AString: string): TF2DRect;
function StrToHandle(const AString: string): UInt64;
function StrToVal(const AString: string): Double;
function ColorHexToStr(const AColor: TColor): string;
function FRectToStr(const ARect: TFRect; const AIs3D: Boolean = True): string;
function F2DRectToStr(const ARect: TF2DRect): string;

function StrToPointsList(const AString: string; const AIs3D: Boolean;
  const AList: Pointer; const AAddProc: TsgProcOfListAddPoint;
  const ASeparator: Char = cnstLineBreak): Integer;
function PointsListToStr(const AIs3D: Boolean; const AList: TObject;
  const ACount: Integer; const GetProc: TsgProcOfListGetPoint;
  const ASeparator: Char = cnstLineBreak): string; overload;
function PointsListToStr(const AIs3D: Boolean; AList: IsgArrayFPoint;
  const ASeparator: Char = cnstLineBreak): string; overload;
function StrToSingleList(const AString: string; const AList: Pointer;
  const AAddProc: TsgProcOfListAddSingle;
  const ASeparator: Char = cnstLineBreak): Integer;
function SingleListToStr(const AList: TObject; const ACount: Integer;
  const GetProc: TsgProcOfListGetSingle;
  const ASeparator: Char = cnstLineBreak): string;

function Utf8ToUni(ADst: PWideChar; AMaxDstLen: DWORD; ASrc: PChar;
  ASrcLen: DWORD): DWORD;
function IsValidXML(const AString: string; var AMessage: string;
  const APoint: PPoint = nil): Boolean;

{$IFDEF SG_PARSER_DEBUG}
function TestParser(const AFileName: string; const Tree: TTreeView): DWORD;
{$ENDIF}

implementation

const
  cnstIncorrectType: string = 'Incorrect type value!';

{$IFDEF SG_USE_DICTIONARY_BY_NAME}

var
  DictionaryByNames: TsgStringList = nil;
  DictionaryByIndexes: TsgStringList = nil;
{$IFDEF SG_OPENING_IN_THEADS}
  DictionaryLock: TRTLCriticalSection;
{$ENDIF}

function IsValidXML(const AString: string; var AMessage: string;
  const APoint: PPoint = nil): Boolean;
{$IFDEF SGFPC}
begin
  Result := True;
end;
{$ELSE}

var
  vDom: IXMLDOMDocument;
  vError: IXMLDOMParseError;
begin
  AMessage := '';
  Result := True;
  vDom := CoDOMDocument.Create;
 // CreateDOMDocument;
  try
    vDom.loadXML(AString);
    try
      vError := vDom.parseError;
      if vError.errorCode <> 0 then
      begin
        Result := False;
        AMessage := vError.reason;
        if APoint <> nil then
        begin
          APoint^.X := vError.linepos;
          APoint^.Y := vError.line;
        end;
      end;
    finally
      vError := nil;
    end;
  finally
    vDom := nil;
  end;
end;
{$ENDIF}

procedure CreateDictionaryNames;
begin
  DictionaryByNames := TsgStringList.Create;
  DictionaryByNames.Sorted := True;
  DictionaryByNames.Duplicates := dupIgnore;
  DictionaryByNames.CaseSensitive := True;
  DictionaryByIndexes := TsgStringList.Create;
  DictionaryByIndexes.Sorted := False;
end;

function GetNameIndex(const AName: string): Integer; {$IFDEF SG_INLINE}inline;
{$ENDIF}
var
  vIndex: Integer;
begin
{$IFNDEF SG_OPENING_IN_THEADS}
  if DictionaryByNames = nil then
    CreateDictionaryNames;
{$ENDIF}
  vIndex := DictionaryByNames.IndexOf(AName);
  if vIndex > -1 then
    Result := Integer(DictionaryByNames.Objects[vIndex])
  else
  begin
{$IFDEF SG_OPENING_IN_THEADS}
    EnterCriticalSection(DictionaryLock);
    try
{$ENDIF}
      DictionaryByIndexes.Add(AName);
      Result := DictionaryByIndexes.Count;
      DictionaryByNames.AddObject(AName, Pointer(Result));
{$IFDEF SG_OPENING_IN_THEADS}
    finally
      LeaveCriticalSection(DictionaryLock);
    end;
{$ENDIF}
  end;
end;

function GetNameByIndex(const AIndex: Integer): string;
{$IFDEF SG_INLINE}inline; {$ENDIF}
begin
  Result := '';
  if (DictionaryByIndexes <> nil) and (AIndex > 0) then
    Result := DictionaryByIndexes[AIndex - 1];
end;
{$ENDIF}
{$IFDEF SG_PARSER_USE_RTTI}

type
  TsgRecordType = (tkrUndefined, tkrTFPoint);

function IsField(P: Pointer): Boolean; inline;
{$IFDEF CPUX86}
begin
  Result := (IntPtr(P) and $FF000000) = $FF000000;
end;
{$ENDIF}
{$IFDEF CPUX64}
begin
  Result := (IntPtr(P) and $FF00000000000000) = $FF00000000000000;
end;
{$ENDIF}

function GetField(Instance: TObject; P: Pointer): Pointer; inline;
begin
  Result := Pointer(PByte(Instance) + (IntPtr(P) and $00FFFFFF));
end;

function GetCodePointer(Instance: TObject; P: Pointer): Pointer; inline;
{$IFDEF CPUX86}
begin
  if IntPtr(P) and $FF000000 = $FE000000 then // Virtual Method
    Result := PPointer(PNativeUInt(Instance)^ + (UIntPtr(P) and $FFFF))^
  else // Static method
    Result := P;
end;
{$ENDIF}
{$IFDEF CPUX64}
begin
  if IntPtr(P) and $FF00000000000000 = $FE00000000000000 then // Virtual Method
    Result := PPointer(PNativeUInt(Instance)^ + (UIntPtr(P) and $FFFF))^
  else // Static method
    Result := P;
end;
{$ENDIF}

function GetPropertyAsFPoint(Instance: TObject; const Info: PPropInfo): TFPoint;
var
  vMetod: TMethod;
begin
  if IsField(Info^.GetProc) then
    Result := PFPoint(GetField(Instance, Info^.GetProc))^
  else
  begin
    vMetod.Data := Instance;
    vMetod.Code := @GetCodePointer(Instance, Info^.GetProc)^;
    Result := sgFunction.TsgObjProcFPoint(vMetod);
  end;
end;

function GetRecordType(PropInfo: PPropInfo): TsgRecordType;
begin
  Result := tkrUndefined;
  if PropInfo^.PropType^^.Name = 'TFPoint' then
    Result := tkrTFPoint;
end;
{$ENDIF}

const
  cnstCDATABegin = '[';
  cnstCDATAEnd = ']';
  cnstCommentBegin = '<!--';
  cnstCommentEnd = '-->';
  cnstDecimalSeparatorFloat = '.';
  cnstDecimalSeparatorValues = ',';
  cnstDecimalSeparatorVersion = '.';
  cnstDOCTYPE = 'DOCTYPE';
  cnstENTITY = 'ENTITY';
  cnstKeyWordsStr: array [TsgTagID] of string = ('', 'cx', 'cy', 'r', 'rx',
    'ry', 'style', 'x1', 'y1', 'x2', 'y2', 'x', 'y', 'dx', 'dy', 'points',
    'width', 'height', 'd', 'pathlength', 'id', 'href', 'transform', 'viewbox',
    'preserveaspectratio', 'class', 'clip-path', 'stroke', 'stroke-width',
    'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit',
    'stroke-dasharray', 'stroke-dashoffset', 'stroke-opacity', 'vector-effect',
    'fill', 'fill-opacity', 'fill-rule', 'gradientunits', 'gradienttransform',
    'spreadmethod', 'fx', 'fy', 'stop-color', 'stop-opacity', 'offset',
    'patternunits', 'patterncontentunits', 'font', 'font-family', 'font-style',
    'font-weight', 'font-size', 'font-variant', 'font-stretch', 'text-anchor',
    'text-decoration', 'display', 'visibility', 'opacity');
{$IFDEF SG_PARSER_DEBUG}
  cnstNodeTypeNames: array [TsgNodeType] of string = ('', 'Element',
    'Attribute', 'Text', 'CDATASection', 'EntityRef', 'Entity',
    'ProcessingInstr', 'Comment', 'Document', 'DocumentType',
    'DocumentFragment', 'Notation');
{$ENDIF}
  cnstRGB = 'rgb';
  cnstColorHexPrefix = '#';
  cnstSeporatorFullName = ':';
cnstStringColors:
array [0 .. 136] of record Name: string;
Value:
TColor;
end
= ((Name: 'aliceblue'; Value: $F0F8FF), (Name: 'antiquewhite'; Value: $FAEBD7),
  (Name: 'aqua'; Value: $00FFFF), (Name: 'aquamarine'; Value: $7FFFD4),
  (Name: 'azure'; Value: $F0FFFF), (Name: 'beige'; Value: $F5F5DC),
  (Name: 'black'; Value: $000000), (Name: 'blue'; Value: $0000FF),
  (Name: 'blueviolet'; Value: $8A2BE2), (Name: 'brown'; Value: $A52A2A),
  (Name: 'burlywood'; Value: $DEB887), (Name: 'cadetblue'; Value: $5F9EA0),
  (Name: 'chartreuse'; Value: $7FFF00), (Name: 'chocolate'; Value: $D2691E),
  (Name: 'coral'; Value: $FF7F50), (Name: 'cornflowerblue'; Value: $6495ED),
  (Name: 'cornsilk'; Value: $FFF8DC), (Name: 'crimson'; Value: $DC143C),
  (Name: 'darkblue'; Value: $00008B), (Name: 'darkcyan'; Value: $008B8B),
  (Name: 'darkgoldenrod'; Value: $B8860B), (Name: 'darkgray'; Value: $A9A9A9),
  (Name: 'darkgreen'; Value: $006400), (Name: 'darkkhaki'; Value: $BDB76B),
  (Name: 'darkmagenta'; Value: $8B008B), (Name: 'darkolivegreen';
  Value: $556B2F), (Name: 'darkorange'; Value: $FF8C00), (Name: 'darkorchid';
  Value: $9932CC), (Name: 'darkred'; Value: $8B0000), (Name: 'darksalmon';
  Value: $E9967A), (Name: 'darkseagreen'; Value: $8FBC8F),
  (Name: 'darkslateblue'; Value: $483D8B), (Name: 'darkslategray';
  Value: $2F4F4F), (Name: 'darkturquoise'; Value: $00CED1), (Name: 'darkviolet';
  Value: $9400D3), (Name: 'deeppink'; Value: $FF1493), (Name: 'deepskyblue';
  Value: $00BFFF), (Name: 'dimgray'; Value: $696969), (Name: 'dodgerblue';
  Value: $1E90FF), (Name: 'firebrick'; Value: $B22222), (Name: 'floralwhite';
  Value: $FFFAF0), (Name: 'forestgreen'; Value: $228B22), (Name: 'fuchsia';
  Value: $FF00FF), (Name: 'gainsboro'; Value: $DCDCDC), (Name: 'ghostwhite';
  Value: $F8F8FF), (Name: 'gold'; Value: $FFD700), (Name: 'goldenrod';
  Value: $DAA520), (Name: 'gray'; Value: $808080), (Name: 'green';
  Value: $008000), (Name: 'greenyellow'; Value: $ADFF2F), (Name: 'honeydew';
  Value: $F0FFF0), (Name: 'hotpink'; Value: $FF69B4), (Name: 'indianred';
  Value: $CD5C5C), (Name: 'indigo'; Value: $4B0082), (Name: 'ivory';
  Value: $FFFFF0), (Name: 'khaki'; Value: $F0E68C), (Name: 'lavender';
  Value: $E6E6FA), (Name: 'lavenderblush'; Value: $FFF0F5), (Name: 'lawngreen';
  Value: $7CFC00), (Name: 'lemonchiffon'; Value: $FFFACD), (Name: 'lightblue';
  Value: $ADD8E6), (Name: 'lightcoral'; Value: $F08080), (Name: 'lightcyan';
  Value: $E0FFFF), (Name: 'lightgoldenrodyellow'; Value: $FAFAD2),
  (Name: 'lightgreen'; Value: $90EE90), (Name: 'lightgrey'; Value: $D3D3D3),
  (Name: 'lightpink'; Value: $FFB6C1), (Name: 'lightsalmon'; Value: $FFA07A),
  (Name: 'lightseagreen'; Value: $20B2AA), (Name: 'lightskyblue';
  Value: $87CEFA), (Name: 'lightslategray'; Value: $778899),
  (Name: 'lightsteelblue'; Value: $B0C4DE), (Name: 'lightyellow';
  Value: $FFFFE0), (Name: 'lime'; Value: $00FF00), (Name: 'limegreen';
  Value: $32CD32), (Name: 'linen'; Value: $FAF0E6), (Name: 'maroon';
  Value: $800000), (Name: 'mediumaquamarine'; Value: $66CDAA),
  (Name: 'mediumblue'; Value: $0000CD), (Name: 'mediumorchid'; Value: $BA55D3),
  (Name: 'mediumpurple'; Value: $9370DB), (Name: 'mediumseagreen';
  Value: $3CB371), (Name: 'mediumslateblue'; Value: $7B68EE),
  (Name: 'mediumspringgreen'; Value: $00FA9A), (Name: 'mediumturquoise';
  Value: $48D1CC), (Name: 'mediumvioletred'; Value: $C71585),
  (Name: 'midnightblue'; Value: $191970), (Name: 'mintcream'; Value: $F5FFFA),
  (Name: 'mistyrose'; Value: $FFE4E1), (Name: 'moccasin'; Value: $FFE4B5),
  (Name: 'navajowhite'; Value: $FFDEAD), (Name: 'navy'; Value: $000080),
  (Name: 'none'; Value: clNone), (Name: 'oldlace'; Value: $FDF5E6),
  (Name: 'olive'; Value: $808000), (Name: 'olivedrab'; Value: $6B8E23),
  (Name: 'orange'; Value: $FFA500), (Name: 'orangered'; Value: $FF4500),
  (Name: 'orchid'; Value: $DA70D6), (Name: 'palegoldenrod'; Value: $EEE8AA),
  (Name: 'palegreen'; Value: $98FB98), (Name: 'paleturquoise'; Value: $AFEEEE),
  (Name: 'palevioletred'; Value: $DB7093), (Name: 'papayawhip'; Value: $FFEFD5),
  (Name: 'peachpuff'; Value: $FFDAB9), (Name: 'peru'; Value: $CD853F),
  (Name: 'pink'; Value: $FFC0CB), (Name: 'plum'; Value: $DDA0DD),
  (Name: 'powderblue'; Value: $B0E0E6), (Name: 'purple'; Value: $800080),
  (Name: 'red'; Value: $FF0000), (Name: 'rosybrown'; Value: $BC8F8F),
  (Name: 'royalblue'; Value: $4169E1), (Name: 'saddlebrown'; Value: $8B4513),
  (Name: 'salmon'; Value: $FA8072), (Name: 'sandybrown'; Value: $F4A460),
  (Name: 'seagreen'; Value: $2E8B57), (Name: 'seashell'; Value: $FFF5EE),
  (Name: 'sienna'; Value: $A0522D), (Name: 'silver'; Value: $C0C0C0),
  (Name: 'skyblue'; Value: $87CEEB), (Name: 'slateblue'; Value: $6A5ACD),
  (Name: 'slategray'; Value: $708090), (Name: 'snow'; Value: $FFFAFA),
  (Name: 'springgreen'; Value: $00FF7F), (Name: 'steelblue'; Value: $4682B4),
  (Name: 'tan'; Value: $D2B48C), (Name: 'teal'; Value: $008080),
  (Name: 'thistle'; Value: $D8BFD8), (Name: 'tomato'; Value: $FF6347),
  (Name: 'turquoise'; Value: $40E0D0), (Name: 'violet'; Value: $EE82EE),
  (Name: 'wheat'; Value: $F5DEB3), (Name: 'white'; Value: $FFFFFF),
  (Name: 'whitesmoke'; Value: $F5F5F5), (Name: 'yellow'; Value: $FFFF00),
  (Name: 'yellowgreen'; Value: $9ACD32));

cnstStringWebKeysLast = 4;
cnstStringWebKeys:
array [0 .. 8] of record Str: string;
Key:
string;
end
= ((Str: '&amp;'; Key: cnstAmpersant), (Str: '&lt;'; Key: cnstLess),
  (Str: '&gt;'; Key: cnstGreater), (Str: '&apos;'; Key: cnstApos),
  (Str: '&quot;'; Key: cnstQuotationMark),

  (Str: '&deg;'; Key: cnstSymbolDegree), (Str: '&copy;'; Key: cnstCopyRight),
  (Str: '&reg;'; Key: cnstRegisteredSign), (Str: '&nbsp;'; Key: cnstSpace));

cnstUnitsOfMeasurements:
array [0 .. 9] of record Name: string;
Value:
TsgUnitsOfMeasurements;
end
= ((Name: '%'; Value: umPR), (Name: 'PR'; Value: umPR), (Name: 'PX';
  Value: umPX), (Name: 'MM'; Value: umMM), (Name: 'CM'; Value: umCM),
  (Name: 'IN'; Value: umIN), (Name: 'EM'; Value: umEM), (Name: 'PT';
  Value: umPT), (Name: 'PC'; Value: umPC), (Name: 'M'; Value: umM));

EncodeBase64Table:
array [0 .. 63] of AnsiChar = AnsiString
  ('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/');

const
  cnstCountValue: array [Boolean] of Integer = (1, 2);

{$IFDEF SGDEL_2009}

type
  TsgNodeEnumeratorBase = class(TInterfacedObject, IEnumerable, IEnumerator)
  private
    FIndex: Integer;
    FList: TsgNodeList;
  public
    constructor Create(const ANodeList: TsgNodeList);
    function GetCurrent: TObject; overload;
    function GetEnumerator: IEnumerator; overload;
    function MoveNext: Boolean;
    procedure Reset;
  end;

  TsgNodeEnumerator = class(TsgNodeEnumeratorBase, IEnumerable<TsgNodeSample>,
    IEnumerator<TsgNodeSample>)
  public
    function GetCurrent: TsgNodeSample; overload;
    function GetEnumerator: IEnumerator<TsgNodeSample>; overload;
  end;

{$ENDIF}

type
  TsgMemoryStream = class(TMemoryStream);
  TsgClassOfNode = class of TsgNodeSample;

  TsgBase64 = class
  private
    FBuffer: string;
    FCol: TsgNativeInt;
    FRMax: TsgNativeInt;
    FRow: TsgNativeInt;
    FStrs: TStrings;
  protected
    function Next: Char;
  public
    procedure Decode(const AStr: string; AStrs: TStrings; const S: TStream);
    procedure EnCode(const AInStream: TStream; const AOutStream: TStream);
  end;

  TsgParserStage = (psUndifined, psBeginValue, psValue, psEnd, psText,
    psAttrib);

var
  KeyWords: TsgStringList = nil;
  TableColors: TsgStringList = nil;
{$IFNDEF SG_OPENING_IN_THEADS}
  Spliter: TsgStringList = nil;
{$ENDIF}

function IsNodeSample(const AValue: Pointer): Boolean;
begin
  try
    Result := False;
    if TObject(AValue) is TsgNodeSample then
      Result := True;
  except
    Result := False;
  end;
end;

function GetXMLNodeTextAsInt(const ANode: TsgNodeSample;
  const ADefaultValue: Integer = 0): Integer;
begin
  Result := ADefaultValue;
  if Assigned(ANode) then
    Result := ANode.TextAsInt;
end;

function GetXMLNodeTextAsBool(const ANode: TsgNodeSample;
  const ADefaultValue: Boolean = False): Boolean;
begin
  Result := ADefaultValue;
  if Assigned(ANode) then
    Result := ANode.TextAsBool;
end;

function GetXMLNodeTextAsDouble(const ANode: TsgNodeSample;
  const ADefaultValue: Double = 0): Double;
begin
  Result := ADefaultValue;
  if Assigned(ANode) then
    Result := ANode.TextAsDouble;
end;

function GetXMLNodeTextAsString(const ANode: TsgNodeSample;
  const ADefaultValue: string = ''): string;
begin
  Result := ADefaultValue;
  if Assigned(ANode) then
    Result := ANode.TextAsStr;
end;

function GetXMLNodeTextAsFRect(const ANode: TsgNodeSample;
  const ADefaultValue: TFRect): TFRect;
begin
  Result := ADefaultValue;
  if Assigned(ANode) then
    Result := ANode.TextAsFRect;
end;

function GetAttributeBool(const AElem: TsgNodeSample; const AName: string;
  const ADefaultValue: Boolean = False): Boolean;
var
  vItem: TsgNodeSample;
begin
  Result := ADefaultValue;
  vItem := AElem.GetAttributeByName(AName);
  if vItem <> nil then
    Result := vItem.ValueAsBool;
end;

function GetAttributeFloat(const AElem: TsgNodeSample; const AName: string;
  const ADefaultValue: Double = 0): Double;
var
  vItem: TsgNodeSample;
begin
  Result := ADefaultValue;
  vItem := AElem.GetAttributeByName(AName);
  if vItem <> nil then
    Result := vItem.ValueAsDouble;
end;

function GetAttributeInt(const AElem: TsgNodeSample; const AName: string;
  const ADefaultValue: Integer = 0): Integer;
var
  vItem: TsgNodeSample;
begin
  Result := ADefaultValue;
  vItem := AElem.GetAttributeByName(AName);
  if vItem <> nil then
    Result := vItem.ValueAsInt;
end;

function GetAttributeStr(const AElem: TsgNodeSample; const AName: string;
  const ADefaultValue: string = ''): string;
var
  vItem: TsgNodeSample;
begin
  Result := ADefaultValue;
  vItem := AElem.GetAttributeByName(AName);
  if vItem <> nil then
    Result := vItem.Value;
end;

function SaveNodeToXMLString(const ANode: TsgNodeSample): string;
var
  vParser: TsgParser;
begin
  vParser := TsgParser.Create;
  try
    try
      vParser.FROOT.Add(ANode);
      Result := vParser.SaveToString;
    finally
      vParser.FROOT.Count := 0;
    end;
  finally
    vParser.Free;
  end;
end;

procedure AssignObjLists(const AOwner: TsgNodeSample;
  const ASource: TsgNodeList; var ADest: TsgNodeList);
var
  I: Integer;
  vNodeBase, vNodeNew: TsgNodeSample;
begin
  if ASource = nil then
  begin
    if ADest <> nil then
      FreeAndNil(ADest);
  end
  else
  begin
    if ADest <> nil then
      ADest.ClearNodes
    else
      ADest := TsgNodeList.CreateNodeList;
    ADest.QwickFind := False;
    ADest.Capacity := ASource.Count;
    for I := 0 to ASource.Count - 1 do
    begin
      vNodeBase := ASource.List[I];
      vNodeNew := TsgClassOfNode(vNodeBase.ClassType).Create;
      ADest.Add(vNodeNew);
      vNodeNew.Assign(vNodeBase);
      vNodeNew.FOwner := AOwner;
    end;
    ADest.QwickFind := ASource.QwickFind;
  end;
end;

procedure CheckStringParams(const AParams: TStrings);
var
  I: Integer;
begin
  I := 0;
  while I < AParams.Count do
  begin
    if Length(AParams[I]) = 0 then
      AParams.Delete(I)
    else
      Inc(I);
  end;
end;

procedure CreateKeyWords;
var
  I: TsgTagID;
begin
  KeyWords := TsgStringList.Create;
  KeyWords.Sorted := True;
  KeyWords.Duplicates := dupIgnore;
  KeyWords.CaseSensitive := False;
  KeyWords.Capacity := Integer(High(TsgTagID)) - Integer(Low(TsgTagID)) + 1;
  for I := Low(TsgTagID) to High(TsgTagID) do
    KeyWords.AddObject(cnstKeyWordsStr[I], Pointer(I));
end;

procedure DecodeBase64(const AString: string; AStrings: TStrings;
  var AStream: TStream);
var
  vCoderBase64: TsgBase64;
begin
  vCoderBase64 := TsgBase64.Create;
  try
    vCoderBase64.Decode(AString, AStrings, AStream);
  finally
    vCoderBase64.Free;
  end;
end;

procedure EncodeBase64(const AInStream: TStream; var AOutStream: TStream);
var
  vCoderBase64: TsgBase64;
begin
  vCoderBase64 := TsgBase64.Create;
  try
    vCoderBase64.EnCode(AInStream, AOutStream);
  finally
    vCoderBase64.Free;
  end;
end;

function StrDecodeBase64(const AString: string): string;
var
  vMemBase64: TMemoryStream;
  vStrings: TStringList;
begin
  Result := '';
  vMemBase64 := TMemoryStream.Create;
  vStrings := TStringList.Create;
  try
    DecodeBase64(AString, nil, TStream(vMemBase64));
    vMemBase64.Position := 0;
    vStrings.LoadFromStream(vMemBase64);
    Result := vStrings.Text;
  finally
    vStrings.Free;
    vMemBase64.Free;
  end;
end;

function StrEncodeBase64(const AString: string): string;
var
  vMem: TMemoryStream;
  vMemBase64: TMemoryStream;
  vStrings: TStringList;
begin
  Result := '';
  vMem := TMemoryStream.Create;
  vMemBase64 := TMemoryStream.Create;
  vStrings := TStringList.Create;
  try
    vStrings.Text := AString;
    vStrings.SaveToStream(vMem);
    vMem.Position := 0;
    EncodeBase64(vMem, TStream(vMemBase64));
    vMemBase64.Position := 0;
    vStrings.LoadFromStream(vMemBase64);
    Result := vStrings.Text;
  finally
    vStrings.Free;
    vMemBase64.Free;
    vMem.Free;
  end;
end;

procedure CreateTableColor;
var
  I: Integer;
begin
  TableColors := TsgStringList.Create;
  TableColors.Capacity := High(cnstStringColors) - Low(cnstStringColors) + 1;
  TableColors.Sorted := True;
  for I := Low(cnstStringColors) to High(cnstStringColors) do
    TableColors.AddObject(cnstStringColors[I].Name,
      Pointer(cnstStringColors[I].Value));
end;

function GetColorByName(const AString: string): TsgColor;
var
  I: Integer;
  vStringColor: string;
begin
  Result.Color := clBlack;
  if Length(AString) > 0 then
  begin
    if TableColors = nil then
      CreateTableColor;
    if (AString[1] = 'c') and (AString[2] = 'l') then
      vStringColor := Copy(AString, 2, Length(AString) - 2)
    else
      vStringColor := AString;
    I := TableColors.IndexOf(vStringColor);
    if I > -1 then
      Result.Color := Integer(TableColors.Objects[I]);
  end;
end;

function GetNameFromFullName(const AFillName: string): string;
var
  vLength, vIndex: Integer;
begin
  vLength := Length(AFillName);
  if vLength > 0 then
  begin
    vIndex := StringScan(cnstSeporatorFullName, AFillName, 1);
    if vIndex > 0 then
      Result := Copy(AFillName, vIndex + 1, vLength - vIndex)
    else
      Result := AFillName;
  end
  else
    Result := '';
end;

function GetCodePageByName(const AStr: string; var ACodePage: Integer): Boolean;
var
  vStr: string;
begin
  vStr := AnsiLowerCase(AStr);
  ACodePage := CodePageIdTable.GetCodePage(vStr);
  Result := ACodePage > -1;
end;

function GetCodePageName(const ACodePage: Integer): string;
begin
  Result := CodePageIdTable.GetName(ACodePage);
end;

{$IFDEF SGDEL_2009}

function GetEncodingCodePageName(const AEncoding: TEncoding): string;
begin
{$IFDEF SGDEL_XE2}
  Result := GetCodePageName(AEncoding.CodePage);
{$ELSE}
  if AEncoding is TUTF8Encoding then
    Result := CodePageIdTable.GetName(65001)
  else
    Result := '';
{$ENDIF}
end;
{$ENDIF}

function GetFullText(const AStrings: TStringList): string;
var
  I: Integer;
  vNode: TsgNodeSample;
  vStr: string;
begin
  Result := '';
  for I := 0 to AStrings.Count - 1 do
  begin
    vStr := AStrings[I];
    if IsHandleOfNode(vStr) then
    begin
      vNode := GetNodeByHandle(vStr);
      if vNode <> nil then
        vStr := vNode.TextAsStr;
    end;
    Result := Result + vStr;
  end;
end;

function GetHandleByNode(const ANode: TsgNodeSample): string;
begin
  Result := cnstLineBreakText + cnstSymbolByHandle +
    IntToHex(TsgNativeInt(ANode), 0) + cnstLineBreakText;
end;

function GetHexValue(const AValue: string): string;
begin
  Result := '';
  if Length(AValue) > 1 then
  begin
    Result := AValue;
    case Result[1] of
      '#':
        Result[1] := '$';
      '$':
        begin
        end;
    else
      Result := '$' + Result;
    end;
  end;
end;

function GetNameByCodePage(const ACodePage: Integer; var AName: string)
  : Boolean;
begin
  AName := CodePageIdTable.GetName(ACodePage);
  Result := Length(AName) > 0;
end;

function GetNodeByHandle(const AStr: string): TsgNodeSample;
var
  vNode: TsgNodeSample;
begin
  Result := nil;
  try
    vNode := Pointer(StrHexToInt(Trim(AStr)));
    if vNode is TsgNodeSample then
      Result := vNode;
  except
  end;
end;

function GetTagIdByName(const AName: string): TsgTagID;
var
  vIndex: Integer;
  vName: string;
begin
  vName := AnsiLowerCase(AName);
  if KeyWords = nil then
    CreateKeyWords;
  if KeyWords.Find(vName, vIndex) then
    Result := TsgTagID(KeyWords.Objects[vIndex])
  else
    Result := idUndefined;
end;

function GetUnitsMeasurements(const AValue: string): TsgUnitsOfMeasurements;
var
  vIndex, vHigh: Integer;
  vValue: string;
begin
  Result := umUndefined;
  vValue := Trim(AValue);
  if Length(vValue) > 0 then
  begin
    vValue := UpperCase(vValue);
    vIndex := Low(cnstUnitsOfMeasurements);
    vHigh := High(cnstUnitsOfMeasurements);
    while (vIndex <= vHigh) and (StringPos(cnstUnitsOfMeasurements[vIndex].Name,
      vValue, 1) < 1) do
      Inc(vIndex);
    if vIndex <= vHigh then
      Result := cnstUnitsOfMeasurements[vIndex].Value;
  end;
end;

function IsApostrophe(const AChar: Char): Boolean;
begin
  case AChar of
    #34, #39:
      Result := True;
  else
    Result := False;
  end;
end;

function IsHandleOfNode(const AStr: string): Boolean;
begin
  Result := (Length(AStr) > 0) and (AStr[1] = cnstSymbolByHandle);
end;

function IsLetter(const AChar: Char): Boolean;
begin
{$IFDEF SGDEL_XEUP}
  Result := AChar.IsLetter;
{$ELSE}
  case AChar of
    'a' .. 'z', 'A' .. 'Z':
      Result := True;
  else
    Result := False;
  end;
{$ENDIF}
end;

function IsName(const AChar: Char): Boolean;
begin
{$IFDEF SGDEL_XEUP}
  case AChar of
    '-', '_', ':':
      Result := True;
  else
    Result := AChar.IsLetterOrDigit;
  end;
{$ELSE}
  case AChar of
    'a' .. 'z', 'A' .. 'Z', '0' .. '9', '-', '_', ':':
      Result := True;
  else
    Result := False;
  end;
{$ENDIF}
  // Result := IsLetter(AChar) or IsNumber(c) or (AChar = '-') or (AChar = '_');
end;

function IsNumber(const AChar: Char): Boolean;
begin
{$IFDEF SGDEL_XEUP}
  Result := AChar.IsNumber;
{$ELSE}
  case AChar of
    '0' .. '9':
      Result := True;
  else
    Result := False;
  end;
{$ENDIF}
end;

function IsSpace(const AChar: Char): Boolean;
begin
{$IFDEF SGDEL_XEUP}
  Result := AChar.IsWhiteSpace;
{$ELSE}
  case AChar of
    #9, #10, #13, #32:
      Result := True;
  else
    Result := False;
  end;
{$ENDIF}
end;

function ReplaceAnsiCodes(const AString: string): string;
begin
  Result := AString;
  ReplaceWebCode(False, Result);
end;

function ReplaceAnsiCodesEx(const AString: string): string;
var
  I: Integer;
begin
  Result := AString;
  if Length(Result) > 0 then
    for I := 0 to cnstStringWebKeysLast do
      ReplaceAnsi(Result, cnstStringWebKeys[I].Key, cnstStringWebKeys[I].Str);
end;

function ReplaceWebCodeEx(const AString: string): string;
var
  I: Integer;
begin
  Result := AString;
  if Length(Result) > 0 then
    for I := 0 to cnstStringWebKeysLast do
      ReplaceAnsi(Result, cnstStringWebKeys[I].Str, cnstStringWebKeys[I].Key);
end;

procedure ReplaceWebCode(const AToAnsi: Boolean; var AString: string);
var
  I, vWebKeyPos, vLengthStrKey: Integer;
  vKey, vValue: string;
begin
  if Length(AString) > 0 then
  begin
    for I := Low(cnstStringWebKeys) to High(cnstStringWebKeys) do
    begin
      if AToAnsi then
      begin
        vKey := cnstStringWebKeys[I].Str;
        vValue := cnstStringWebKeys[I].Key;
      end
      else
      begin
        vKey := cnstStringWebKeys[I].Key;
        vValue := cnstStringWebKeys[I].Str;
      end;
      vLengthStrKey := Length(vKey);
      vWebKeyPos := StringPos(vKey, AString, 1);
      while vWebKeyPos > 0 do
      begin
        Delete(AString, vWebKeyPos, vLengthStrKey);
        Insert(vValue, AString, vWebKeyPos);
        Inc(vWebKeyPos, Length(vValue));
        vWebKeyPos := StringPos(vKey, AString, vWebKeyPos);
      end;
    end;
  end;
end;

function ReplaceWebCodes(const AString: string): string;
begin
  Result := AString;
  ReplaceWebCode(True, Result);
end;

function ScanDigitsInString(const AString: string; const ALength: Integer;
  const AIsReadExponent: Boolean; var AIndex: Integer): Boolean;
var
  vHasPoint, vHasMinus, vHasDigits: Boolean;
begin
  Result := False;
  vHasMinus := False;
  vHasPoint := False;
  vHasDigits := False;
  while AIndex <= ALength do
  begin
    case AString[AIndex] of
      '0' .. '9':
        vHasDigits := True;
      'e', 'E':
        begin
          if not AIsReadExponent then
          begin
            if AIndex < ALength then
            begin
              Inc(AIndex);
              Result := ScanDigitsInString(AString, ALength, True, AIndex)
            end
            else
              Result := True;
          end
          else
            Result := True;
          Break;
        end;
      '-':
        begin
          if vHasMinus or vHasDigits then
          begin
            Result := True;
            Break;
          end;
          vHasMinus := True;
        end;
      '.':
        begin
          if vHasPoint or AIsReadExponent then
          begin
            Result := True;
            Break;
          end;
          vHasPoint := True;
        end;
      ' ':
        begin
          if vHasDigits then
          begin
            Result := True;
            Break;
          end;
        end;
    else
      Result := True;
      Break;
    end;
    Inc(AIndex);
  end;
end;

procedure SetStringsLineBreak(const AStrings: TsgStringList;
  const AText: string; ALineBreak: Char = cnstLineBreak);
begin
  if (Length(AText) > 0) and (StringScan(ALineBreak, AText, 1) < 1) then
  begin
    if (ALineBreak <> cnstLineBreak) and
      (StringScan(cnstLineBreak, AText, 1) > 0) then
      ALineBreak := cnstLineBreak
    else
      ALineBreak := cnstLineBreakDef;
  end;
  AStrings.LineBreak := ALineBreak;
end;

procedure SetStringsText(const AStrings: TsgStringList; const AText: string);
begin
  AStrings.Text := AText;
  if AStrings.LineBreak = cnstLineBreakDef then
    CheckStringParams(AStrings);
end;

function sgConvToFloat(const AString: string): Double;
var
  I, vLenght: Integer;
  vString: string;
  vValue: Extended;
begin
  Result := 0;
  vLenght := Length(AString);
  vString := Copy(AString, 1, vLenght);
  if vLenght > 0 then
  begin
    I := 1;
    while ScanDigitsInString(vString, vLenght, False, I) do
    begin
      vString := Copy(vString, 1, I - 1);
      I := 1;
      vLenght := Length(vString);
    end;
    if Length(vString) > 0 then
    begin
      SysUtils.TextToFloat(PChar(vString), vValue, fvExtended);
      Result := vValue;
    end;
  end
end;

function BoolToStr(const AValue: Boolean): string;
begin
  if AValue then
    Result := cnstTrue
  else
    Result := cnstFalse;
end;

function FloatToString(const AValue: Double): string;
begin
  Result := DoubleToStr(AValue, cnstDecimalSeparatorFloat)
end;

function FPointToStr(const AValue: TFPoint): string;
begin
  Result := FloatToString(AValue.X) + cnstDecimalSeparatorValues +
    FloatToString(AValue.Y);
  if AValue.Z <> 0 then
  begin
    Result := Result + cnstDecimalSeparatorValues + FloatToString(AValue.Z);
  end;
end;

function TPointToStr(const AValue: TPoint): string;
begin
  Result := IntToStr(AValue.X) + cnstDecimalSeparatorValues +
    IntToStr(AValue.Y);
end;

function TRectToStr(const ARect: TRect): string;
begin
  Result := TPointToStr(ARect.TopLeft) + cnstDecimalSeparatorValues +
    TPointToStr(ARect.BottomRight);
end;

function HandleToStr(const AValue: UInt64): string;
begin
  Result := '$' + IntToHex(AValue, 0);
end;

function PointerToStr(const AValue: Pointer): string;
begin
  Result := '$' + IntToHex({$IFDEF SG_CPUX64}UInt64(AValue),
    16{$ELSE}Cardinal(AValue), 8{$ENDIF});
end;

function VersionToStr(const AValue: TsgVersion): string;
begin
  Result := IntToStr(AValue.Major) + cnstDecimalSeparatorVersion +
    IntToStr(AValue.Minor);
end;

function StrHexToColor(const AString: string): TsgColor;
var
  vStringColor: string;
begin
  if (Length(AString) = 4) and (AString[1] = '#') then
    vStringColor := AString[1] + AString[2] + AString[2] + AString[3] +
      AString[3] + AString[4] + AString[4]
  else
    vStringColor := AString;
  Result.Color := StrHexToInt(vStringColor);
end;

function StrHexToInt(const AString: string): Integer;
var
  vHexValue: string;
begin
  Result := 0;
  vHexValue := GetHexValue(AString);
  if Length(vHexValue) > 0 then
    Result := StrToInt(vHexValue);
end;

function StrRGBToColor(const AString: string): TsgColor;
var
  I, J: Integer;
  vStringColor: string;
begin
  Result.Color := 0;
  J := 3;
  for I := 0 to 2 do
  begin
    vStringColor := '';
    Inc(J);
    while J <= Length(AString) do
    begin
      if sgFunction.IsDigit(AString[J]) then
        vStringColor := vStringColor + AString[J]
      else if AString[J] = ',' then
        Break;
      Inc(J);
    end;
    Result.V[2 - I] := Round(StrToVal(vStringColor));
  end;
end;

function StrToColor(const AString: string): Integer;
var
  vColor: TsgColor;
  I: Integer;
  vBuffer: Byte;
  vStringColor, vValue: string;
begin
  vValue := AnsiLowerCase(AString);
  if StringPos(cnstRGB, vValue, 1) > 0 then
    vColor := StrRGBToColor(AString)
  else
  begin
    vStringColor := '';
    for I := 1 to Length(vValue) do
      case vValue[I] of
        '0' .. '9', 'a' .. 'z', '#':
          vStringColor := vStringColor + vValue[I]
      else
        if Length(vStringColor) > 0 then
          Break;
      end;
    if Length(vStringColor) > 0 then
    begin
      if vStringColor[1] <> cnstColorHexPrefix then
        vColor := GetColorByName(vStringColor)
      else
        vColor := StrHexToColor(vStringColor);
    end
    else
      vColor.Color := 0;
  end;
  vBuffer := vColor.R;
  vColor.R := vColor.B;
  vColor.B := vBuffer;

  Result := vColor.Color;
end;

function StrToColorHex(AString: string): TColor;
begin
  Result := sgFunction.StrToColorRGB(AString, cnstColorHexPrefix);
end;

function StrToBool(const AString: string; var ABool: Boolean): Boolean;
var
  vValue: string;
begin
  ABool := False;
  Result := True;
  vValue := AnsiLowerCase(AString);
  if vValue = cnstTrue then
    ABool := True
  else if vValue <> cnstFalse then
    Result := False;
end;

function StringToFloat(const AString: string): Double;
var
  vDs: Char;
begin
  vDs := SetDecimalSeparator(cnstDecimalSeparatorFloat);
  try
    Result := sgConvToFloat(AString);
  finally
    SetDecimalSeparator(vDs);
  end;
end;

function StrToFPoint(const AString: string): TFPoint;
var
  vDs: Char;
{$IFDEF SG_OPENING_IN_THEADS}
  Spliter: TsgStringList;
{$ENDIF}
begin
  Result := cnstFPointZero;
{$IFDEF SG_OPENING_IN_THEADS}
  Spliter := TsgStringList.Create;
  try
{$ENDIF}
    StrToStrings(AString, cnstDecimalSeparatorValues, Spliter);
    if Spliter.Count > 0 then
    begin
      vDs := SetDecimalSeparator(cnstDecimalSeparatorFloat);
      try
        Result.X := sgConvToFloat(Spliter[0]);
        if Spliter.Count > 1 then
        begin
          Result.Y := sgConvToFloat(Spliter[1]);
          if Spliter.Count > 2 then
            Result.Z := sgConvToFloat(Spliter[2]);
        end;
      finally
        SetDecimalSeparator(vDs);
      end;
    end;
{$IFDEF SG_OPENING_IN_THEADS}
  finally
    Spliter.Free;
  end;
{$ENDIF}
end;

function StrToFRect(const AString: string; const AIs3D: Boolean = True): TFRect;
var
  I, J: Integer;
  vDs: Char;
{$IFDEF SG_OPENING_IN_THEADS}
  Spliter: TsgStringList;
{$ENDIF}
begin
  Result := cnstFRectZero;
{$IFDEF SG_OPENING_IN_THEADS}
  Spliter := TsgStringList.Create;
  try
{$ENDIF}
    StrToStrings(AString, cnstDecimalSeparatorValues, Spliter);
    vDs := SetDecimalSeparator(cnstDecimalSeparatorFloat);
    try
      I := 0;
      J := 0;
      while (I < Spliter.Count) and (J <= 5) do
      begin
        Result.D[J] := sgConvToFloat(Spliter[I]);
        if not AIs3D then
        begin
          case J of
            1, 4:
              Inc(J);
          end;
        end;
        Inc(J);
        Inc(I);
      end;
    finally
      SetDecimalSeparator(vDs);
    end;
{$IFDEF SG_OPENING_IN_THEADS}
  finally
    Spliter.Free;
  end;
{$ENDIF}
end;

function StrToF2DRect(const AString: string): TF2DRect;
begin
  Result := MakeF2dRectFromFRect(StrToFRect(AString, False));
end;

function StrToHandle(const AString: string): UInt64;
var
  vHexValue: string;
begin
  Result := 0;
  vHexValue := GetHexValue(AString);
  if Length(vHexValue) > 0 then
    Result := StrToInt(vHexValue);
end;

function StrToPointer(const AString: string): Pointer;
var
  vHexValue: string;
  vValue: {$IFDEF SG_CPUX64}UInt64{$ELSE}Cardinal{$ENDIF};
begin
  Result := nil;
  vHexValue := GetHexValue(AString);
  if Length(vHexValue) > 0 then
  begin
    vValue := StrToInt(vHexValue);
    Result := Pointer(vValue);
  end;
end;

function StrToTPoint(const AString: string): TPoint;
{$IFDEF SG_OPENING_IN_THEADS}
var
  Spliter: TsgStringList;
{$ENDIF}
begin
  Result := cnstPointZero;
{$IFDEF SG_OPENING_IN_THEADS}
  Spliter := TsgStringList.Create;
  try
{$ENDIF}
    StrToStrings(AString, cnstDecimalSeparatorValues, Spliter);
    if Spliter.Count > 0 then
    begin
      Result.X := StrToIntDef(Spliter[0], 0);
      if Spliter.Count > 1 then
        Result.Y := StrToIntDef(Spliter[1], 0);
    end;
{$IFDEF SG_OPENING_IN_THEADS}
  finally
    Spliter.Free;
  end;
{$ENDIF}
end;

function StrToTRect(const AString: string): TRect;
var
  vValues: array [0 .. 3] of Integer absolute Result;
  I: Integer;
  vDs: Char;
{$IFDEF SG_OPENING_IN_THEADS}
  Spliter: TsgStringList;
{$ENDIF}
begin
  Result := cnstRectZero;
{$IFDEF SG_OPENING_IN_THEADS}
  Spliter := TsgStringList.Create;
  try
{$ENDIF}
    StrToStrings(AString, cnstDecimalSeparatorValues, Spliter);
    vDs := SetDecimalSeparator(cnstDecimalSeparatorFloat);
    try
      I := 0;
      while (I < Spliter.Count) and (I <= 3) do
      begin
        if Length(Spliter[I]) > 0 then
          vValues[I] := StrToIntDef(Spliter[I], 0);
        Inc(I);
      end;
    finally
      SetDecimalSeparator(vDs);
    end;
{$IFDEF SG_OPENING_IN_THEADS}
  finally
    Spliter.Free;
  end;
{$ENDIF}
end;

function StrToVal(const AString: string): Double;
var
  vDs: Char;
begin
  vDs := SetDecimalSeparator(cnstDecimalSeparatorFloat);
  try
    Result := sgConvToFloat(AString);
  finally
    SetDecimalSeparator(vDs);
  end;
end;

function StrToVersion(const AString: string): TsgVersion;
{$IFDEF SG_OPENING_IN_THEADS}
Spliter:
TsgStringList;
{$ENDIF}
begin
  FillChar(Result, SizeOf(Result), 0);
{$IFDEF SG_OPENING_IN_THEADS}
  Spliter := TsgStringList.Create;
  try
{$ENDIF}
    StrToStrings(AString, cnstDecimalSeparatorVersion, Spliter);
    if Spliter.Count > 0 then
    begin
      Result.Major := StrToIntDef(Spliter[0], 0);
      if Spliter.Count > 1 then
        Result.Minor := StrToIntDef(Spliter[1], 0);
    end;
{$IFDEF SG_OPENING_IN_THEADS}
  finally
    Spliter.Free;
  end;
{$ENDIF}
end;

function StrToPointsList(const AString: string; const AIs3D: Boolean;
  const AList: Pointer; const AAddProc: TsgProcOfListAddPoint;
  const ASeparator: Char = cnstLineBreak): Integer;
var
  I, J: Integer;
  vStrings: TsgStringList;
  vPoint: TFPoint;
begin
  Result := 0;
  if Length(AString) > 0 then
  begin
    vStrings := TsgStringList.Create;
    try
      StrToStrings(AString, ASeparator, vStrings);
      I := 0;
      while I < vStrings.Count do
      begin
        J := 0;
        vPoint := cnstFPointZero;
        while J < cnstCountValue[AIs3D] do
        begin
          if (I < vStrings.Count) and (Length(vStrings[I]) > 0) then
          begin
            vPoint.V[J] := sgConvToFloat(vStrings[I]);
            Inc(I);
          end
          else
            Break;
          Inc(J);
        end;
        if J > 0 then
        begin
          AAddProc(AList, vPoint);
          Inc(Result);
        end
        else
          Break;
      end;
    finally
      vStrings.Free;
    end;
  end;
end;

procedure CheckSepatator(var AStr: string;
  const ASeparator: Char = cnstLineBreak);
var
  vLength: Integer;
begin
  vLength := Length(AStr);
  if (vLength > 0) and (AStr[vLength] = ASeparator) then
    SetLength(AStr, vLength - 1);
end;

function PointToStr(const APoint: TFPoint; const AIs3D: Boolean;
  const ASeparator: Char = cnstLineBreak): string;
begin
  Result := ValToStr(APoint.X) + ASeparator + ValToStr(APoint.Y) + ASeparator;
  if AIs3D then
    Result := Result + ValToStr(APoint.Z) + ASeparator;
end;

function PointsListToStr(const AIs3D: Boolean; const AList: TObject;
  const ACount: Integer; const GetProc: TsgProcOfListGetPoint;
  const ASeparator: Char = cnstLineBreak): string; overload;
var
  I: Integer;
begin
  Result := '';
  for I := 0 to ACount - 1 do
    Result := Result + PointToStr(GetProc(AList, I), AIs3D, ASeparator);
  CheckSepatator(Result, ASeparator);
end;

function PointsListToStr(const AIs3D: Boolean; AList: IsgArrayFPoint;
  const ASeparator: Char = cnstLineBreak): string; overload;
var
  I: Integer;
begin
  Result := '';
  for I := 0 to AList.FPointCount - 1 do
    Result := Result + PointToStr(AList.FPoints[I], AIs3D, ASeparator);
  CheckSepatator(Result, ASeparator);
end;

function ColorHexToStr(const AColor: TColor): string;
begin
  Result := sgFunction.ColorRGBToStr(AColor, cnstColorHexPrefix);
end;

function FRectToStr(const ARect: TFRect; const AIs3D: Boolean = True): string;
begin
  Result := PointToStr(ARect.TopLeft, AIs3D, cnstDecimalSeparatorValues) +
    PointToStr(ARect.BottomRight, AIs3D, cnstDecimalSeparatorValues);
  SetLength(Result, Length(Result) - 1);
end;

function F2DRectToStr(const ARect: TF2DRect): string;
begin
  Result := FRectToStr(MakeFRectFromF2dRect(ARect), False);
end;

function StrToSingleList(const AString: string; const AList: Pointer;
  const AAddProc: TsgProcOfListAddSingle;
  const ASeparator: Char = cnstLineBreak): Integer;
var
  I: Integer;
  vStrings: TsgStringList;
  vValue: Single;
begin
  Result := 0;
  if Length(AString) > 0 then
  begin
    vStrings := TsgStringList.Create;
    try
      StrToStrings(AString, ASeparator + cnstLineBreakDef, vStrings);
      I := 0;
      while I < vStrings.Count do
      begin
        vValue := sgConvToFloat(vStrings[I]);
        AAddProc(AList, vValue);
        Inc(Result);
        Inc(I);
      end;
    finally
      vStrings.Free;
    end;
  end;
end;

function SingleListToStr(const AList: TObject; const ACount: Integer;
  const GetProc: TsgProcOfListGetSingle;
  const ASeparator: Char = cnstLineBreak): string;
var
  I: Integer;
  vItem: Single;
  vSeparator: string;
begin
  Result := '';
  for I := 0 to ACount - 1 do
  begin
    vSeparator := ASeparator + cnstLineBreakDef;
    vItem := GetProc(AList, I);
    Result := Result + ValToStr(vItem) + vSeparator;
  end;
end;

function Utf8ToUni(ADst: PWideChar; AMaxDstLen: DWORD; ASrc: PChar;
  ASrcLen: DWORD): DWORD;
var
  I, Cnt: DWORD;
  vChar: Byte;
  vUnicode: DWORD;
begin
  if ASrc = nil then
  begin
    Result := 0;
    Exit;
  end;
  Result := $FFFFFFFF;
  Cnt := 0;
  I := 0;
  if ADst <> nil then
  begin
    while (I < ASrcLen) and (Cnt < AMaxDstLen) do
    begin
      vUnicode := DWORD(ASrc[I]);
      Inc(I);
      if (vUnicode and $80) <> 0 then
      begin
        vUnicode := vUnicode and $3F;
        if I > ASrcLen then
          Exit;
        if (vUnicode and $20) <> 0 then
        begin
          vChar := Byte(ASrc[I]);
          Inc(I);
          if ((vChar and $C0) <> $80) or (I > ASrcLen) then
            Exit;
          vUnicode := (vUnicode shl 6) or (vChar and $3F);
        end;
        vChar := Byte(ASrc[I]);
        Inc(I);
        if (vChar and $C0) <> $80 then
          Exit;
        ADst[Cnt] := WideChar((vUnicode shl 6) or (vChar and $3F));
      end
      else
        ADst[Cnt] := WideChar(vUnicode);
      Inc(Cnt);
    end;
    if Cnt >= AMaxDstLen then
      Cnt := AMaxDstLen - 1;
    ADst[Cnt] := #0;
  end
  else
  begin
    while (I <= ASrcLen) do
    begin
      vChar := Byte(ASrc[I]);
      Inc(I);
      if (vChar and $80) <> 0 then
      begin
        if ((vChar and $F0) = $F0) or ((vChar and $40) = 0) or (I > ASrcLen) or
          ((Byte(ASrc[I]) and $C0) <> $80) then
          Exit;
        Inc(I);
        if (I > ASrcLen) or (((vChar and $20) <> 0) and
          ((Byte(ASrc[I]) and $C0) <> $80)) then
          Exit;
        Inc(I);
      end;
      Inc(Cnt);
    end;
  end;
  Result := Cnt + 1;
end;

function ValToStr(const ADouble: Double): string;
// var
// vDs: Char;
begin
  // vDs := SetDecimalSeparator(cnstDecimalSeparatorFloat);
  // try
  Result := DoubleToStr(ADouble, cnstDecimalSeparatorFloat);
  // finally
  // SetDecimalSeparator(vDs);
  // end;
end;

{$IFDEF SG_PARSER_DEBUG}

procedure AddNode(const ANode: TsgNode; const ATreeNode: TTreeNode;
  const ATree: TTreeView);
var
  I: Integer;
  vNewTreeNode: TTreeNode;
  vNodeText, vNodeName, vNodeValue: string;
  vAttrNode: TsgNode;
begin
  vNodeText := ANode.Name;
  if Length(ANode.Value) > 0 then
    vNodeText := vNodeText + ' = ' + ANode.Value;
  if Length(ANode.Text) > 0 then
    vNodeText := vNodeText + ' : ' + ANode.Text;
  vNodeText := vNodeText + ' :: ' + cnstNodeTypeNames[ANode.NodeType];
  vNewTreeNode := ATree.Items.AddChild(ATreeNode, vNodeText);
  if ANode.HasAttributeNodes then
    for I := 0 to ANode.AttributeNodesCount - 1 do
    begin
      vAttrNode := TsgNode(ANode.AttributeNodes[I]);
      vNodeName := vAttrNode.Name;
      vNodeValue := vAttrNode.Value;
      ATree.Items.AddChild(vNewTreeNode, '[' + vNodeName + ' = ' +
        vNodeValue + ']');
    end;
  if ANode.HasChildNodes then
    for I := 0 to ANode.ChildNodesCount - 1 do
      AddNode(TsgNode(ANode.ChildNodes[I]), vNewTreeNode, ATree);
end;

function TestParser(const AFileName: string; const Tree: TTreeView): DWORD;
var
  I: Integer;
  vParser: TsgParser;
begin
  vParser := TsgParser.Create;
  try
    Result := GetTickCount;
    vParser.LoadFromFile(AFileName);
    Result := GetTickCount - Result;
    for I := 0 to vParser.ROOT.Count - 1 do
      AddNode(TsgNode(vParser.ROOT[I]), nil, Tree);
  finally
    vParser.Free;
  end;
end;
{$ENDIF}
{ TsgBase64 }

procedure TsgBase64.EnCode(const AInStream: TStream; const AOutStream: TStream);
const
  cnstColumnWidth = 75;
type
  PInteger = ^Integer;
var
  vInBuffer: array [0 .. 509] of Byte;
  vOutBuffer: array [0 .. 1023] of AnsiChar;
  vBuffer: PAnsiChar;
  I, J, K, BytesRead: Integer;
  vBlock: TsgParametrs;
begin
  K := 0;
  repeat
    BytesRead := AInStream.Read(vInBuffer, SizeOf(vInBuffer));
    I := 0;
    vBuffer := vOutBuffer;
    while I < BytesRead do
    begin
      if BytesRead - I < 3 then
        J := BytesRead - I
      else
        J := 3;
      vBlock.Parametrs := 0;
      vBlock.Param1 := vInBuffer[I];
      if J > 1 then
        vBlock.Param2 := vInBuffer[I + 1];
      if J > 2 then
        vBlock.Param3 := vInBuffer[I + 2];
      vBuffer[0] := EncodeBase64Table[vBlock.Params[0] shr 2];
      vBuffer[1] := EncodeBase64Table
        [((vBlock.Params[0] shl 4) or (vBlock.Params[1] shr 4)) and $0000003F];
      if J < 2 then
        vBuffer[2] := '='
      else
        vBuffer[2] := EncodeBase64Table
          [((vBlock.Params[1] shl 2) or (vBlock.Params[2] shr 6)) and
          $0000003F];
      if J < 3 then
        vBuffer[3] := '='
      else
        vBuffer[3] := EncodeBase64Table[vBlock.Params[2] and $0000003F];
      (* FillChar(vBuffer[2],2,'=');
        if J >= 2 then
        begin
        vBuffer[2] := EncodeBase64Table[((vBlock.Params[1] shl 2) or (vBlock.Params[2] shr 6)) and $0000003f];
        if J = 3 then
        vBuffer[3] := EncodeBase64Table[vBlock.Params[2] and $0000003f];
        end;
      *)
      Inc(I, 3);
      Inc(vBuffer, 4);
      Inc(K, 4);
      if K > cnstColumnWidth then
      begin
        vBuffer[0] := #$0D;
        vBuffer[1] := #$0A;
        Inc(vBuffer, 2);
        K := 0;
      end;
    end;
    AOutStream.Write(vOutBuffer, vBuffer - PAnsiChar(@vOutBuffer));
  until BytesRead = 0;
end;

function TsgBase64.Next: Char;
begin
  repeat
    Result := #0;
    if FCol > Length(FBuffer) then
    begin
      Inc(FRow);
      if FRow >= FRMax then
        Exit;
      FCol := 1;
      FBuffer := FStrs[FRow];
      if FBuffer = '' then
        Continue;
    end;
    Result := FBuffer[FCol];
    Inc(FCol);
  until CharInSet(Result, ['+', '/', '0' .. '9', 'A' .. 'Z', 'a' .. 'z']);
end;

procedure TsgBase64.Decode(const AStr: string; AStrs: TStrings;
  const S: TStream);
{
  Initial data - or single line Str, or the list of lines Strs
  The result is written in stream S or (at S=nil) is refunded as a line
}
var
  I, Count, Index, Read: Integer;
  Ch: Char;
  Buf: array [0 .. 3076] of AnsiChar;
  pBuf: PInteger;
begin
  Count := 0;
  FRow := 0;
  FCol := 1;
  FRMax := 0;
  FBuffer := AStr;
  FStrs := AStrs;
  if (FStrs <> nil) and (FStrs.Count > 0) then
  begin
    FRMax := FStrs.Count;
    FBuffer := FStrs[0];
  end;
  repeat
    if Count >= 3072 then
    begin
      if S <> nil then
        S.WriteBuffer(Buf[0], Count);
      Count := 0;
    end;
    pBuf := @Buf[Count];
    pBuf^ := 0;
    Read := 0;
    for I := 0 to 3 do
    begin
      Ch := Next;
      case Ch of
        'A' .. 'Z':
          Index := Ord(Ch) - Ord('A');
        'a' .. 'z':
          Index := Ord(Ch) - Ord('a') + 26;
        '0' .. '9':
          Index := Ord(Ch) - Ord('0') + 52;
        '+':
          Index := 62;
        '/':
          Index := 63;
      else
        Index := 0;
      end;
      if Ch <> #0 then
        Inc(Read);
      pBuf^ := pBuf^ shl 6 or Index;
    end;
    pBuf^ := SwapWords(pBuf^) shr 8;
    Inc(Count, 3);
  until Read < 4;
  case Read of
    0:
      Dec(Count, 3);
    1, 2:
      Dec(Count, 2);
    3:
      Dec(Count);
  end;
  if Count < 0 then
    Exit;
  if S <> nil then
    S.WriteBuffer(Buf[0], Count)
end;

{ TsgNodeList }

function TsgNodeList.GetCaseSensitivity: Boolean;
begin
  Result := (FFlags and 2) <> 0;
end;

function TsgNodeList.GetNode(const AIndex: Integer): TsgNodeSample;
begin
  Result := List[AIndex];
end;

function TsgNodeList.GetNodeByName(const AName: string): TsgNodeSample;
var
  I: Integer;
  vNode: TsgNodeSample;
  vCaseSensitivity: Boolean;
begin
  Result := nil;
  if QwickFind then
  begin
    I := FSortedList.IndexOf(AName);
    if I > -1 then
      Result := TsgNodeSample(FSortedList.Objects[I]);
  end
  else
  begin
    vCaseSensitivity := CaseSensitivity;
    for I := 0 to Count - 1 do
    begin
      vNode := TsgNodeSample(List[I]);
      if (vNode.NodeType in [ntElement, ntAttribute]) and
        (AnsiCmprStr(vNode.Name, AName, vCaseSensitivity) = 0) then
      begin
        Result := vNode;
        Break;
      end;
    end;
  end;
end;

function TsgNodeList.GetQwickFind: Boolean;
begin
  Result := FSortedList <> nil;
end;

procedure TsgNodeList.Notify(Ptr: Pointer; Action: TListNotification);
var
  I: Integer;
begin
  inherited Notify(Ptr, Action);
  if (Ptr <> nil) and QwickFind then
  begin
    case Action of
      lnAdded:
        begin
          FSortedList.AddObject(TsgNodeSample(Ptr).Name, Ptr);
        end;
      lnDeleted:
        begin
          if FSortedList.Count > 0 then
          begin
            I := FSortedList.IndexOf(TsgNodeSample(Ptr).Name);
            if (I > -1) and (FSortedList.Objects[I] <> Ptr) then
              I := -1;
            if I < 0 then
              I := FSortedList.IndexOfObject(Ptr);
            if I > -1 then
              FSortedList.Delete(I);
          end;
        end;
    end;
  end;
end;

procedure TsgNodeList.UpdateNode(const ANode: TsgNodeSample);
var
  I: Integer;
begin
  if QwickFind then
  begin
    I := FSortedList.IndexOfObject(ANode);
    if I > -1 then
    begin
      FSortedList.Delete(I);
      FSortedList.AddObject(ANode.Name, ANode);
    end;
  end;
end;

procedure TsgNodeList.SetCaseSensitivity(const AValue: Boolean);
begin
  FFlags := (FFlags and 253) or (Byte(AValue) shl 1);
  if QwickFind then
    TsgStringList(FSortedList).CaseSensitive := AValue;
end;

procedure TsgNodeList.SetQwickFind(const AValue: Boolean);
begin
  if AValue then
  begin
    if FSortedList = nil then
      CreateSortedList;
  end
  else
    FreeAndNil(FSortedList);
end;

class function TsgNodeList.CreateNodeList(const AQwickFind: Boolean = False)
  : TsgNodeList;
begin
  Result := TsgNodeList.Create;
  Result.QwickFind := AQwickFind;
end;

procedure TsgNodeList.CreateSortedList;
var
  I: Integer;
  vNode: TsgNodeSample;
begin
  FSortedList := TsgStringList.Create;
  TsgStringList(FSortedList).CaseSensitive := CaseSensitivity;
  FSortedList.Capacity := Count;
  FSortedList.Duplicates := dupAccept;
  for I := 0 to Count - 1 do
  begin
    vNode := List[I];
    FSortedList.AddObject(vNode.Name, vNode);
  end;
end;

destructor TsgNodeList.Destroy;
begin
  ClearNodes;
  FreeAndNil(FSortedList);
  inherited Destroy;
end;

procedure TsgNodeList.ClearNodes;
var
  I: Integer;
begin
  if QwickFind then
    FSortedList.Clear;
  for I := Count - 1 downto 0 do
    FreeAndNil(TObject(List[I]));
  Count := 0;
end;

{ TsgNodeSample }

procedure TsgNodeSample.CheckText(const ANode: TsgNodeSample);
begin
end;

procedure TsgNodeSample.Clear;
begin
end;

function TsgNodeSample.Compare(const ANode: TsgNodeSample;
  const ACompareChilds: Boolean): Integer;
begin
  Result := 1;
  if ClassType = ANode.ClassType then
  begin
    Result := CompareText(Name, ANode.Name);
    if Result = 0 then
      Result := ValueData.Compare(ANode.ValueData, False);
    if Result = 0 then
      Result := CompareAttributes(ANode);
    if (Result = 0) and ACompareChilds then
      Result := CompareChilds(ANode, True);
  end;
end;

function TsgNodeSample.CompareAttributes(const ANode: TsgNodeSample): Integer;
var
  I: Integer;
begin
  Result := 0;
  if HasAttributeNodes then
  begin
    if ANode.HasAttributeNodes then
    begin
      if AttributeNodesCount = ANode.AttributeNodesCount then
      begin
        for I := 0 to AttributeNodesCount - 1 do
        begin
          Result := AttributeNodes[I].Compare(ANode.AttributeNodes[I], False);
          if Result <> 0 then
            Break;
        end;
      end
      else
      begin
        if AttributeNodesCount > ANode.AttributeNodesCount then
          Result := 1
        else
          Result := -1;
      end;
    end
    else
      Result := 1;
  end
  else
  begin
    if ANode.HasAttributeNodes then
      Result := -1;
  end;
end;

function TsgNodeSample.CompareChilds(const ANode: TsgNodeSample;
  const ACompareChilds: Boolean): Integer;
var
  I: Integer;
begin
  Result := 0;
  if HasChildNodes then
  begin
    if ANode.HasChildNodes then
    begin
      if ChildNodesCount = ANode.ChildNodesCount then
      begin
        for I := 0 to ChildNodesCount - 1 do
        begin
          Result := ChildNodes[I].Compare(ANode.ChildNodes[I], True);
          if Result <> 0 then
            Break;
        end;
      end
      else
      begin
        if ChildNodesCount > ANode.ChildNodesCount then
          Result := 1
        else
          Result := -1;
      end;
    end
    else
      Result := 1;
  end
  else
  begin
    if ANode.HasChildNodes then
      Result := -1;
  end;
end;

{$IFDEF SGDEL_2009}

function TsgNodeSample.GetAttributesEnumerable: IEnumerable<TsgNodeSample>;
begin
  Result := TsgNodeEnumerator.Create(nil);
end;

function TsgNodeSample.GetChildsEnumerable: IEnumerable<TsgNodeSample>;
begin
  Result := TsgNodeEnumerator.Create(nil);
end;
{$ENDIF}

function TsgNodeSample.GetAttribNode(const AIndex: Integer): TsgNodeSample;
begin
  Result := nil;
end;

function TsgNodeSample.GetAttributeNodesCount: Integer;
begin
  Result := 0;
end;

function TsgNodeSample.GetChildByName(const AName: string): TsgNodeSample;
begin
  Result := nil;
end;

function TsgNodeSample.GetChildNode(const AIndex: Integer): TsgNodeSample;
begin
  Result := nil;
end;

function TsgNodeSample.GetChildNodeCount: Integer;
begin
  Result := 0;
end;

function TsgNodeSample.GetClassID: Cardinal;
begin
  Result := cnstClassID_XML;
end;

function TsgNodeSample.GetClosed: Boolean;
begin
  Result := True;
end;

function TsgNodeSample.GetCountOwner: Integer;
var
  vOwner: TsgNodeSample;
begin
  Result := 0;
  vOwner := FOwner;
  while Assigned(vOwner) do
  begin
    vOwner := vOwner.FOwner;
    Inc(Result);
  end;
end;

function TsgNodeSample.GetDataInternal(const AOwner: Pointer): Pointer;
begin
  Result := nil;
end;

function TsgNodeSample.GetFullName: string;
var
  vPrefix: string;
begin
  vPrefix := Prefix;
  if Length(vPrefix) = 0 then
    Result := Name
  else
    Result := vPrefix + cnstSeporatorFullName + Name;
end;

function TsgNodeSample.GetPosition: TsgNodeSamplePosition;
begin
  Result := FPosition;
end;

function TsgNodeSample.GetName: string;
begin
  Result := ClassName;
  Delete(Result, 1, 7);
end;

function TsgNodeSample.GetNodeType: TsgNodeType;
begin
  Result := ntUndefined;
end;

function TsgNodeSample.GetOwnerList(const AType: TsgNodeType): TsgNodeList;
begin
  Result := nil;
end;

function TsgNodeSample.GetPostamble: Boolean;
begin
  Result := False;
end;

function TsgNodeSample.GetPrefix: string;
begin
  Result := '';
end;

function TsgNodeSample.GetSaveValueStr: string;
begin
  Result := '';
  if Length(ValueAsStr) > 0 then
    Result := '="' + ValueAsText + '"';
end;

function TsgNodeSample.GetSpacesBySave: string;
var
  I: Integer;
begin
  I := GetCountOwner;
  Result := StringOfChar(' ', I * 2);
end;

function TsgNodeSample.GetTextAsBool: Boolean;
begin
  Result := TsgData.GetAsBool(TextData);
end;

function TsgNodeSample.GetTextAsDouble: Double;
begin
  Result := TsgData.GetAsDouble(TextData);
end;

function TsgNodeSample.GetTextAsF2DRect: TF2DRect;
begin
  Result := TsgData.GetAsF2DRect(TextData);
end;

function TsgNodeSample.GetTextAsFPoint: TFPoint;
begin
  Result := TsgData.GetAsFPoint(TextData);
end;

function TsgNodeSample.GetTextAsFRect: TFRect;
begin
  Result := TsgData.GetAsFRect(TextData);
end;

function TsgNodeSample.GetTextAsInt: Integer;
begin
  Result := TsgData.GetAsInt(TextData);
end;

function TsgNodeSample.GetTextAsInt64: Int64;
begin
  Result := TsgData.GetAsInt64(TextData);
end;

function TsgNodeSample.GetTextAsPointer: Pointer;
begin
  Result := TsgData.GetAsPointer(TextData);
end;

function TsgNodeSample.GetTextAsStr: string;
begin
  Result := TsgData.GetAsStr(TextData);
end;

function TsgNodeSample.GetTextAsText: string;
begin
  Result := TsgData.GetAsText(TextData);
end;

function TsgNodeSample.GetTextData: TsgData;
begin
  Result := nil;
end;

function TsgNodeSample.GetTextAsHandle: UInt64;
begin
  Result := TsgData.GetAsHandle(TextData);
end;

function TsgNodeSample.GetUnitsOfMeasurements: TsgUnitsOfMeasurements;
var
  vValueData: TsgData;
begin
  Result := umUndefined;
  vValueData := ValueData;
  if Assigned(vValueData) then
    Result := vValueData.Measurements;
end;

function TsgNodeSample.GetValueAsBool: Boolean;
begin
  Result := TsgData.GetAsBool(ValueData);
end;

function TsgNodeSample.GetValueAsDouble: Double;
begin
  Result := TsgData.GetAsDouble(ValueData);
end;

function TsgNodeSample.GetValueAsF2DRect: TF2DRect;
begin
  Result := TsgData.GetAsF2DRect(ValueData);
end;

function TsgNodeSample.GetValueAsFPoint: TFPoint;
begin
  Result := TsgData.GetAsFPoint(ValueData);
end;

function TsgNodeSample.GetValueAsFRect: TFRect;
begin
  Result := TsgData.GetAsFRect(ValueData);
end;

function TsgNodeSample.GetValueAsInt: Integer;
begin
  Result := TsgData.GetAsInt(ValueData);
end;

function TsgNodeSample.GetValueAsInt64: Int64;
begin
  Result := TsgData.GetAsInt64(ValueData);
end;

function TsgNodeSample.GetValueAsPointer: Pointer;
begin
  Result := TsgData.GetAsPointer(ValueData);
end;

function TsgNodeSample.GetValueAsStr: string;
begin
  Result := TsgData.GetAsStr(ValueData);
end;

function TsgNodeSample.GetValueAsText: string;
begin
  Result := TsgData.GetAsText(ValueData);
end;

function TsgNodeSample.GetValueData: TsgData;
begin
  Result := nil;
end;

function TsgNodeSample.GetValueAsHandle: UInt64;
begin
  Result := TsgData.GetAsHandle(ValueData);
end;

procedure TsgNodeSample.Read(const AParser: TsgParser);
begin
end;

procedure TsgNodeSample.ReName(const ANewName: string);
var
  vNodes: TsgNodeList;
begin
  vNodes := nil;
  if Length(Name) > 0 then
    vNodes := GetOwnerList(NodeType);
  SetName(ANewName);
  if Assigned(vNodes) then
    vNodes.UpdateNode(Self);
end;

{$IFDEF SG_EXPORT_WITH_STRINGS}

procedure TsgNodeSample.SaveToStrings(const AStrings: TStrings);
var
  vAttribute: TsgNodeSample;
  I: Integer;
  vTmp, vSpaces, vName: string;
begin
  vSpaces := GetSpacesBySave;

  vName := ReplaceAnsiCodesEx(Name);

  vTmp := vSpaces + '<' + vName;
  vTmp := vTmp + GetSaveValueStr;

  for I := 0 to GetAttributeNodesCount - 1 do
  begin
    vAttribute := AttributeNodes[I];
    vTmp := vTmp + ' ' + ReplaceAnsiCodesEx(vAttribute.Name) + '="' +
      vAttribute.ValueAsText + '"';
  end;
  if (GetChildNodeCount > 0) or (Length(TextAsStr) > 0) then
  begin
    vTmp := vTmp + '>' + TextAsText;
    if GetChildNodeCount > 0 then
    begin
      AStrings.AddObject(vTmp, Self);
      try
        vTmp := '';
        for I := 0 to GetChildNodeCount - 1 do
          ChildNodes[I].SaveToStrings(AStrings);
      finally
        AStrings.AddObject(vSpaces + '</' + vName + '>', Self);
      end;
    end
    else
      AStrings.AddObject(vTmp + '</' + vName + '>', Self);
  end
  else
    AStrings.AddObject(vTmp + '/>', Self);
end;
{$ELSE}

procedure TsgNodeSample.SaveString(const AStream: TStream;
  const AString: string);
{$IFDEF SGDEL_2009}
var
  vStringStream: TStringStream;
begin
  if TsgParser.SaveEncoding <> TEncoding.Unicode then
  begin
    vStringStream := TStringStream.Create(AString,
      TsgParser.SaveEncoding, False);
    try
      vStringStream.SaveToStream(AStream);
    finally
      vStringStream.Free;
    end;
  end
  else
    AStream.WriteBuffer(Pointer(AString)^, Length(AString) * SizeOf(Char));
{$ELSE}
begin
  AStream.WriteBuffer(Pointer(AString)^, Length(AString) * SizeOf(Char));
{$ENDIF}
end;

procedure TsgNodeSample.SaveToStream(const AStream: TStream);
var
  vAttribute: TsgNodeSample;
  I: Integer;
  vTmp, vSpaces, vName: string;
begin
  vSpaces := GetSpacesBySave;

  vName := ReplaceAnsiCodesEx(Name);

  vTmp := vSpaces + '<' + vName;
  vTmp := vTmp + GetSaveValueStr;

  for I := 0 to GetAttributeNodesCount - 1 do
  begin
    vAttribute := AttributeNodes[I];
    vTmp := vTmp + ' ' + ReplaceAnsiCodesEx(vAttribute.Name) + '="' +
      vAttribute.ValueAsText + '"';
  end;

  if (GetChildNodeCount > 0) or (Length(TextAsStr) > 0) then
  begin
    vTmp := vTmp + '>' + TextAsText;
    if GetChildNodeCount > 0 then
      vTmp := vTmp + sLineBreak;
    SaveString(AStream, vTmp);
    try
      vTmp := '';
      for I := 0 to GetChildNodeCount - 1 do
        ChildNodes[I].SaveToStream(AStream);
    finally
      vTmp := '</' + vName + '>' + sLineBreak;
      if GetChildNodeCount > 0 then
        vTmp := vSpaces + vTmp;
      SaveString(AStream, vTmp);
    end;
  end
  else
  begin
    vTmp := vTmp + '/>' + sLineBreak;
    SaveString(AStream, vTmp);
  end;
end;
{$ENDIF}

procedure TsgNodeSample.SetClosed(const AValue: Boolean);
begin
end;

procedure TsgNodeSample.SetFullName(const AValue: string);
var
  vIndex, vLength: Integer;
begin
  vLength := Length(AValue);
  if vLength > 0 then
  begin
    vIndex := StringScan(cnstSeporatorFullName, AValue, 1);
    if vIndex > 0 then
    begin
      Prefix := Copy(AValue, 1, vIndex - 1);
      Name := Copy(AValue, vIndex + 1, vLength - vIndex);
    end
    else
    begin
      Name := AValue;
      Prefix := '';
    end;
  end
  else
  begin
    Name := '';
    Prefix := '';
  end;
end;

procedure TsgNodeSample.SetPosition(const AValue: TsgNodeSamplePosition);
begin
  FPosition := AValue;
end;

procedure TsgNodeSample.SetName(const AValue: string);
begin
end;

procedure TsgNodeSample.SetNodeType(const AValue: TsgNodeType);
begin
end;

procedure TsgNodeSample.SetPostamble(const AValue: Boolean);
begin
end;

procedure TsgNodeSample.SetPrefix(const AValue: string);
begin
end;

procedure TsgNodeSample.SetReader(const AReader: Pointer);
begin
end;

procedure TsgNodeSample.SetTextAsBool(const AValue: Boolean);
begin
  TsgData.SetAsBool(TextData, AValue);
end;

procedure TsgNodeSample.SetTextAsDouble(const AValue: Double);
begin
  TsgData.SetAsDouble(TextData, AValue);
end;

procedure TsgNodeSample.SetTextAsF2DRect(const AValue: TF2DRect);
begin
  TsgData.SetAsF2DRect(TextData, AValue);
end;

procedure TsgNodeSample.SetTextAsFPoint(const AValue: TFPoint);
begin
  TsgData.SetAsFPoint(TextData, AValue);
end;

procedure TsgNodeSample.SetTextAsFRect(const AValue: TFRect);
begin
  TsgData.SetAsFRect(TextData, AValue);
end;

procedure TsgNodeSample.SetTextAsInt(const AValue: Integer);
begin
  TsgData.SetAsInt(TextData, AValue);
end;

procedure TsgNodeSample.SetTextAsInt64(const AValue: Int64);
begin
  TsgData.SetAsInt64(TextData, AValue);
end;

procedure TsgNodeSample.SetTextAsPointer(const AValue: Pointer);
begin
  TsgData.SetAsPointer(TextData, AValue);
end;

procedure TsgNodeSample.SetTextAsStr(const AValue: string);
begin
  TsgData.SetAsStr(TextData, AValue);
end;

procedure TsgNodeSample.SetTextAsText(const AValue: string);
begin
  TsgData.SetAsText(TextData, AValue);
end;

procedure TsgNodeSample.SetTextAsHandle(const AValue: UInt64);
begin
  TsgData.SetAsHandle(TextData, AValue);
end;

procedure TsgNodeSample.SetValueAsBool(const AValue: Boolean);
begin
  TsgData.SetAsBool(ValueData, AValue);
end;

procedure TsgNodeSample.SetValueAsDouble(const AValue: Double);
begin
  TsgData.SetAsDouble(ValueData, AValue);
end;

procedure TsgNodeSample.SetValueAsF2DRect(const AValue: TF2DRect);
begin
  TsgData.SetAsF2DRect(ValueData, AValue);
end;

procedure TsgNodeSample.SetValueAsFPoint(const AValue: TFPoint);
begin
  TsgData.SetAsFPoint(ValueData, AValue);
end;

procedure TsgNodeSample.SetValueAsFRect(const AValue: TFRect);
begin
  TsgData.SetAsFRect(ValueData, AValue);
end;

procedure TsgNodeSample.SetValueAsInt(const AValue: Integer);
begin
  TsgData.SetAsInt(ValueData, AValue);
end;

procedure TsgNodeSample.SetValueAsInt64(const AValue: Int64);
begin
  TsgData.SetAsInt64(ValueData, AValue);
end;

procedure TsgNodeSample.SetValueAsPointer(const AValue: Pointer);
begin
  TsgData.SetAsPointer(ValueData, AValue);
end;

procedure TsgNodeSample.SetValueAsStr(const AValue: string);
begin
  TsgData.SetAsStr(ValueData, AValue);
end;

procedure TsgNodeSample.SetValueAsText(const AValue: string);
begin
  TsgData.SetAsText(ValueData, AValue);
end;

procedure TsgNodeSample.SetValueAsHandle(const AValue: UInt64);
begin
  TsgData.SetAsHandle(ValueData, AValue);
end;

constructor TsgNodeSample.Create;
begin
  inherited Create;
  FPosition.X := -1;
  FPosition.Y := -1;
  FEntity := nil;
  FUserData := nil;
end;

procedure TsgNodeSample.Assign(const Obj: TObject);
begin
end;

function TsgNodeSample.GetAttributeByName(const AName: string): TsgNodeSample;
begin
  Result := nil;
end;

function TsgNodeSample.GetAttributeByTagID(const ATag: TsgTagID): TsgNodeSample;
begin
  Result := GetAttributeByName(cnstKeyWordsStr[ATag]);
end;

function TsgNodeSample.GetData(const AReader, AOwner: Pointer): Pointer;
begin
  SetReader(AReader);
  Result := GetDataInternal(AOwner);
end;

function TsgNodeSample.HasAttribute(const AName: string): Boolean;
begin
  Result := GetAttributeByName(AName) <> nil;
end;

function TsgNodeSample.HasAttributeNodes: Boolean;
begin
  Result := GetAttributeNodesCount > 0;
end;

function TsgNodeSample.HasChildNodes: Boolean;
begin
  Result := GetChildNodeCount > 0;
end;

function TsgNodeSample.HasPosition: Boolean;
begin
  Result := (FPosition.X > -1) and (FPosition.Y > -1);
end;

function TsgNodeSample.IsEqual(const ANode: TsgNodeSample;
  const ACompareChilds: Boolean): Boolean;
begin
  Result := Compare(ANode, ACompareChilds) = 0;
end;

{ TsgNodeComment }

constructor TsgNodeComment.Create;
begin
  inherited Create;
  FValue := TsgData.Create;
end;

destructor TsgNodeComment.Destroy;
begin
  FreeAndNil(FValue);
  inherited Destroy;
end;

function TsgNodeComment.GetNodeType: TsgNodeType;
begin
  Result := ntComment;
end;

function TsgNodeComment.GetValueData: TsgData;
begin
  Result := FValue;
end;

procedure TsgNodeComment.Read(const AParser: TsgParser);
begin
  AParser.BufferNext;
  FValue.ValueAsText := AParser.BufferValueToStr(cnstCommentEnd);
  if Length(FValue.ValueAsStr) = 0 then
    FValue.ValueAsText := AParser.BufferValueToChar('>');
end;

{$IFDEF SG_EXPORT_WITH_STRINGS}

procedure TsgNodeComment.SaveToStrings(const AStrings: TStrings);
var
  vComment: string;
begin
  if NodeType <> ntComment then
    inherited SaveToStrings(AStrings)
  else
  begin
    vComment := cnstCommentBegin + ValueAsText + cnstCommentEnd + sLineBreak;
    AStrings.AddObject(vComment, Self);
  end;
end;
{$ELSE}

procedure TsgNodeComment.SaveToStream(const AStream: TStream);
var
  vComment: string;
begin
  if NodeType <> ntComment then
    inherited SaveToStream(AStream)
  else
  begin
    vComment := cnstCommentBegin + ValueAsText + cnstCommentEnd + sLineBreak;
    SaveString(AStream, vComment);
  end;
end;
{$ENDIF}

procedure TsgNodeComment.Assign(const Obj: TObject);
begin
  inherited Assign(Obj);
  if Obj is TsgNodeComment then
    FValue.Assign(TsgNodeComment(Obj).FValue);
end;

{ TsgNodeAttrib }

{$IFDEF SG_USE_DICTIONARY_BY_NAME}

function TsgNodeAttrib.GetFName: string;
begin
  Result := GetNameByIndex(FNameIndex);
end;
{$ENDIF}

function TsgNodeAttrib.GetName: string;
begin
  Result := FName;
end;

function TsgNodeAttrib.GetNodeType: TsgNodeType;
begin
  Result := ntAttribute;
end;

function TsgNodeAttrib.GetPrefix: string;
begin
  Result := FPrefix;
end;

procedure TsgNodeAttrib.Read(const AParser: TsgParser);
var
  vFindEqual: Boolean;
begin
  vFindEqual := False;
  while AParser.CheckState do
  begin
    if not IsSpace(AParser.BufferValue) then
    begin
      if AParser.BufferValue = '=' then
        vFindEqual := True;
      Break;
    end;
    AParser.BufferNext;
  end;
  if vFindEqual then
  begin
    AParser.BufferNext;
    while AParser.CheckState do
    begin
      if not IsSpace(AParser.BufferValue) then
      begin
        if IsApostrophe(AParser.BufferValue) then
        begin
          FValue.ValueAsText := AParser.ReadValue;
          AParser.BufferNext;
        end;
        Break;
      end;
      AParser.BufferNext;
    end;
  end;
end;

{$IFDEF SG_USE_DICTIONARY_BY_NAME}

procedure TsgNodeAttrib.SetFName(const AValue: string);
begin
  FNameIndex := GetNameIndex(AValue);
end;
{$ENDIF}

procedure TsgNodeAttrib.SetName(const AValue: string);
begin
  FName := AValue;
end;

procedure TsgNodeAttrib.SetPrefix(const AValue: string);
begin
  FPrefix := AValue;
end;

procedure TsgNodeAttrib.Assign(const Obj: TObject);
begin
  inherited Assign(Obj);
  if Obj is TsgNodeAttrib then
  begin
{$IFNDEF SG_USE_DICTIONARY_BY_NAME}
    FName := TsgNodeAttrib(Obj).FName;
{$ELSE}
    FNameIndex := TsgNodeAttrib(Obj).FNameIndex;
{$ENDIF}
    FPrefix := TsgNodeAttrib(Obj).FPrefix;
  end;
end;

{ TsgNode }

function TsgNode.GetAttribNode(const AIndex: Integer): TsgNodeSample;
begin
  Result := FAttributes.List[AIndex];
end;

function TsgNode.GetAttributeNodesCount: Integer;
begin
  if FAttributes <> nil then
    Result := FAttributes.Count
  else
    Result := 0;
end;

function TsgNode.GetChildByName(const AName: string): TsgNodeSample;
begin
  Result := nil;
  if FChilds <> nil then
    Result := FChilds.NodeByName[AName];
end;

function TsgNode.GetChildNode(const AIndex: Integer): TsgNodeSample;
begin
  Result := FChilds.Nodes[AIndex];
end;

function TsgNode.GetChildNodeCount: Integer;
begin
  if FChilds <> nil then
    Result := FChilds.Count
  else
    Result := 0;
end;

function TsgNode.GetClosed: Boolean;
begin
  Result := FFlags and 1 <> 0;
end;

function TsgNode.GetNodeType: TsgNodeType;
begin
  Result := FType;
end;

function TsgNode.GetOwnerList(const AType: TsgNodeType): TsgNodeList;
begin
  Result := nil;
  case AType of
    ntAttribute:
      Result := FAttributes;
    ntElement:
      Result := FChilds;
  end;
end;

function TsgNode.GetPostamble: Boolean;
begin
  Result := FFlags and 2 <> 0;
end;

function TsgNode.GetSaveValueStr: string;
begin
  Result := inherited GetSaveValueStr;
  if Length(Result) > 0 then
    Result := ' ' + cnstNodeValue + Result;
end;

function TsgNode.GetTextData: TsgData;
begin
  if not Assigned(FText) then
    FText := TsgData.Create;
  Result := FText;
end;

procedure TsgNode.Read(const AParser: TsgParser);
var
  vStage: TsgParserStage;
  vNode: TsgNodeSample;
begin
  inherited Read(AParser);
  vStage := psEnd;
  while AParser.CheckState do
  begin
    case vStage of
      psEnd:
        begin
          case AParser.BufferValue of
            '/':
              begin
                if Length(Name) > 0 then
                  Closed := True
                else
                  Postamble := True;
                AParser.BufferNext;
              end;
            '>':
              begin
                AParser.BufferNext;
                if Closed or Postamble then
                  Break
                else
                  vStage := psText;
              end;
          else
            if IsLetter(AParser.BufferValue) then
              vStage := psAttrib
            else
              AParser.BufferNext;
          end;
        end;
      psText:
        begin
          if not(Closed or Postamble) then
            ReadTextAndChildNodes(AParser);
          Break;
        end;
      psAttrib:
        begin
          if IsLetter(AParser.BufferValue) then
          begin
            vNode := AParser.ReadAttrib;
            try
              AddAttrib(vNode);
              vNode.Read(AParser);
              vStage := psEnd;
            finally
              if (vNode.FullName = cnstNodeValue) and RemoveAttrib(vNode) then
              begin
                ValueData.Assign(vNode.ValueData);
                vNode.Free;
              end;
            end;
          end;
        end;
{$IFDEF SG_DEBUG}
    else
      ShowMessage(sNodeStageUndefined)
{$ENDIF}
    end;
  end;
end;

procedure TsgNode.ReadTextAndChildNodes(const AParser: TsgParser);
var
  vNode: TsgNodeSample;
  vTextValue: string;
begin
  while AParser.CheckState do
  begin
    vTextValue := ReplaceWebCodeEx(AParser.ReadText);
    if Length(vTextValue) > 0 then
      TextData.ValueAsStr := TextData.ValueAsStr + vTextValue;
    if AParser.CheckState then
    begin
      vNode := AParser.ReadNode;
      if vNode <> nil then
      begin
        vNode.Read(AParser);
        if vNode.Postamble then
        begin
          Closed := True;
          FreeAndNil(vNode);
          Break;
        end
        else
        begin
          AddChild(vNode);
          CheckText(vNode);
        end;
      end
      else
        Break;
    end
    else
      Break;
  end;
end;

function TsgNode.RemoveAttrib(const ANode: TsgNodeSample): Boolean;
begin
  Result := RemoveNode(FAttributes, ANode);
end;

function TsgNode.RemoveChild(const ANode: TsgNodeSample): Boolean;
begin
  Result := RemoveNode(FChilds, ANode);
end;

function TsgNode.RemoveAttributeByName(const AName: string;
  const ADoFree: Boolean): Boolean;
begin
  Result := RemoveByName(FAttributes, AName, ADoFree);
end;

function TsgNode.RemoveByName(const ANodes: TsgNodeList; const AName: string;
  const ADoFree: Boolean): Boolean;
var
  vNode: TsgNodeSample;
begin
  Result := False;
  if Assigned(ANodes) then
  begin
    vNode := ANodes.NodeByName[AName];
    if Assigned(vNode) then
    begin
      Result := RemoveNode(ANodes, vNode);
      if Result and ADoFree then
        vNode.Free;
    end;
  end;
end;

function TsgNode.RemoveNode(const ANodes: TsgNodeList;
  const ANode: TsgNodeSample): Boolean;
begin
  Result := False;
  if ANode.FOwner = Self then
  begin
    ANode.FOwner := nil;
    if Assigned(ANodes) then
    begin
      if ANodes.Remove(ANode) > -1 then
        Result := True;
    end;
  end;
end;

function TsgNode.RemoveChildByName(const AName: string;
  const ADoFree: Boolean): Boolean;
begin
  Result := RemoveByName(FChilds, AName, ADoFree);
end;

procedure TsgNode.SetClosed(const AValue: Boolean);
begin
  FFlags := (FFlags and 254) or Byte(AValue);
end;

procedure TsgNode.SetNodeType(const AValue: TsgNodeType);
begin
  FType := AValue;
end;

procedure TsgNode.SetPostamble(const AValue: Boolean);
begin
  FFlags := (FFlags and 253) or (Byte(AValue) shl 1);
end;

procedure TsgNode.Clear;
begin
  inherited Clear;
  if FAttributes <> nil then
    FAttributes.ClearNodes;
  if FChilds <> nil then
    FChilds.ClearNodes;
end;

constructor TsgNode.Create;
begin
  inherited Create;
  // FText := TsgData.Create;//create in get metod
  FType := ntElement;
end;

destructor TsgNode.Destroy;
begin
  FreeAndNil(FText);
  if FAttributes <> nil then
    FAttributes.Free;
  if FChilds <> nil then
    FChilds.Free;
  inherited Destroy;
end;

{$IFDEF SG_PARSER_USE_RTTI}

procedure TsgNode.LoadFromObject(const AObj: TObject; AClass: TClass = nil);
var
  I, J: Integer;
  vTypeData: PTypeData;
  vPropList: TPropList;
  vPPropInfo: PPropInfo;
  vClasses: TList;
  vFloatValue: Double;
  vIntValue: Integer;
  vFPointValue: TFPoint;
  vRecordName: string;
begin
  if AClass = nil then
    AClass := AObj.ClassType;
  vTypeData := GetTypeData(AClass.ClassInfo);
  GetPropInfos(AClass.ClassInfo, @vPropList);
  for I := 0 to vTypeData.PropCount - 1 do
  begin
    vPPropInfo := vPropList[I];
    if not Assigned(vPPropInfo) then
      Continue;
    case vPPropInfo^.PropType^.Kind of
      tkFloat:
        begin
          vFloatValue := GetPropValue(Self, vPPropInfo);
          AddAttribNV(vPPropInfo^.Name).ValueAsDouble := vFloatValue;
        end;
      tkInteger:
        begin
          vIntValue := GetPropValue(Self, vPPropInfo);
          AddAttribNV(vPPropInfo^.Name).ValueAsInt := vIntValue;
        end;
      tkRecord:
        begin
          case GetRecordType(vPPropInfo) of
            tkrTFPoint:
              begin
                vFPointValue := GetPropertyAsFPoint(Self, vPPropInfo);
                AddAttribNV(vPPropInfo^.Name).ValueAsFPoint := vFPointValue;
              end;
          end;
        end;
    end;
  end;
end;
{$ENDIF}
{$IFDEF SGDEL_2009}

function TsgNode.GetAttributesEnumerable: IEnumerable<TsgNodeSample>;
begin
  Result := TsgNodeEnumerator.Create(FAttributes);
end;

function TsgNode.GetChildsEnumerable: IEnumerable<TsgNodeSample>;
begin
  Result := TsgNodeEnumerator.Create(FChilds);
end;
{$ENDIF}

procedure TsgNode.AddAttrib(const ANode: TsgNodeSample);
begin
  if FAttributes = nil then
    FAttributes := TsgNodeList.CreateNodeList;
  FAttributes.Add(ANode);
  ANode.FOwner := Self;
end;

function TsgNode.AddAttribNV(const AFullName: string; const AValue: string = '')
  : TsgNodeAttrib;
begin
  Result := nil;
  if Length(AFullName) > 0 then
  begin
    Result := TsgNodeAttrib.Create;
    Result.FullName := AFullName;
    Result.ValueAsStr := AValue;
    AddAttrib(TsgNode(Result));
  end;
end;

procedure TsgNode.AddChild(const ANode: TsgNodeSample);
begin
  if FChilds = nil then
    FChilds := TsgNodeList.CreateNodeList;
  FChilds.Add(ANode);
  ANode.FOwner := Self;
end;

function TsgNode.AddChildNV(const AFullName: string;
  const AValue: string = ''): TsgNode;
begin
  Result := nil;
  if Length(AFullName) > 0 then
  begin
    Result := TsgNode.Create;
    Result.FullName := AFullName;
    if Length(AValue) > 0 then
      Result.ValueAsStr := AValue;
    AddChild(Result);
  end;
end;

function TsgNode.AddAttribs(const AAttribs: TStrings): Integer;
var
  I: Integer;
begin
  if Assigned(AAttribs) and (AAttribs.Count > 0) then
  begin
    Result := AttributeNodesCount;
    try
      for I := 0 to AAttribs.Count - 1 do
        AddAttribNV(AAttribs.Names[I], AAttribs.ValueFromIndex[I]);
    finally
      Result := AttributeNodesCount - Result;
    end;
  end
  else
    Result := 0;
end;

procedure TsgNode.Assign(const Obj: TObject);
begin
  inherited Assign(Obj);
  if Obj is TsgNode then
  begin
    FType := TsgNode(Obj).FType;
    FreeAndNil(FText);
    if Assigned(TsgNode(Obj).FText) then
      TextData.Assign(TsgNode(Obj).FText);
    AssignObjLists(Self, TsgNode(Obj).FAttributes, FAttributes);
    AssignObjLists(Self, TsgNode(Obj).FChilds, FChilds);
  end;
end;

function TsgNode.GetAttributeByName(const AName: string): TsgNodeSample;
begin
  if FAttributes = nil then
    Result := nil
  else
    Result := FAttributes.NodeByName[AName];
end;

function TsgNode.GetNodeByAttribValue(const ANodeName, AName, AValue: string;
  const ACaseSensitivity: Boolean = True): TsgNodeSample;
var
  I: Integer;
  vNode: TsgNodeSample;
  vAttribute: TsgNodeSample;
begin
  Result := nil;
  for I := 0 to ChildNodesCount - 1 do
  begin
    vNode := ChildNodes[I];
    if (Length(ANodeName) = 0) or
      (AnsiCmprStr(ANodeName, vNode.Name, FChilds.CaseSensitivity) = 0) then
    begin
      if vNode.HasAttributeNodes then
      begin
        vAttribute := vNode.GetAttributeByName(AName);
        if AnsiCmprStr(vAttribute.ValueAsStr, AValue, ACaseSensitivity) = 0 then
        begin
          Result := vNode;
          Break;
        end;
      end;
    end;
  end;
end;

{ TsgNodeCDATA }

function TsgNodeCDATA.GetNodeType: TsgNodeType;
begin
  Result := ntCData;
end;

procedure TsgNodeCDATA.Read(const AParser: TsgParser);
var
  vValue: string;
begin
  vValue := '';
  while AParser.BufferCheckPos and (AParser.BufferValue <> cnstCDATABegin) do
    AParser.BufferNext;
  AParser.BufferNext;
  while AParser.BufferCheckPos do
  begin
    if AParser.BufferValue = cnstCDATAEnd then
    begin
      if (AParser.FBufferIndex + 1 < AParser.FBufferSize - 1) or
        (AParser.FBuffer[AParser.FBufferIndex + 1] = cnstCDATAEnd) then
        Break;
    end;
    vValue := vValue + AParser.BufferValue;
    AParser.BufferNext;
  end;
  FValue.ValueAsText := vValue;
end;

{ TsgNodeDocument }

function TsgNodeDocument.GetClosed: Boolean;
begin
  Result := True;
end;

function TsgNodeDocument.GetNodeType: TsgNodeType;
begin
  Result := ntDocument;
end;

{$IFDEF SG_EXPORT_WITH_STRINGS}

procedure TsgNodeDocument.SaveToStrings(const AStrings: TStrings);
var
  I: Integer;
  vName, vTmp: string;
  vAttribute: TsgNodeSample;
begin
  vName := Name;
  if Length(vName) > 0 then
  begin
    if vName[1] <> '?' then
      vName := '?' + vName;
    vTmp := GetSpacesBySave + '<' + vName;
    for I := 0 to GetAttributeNodesCount - 1 do
    begin
      vAttribute := AttributeNodes[I];
      vTmp := vTmp + ' ' + vAttribute.Name + '="' + vAttribute.ValueAsStr + '"';
    end;
    vTmp := vTmp + '?>' + sLineBreak;
    AStrings.AddObject(vTmp, Self);
  end;
end;
{$ELSE}

procedure TsgNodeDocument.SaveToStream(const AStream: TStream);
var
  I: Integer;
  vName, vTmp: string;
  vAttribute: TsgNodeSample;
begin
  vName := Name;
  if Length(vName) > 0 then
  begin
    if vName[1] <> '?' then
      vName := '?' + vName;
    vTmp := GetSpacesBySave + '<' + vName;
    for I := 0 to GetAttributeNodesCount - 1 do
    begin
      vAttribute := AttributeNodes[I];
      vTmp := vTmp + ' ' + vAttribute.Name + '="' + vAttribute.ValueAsStr + '"';
    end;
    vTmp := vTmp + '?>' + sLineBreak;
    SaveString(AStream, vTmp);
  end;
end;
{$ENDIF}
{ TsgNodeDocType }

function TsgNodeDocType.GetName: string;
begin
  Result := cnstDOCTYPE;
end;

function TsgNodeDocType.GetNodeType: TsgNodeType;
begin
  Result := ntDocType;
end;

procedure TsgNodeDocType.Read(const AParser: TsgParser);
var
  vValue: string;
  vReadValue: Integer;
  vChar: Char;
begin
  vValue := '';
  vReadValue := 0;
  while AParser.FBufferIndex < AParser.FBufferSize do
  begin
    vChar := AParser.BufferValue;
    AParser.BufferNext;
    case vChar of
      '>':
        begin
          if vReadValue = 0 then
            Break
          else
            vValue := vValue + vChar;
        end;
      '[':
        Inc(vReadValue);
      ']':
        Dec(vReadValue);
    else
      if vReadValue > 0 then
        vValue := vValue + vChar;
    end;
  end;
  FreeValues;
  if Length(vValue) > 0 then
  begin
    FValue.ValueAsText := vValue;
    ReadValues(AParser);
  end;
end;

procedure TsgNodeDocType.ReadValues(const AParser: TsgParser);
var
  vParser: TsgParser;
  vStream: TMemoryStream;
  vValueStr: string;
{$IFDEF SGDEL_2009}
  vBom: TBytes;
{$ENDIF}
begin
  vParser := TsgParser.Create;
  try
    vParser.Assign(AParser);
    vStream := TMemoryStream.Create;
    try
      vValueStr := FValue.ValueAsStr;
      TsgMemoryStream(vStream).Capacity := Length(vValueStr) * SizeOf(Char) + 2;
{$IFDEF SGDEL_2009}
      vBom := TEncoding.Unicode.GetPreamble;
      vStream.Write(vBom[0], Length(vBom));
{$ENDIF}
      vStream.Write(vValueStr[1], Length(vValueStr) * SizeOf(Char));
      vValueStr := '';
      vStream.Seek(0, soFromBeginning);
      try
        vParser.LoadFromStream(vStream);
      except
      end;
    finally
      vStream.Free;
    end;
    if vParser.ROOT.Count > 0 then
    begin
      FValues := TsgNodeList.CreateNodeList;
      FValues.QwickFind := False;
      CopyLists(FValues, vParser.ROOT);
      vParser.ROOT.Count := 0;
    end;
  finally
    vParser.Free;
  end;
end;

procedure TsgNodeDocType.SetName(const AValue: string);
begin
end;

destructor TsgNodeDocType.Destroy;
begin
  FreeValues;
  inherited Destroy;
end;

procedure TsgNodeDocType.FreeValues;
begin
  if FValues <> nil then
  begin
    FValues.ClearNodes;
    FreeAndNil(FValues);
  end;
end;

procedure TsgNodeDocType.Assign(const Obj: TObject);
begin
  inherited Assign(Obj);
  if Obj is TsgNodeDocType then
    AssignObjLists(Self, TsgNodeDocType(Obj).FValues, FValues);
end;

{ TsgNodeEntity }

function TsgNodeEntity.GetName: string;
begin
  Result := FName;
  if Length(Result) = 0 then
    Result := cnstENTITY;
end;

function TsgNodeEntity.GetNodeType: TsgNodeType;
begin
  Result := ntEntity;
end;

procedure TsgNodeEntity.Read(const AParser: TsgParser);
var
  vName, vValue: string;
  vValueKey: Char;
begin
  vName := ReadNodeName(AParser);
  while AParser.CheckState and (IsSpace(AParser.BufferValue) or
    (AParser.BufferValue = '>')) do
    AParser.BufferNext;
  if (Length(vName) > 0) and AParser.CheckState and
    IsApostrophe(AParser.BufferValue) then
  begin
    vValueKey := AParser.BufferValue; // this value from IsValueKey
    vValue := '';
    AParser.BufferNext;
    while AParser.CheckState and (not(AParser.BufferValue = vValueKey) or
      (AParser.BufferValue = '>')) do
    begin
      vValue := vValue + AParser.BufferValue;
      AParser.BufferNext;
    end;
    if Length(vValue) > 0 then
    begin
      Name := vName;
      FValue.ValueAsText := vValue;
    end;
  end;
end;

function TsgNodeEntity.ReadNodeName(const AParser: TsgParser): string;
begin
  Result := '';
  while AParser.CheckState do
  begin
    if IsLetter(AParser.BufferValue) then
      Result := Result + AParser.BufferValue
    else
    begin
      if Length(Result) > 0 then
      begin
        if IsName(AParser.BufferValue) then
          Result := Result + AParser.BufferValue
        else
          Break;
      end;
    end;
    AParser.BufferNext;
  end;
end;

procedure TsgNodeEntity.SetName(const AValue: string);
begin
  if UpperCase(AValue) = cnstENTITY then
    FName := ''
  else
    FName := AValue;
end;

{ TsgNodeHTML }

function TsgNodeHTML.GetClassID: Cardinal;
begin
  Result := cnstClassID_HTML;
end;

{ TsgParser }

procedure TsgParser.BeginUpdateProgress;
begin
  FBufferReaded := 0;
  FBufferStep := cnstPercentUpdateProgress * FBufferSize div 100;
  if Assigned(FOnProgress) then
    FOnProgress(psStarting, 0, 100);
end;

procedure TsgParser.EndUpdateProgress;
begin
  if Assigned(FOnProgress) then
    FOnProgress(psEnding, 100, 100);
end;

procedure TsgParser.UpdateProgress;
var
  vBufferReaded: Integer;
begin
  vBufferReaded := FBufferIndex - FBufferReaded;
  if vBufferReaded > FBufferStep then
  begin
    FBufferReaded := FBufferIndex;
    if Assigned(FOnProgress) then
      FOnProgress(psRunning, FBufferReaded, FBufferSize);
  end;
end;

function TsgParser.AddDeclaration(const AVersion: string = '1.0';
  AEncoding: string = ''): Boolean;
var
  vNode: TsgNodeDocument;
begin
  Result := False;
  if (FROOT.Count = 0) or (not(FROOT.Nodes[0] is TsgNodeDocument)) or
    (FROOT.Nodes[0].Name <> 'xml') then
  begin
    vNode := TsgNodeDocument.Create;
    vNode.Name := 'xml';
    FROOT.Insert(0, vNode);
    vNode.AddAttribNV(cnstVersion, AVersion);
    if Length(AEncoding) = 0 then
      AEncoding := 'UTF-8';
    vNode.AddAttribNV(cnstEncoding, AEncoding);
    Result := True;
  end;
end;

procedure TsgParser.AfterLoadFromStream;
begin
end;

procedure TsgParser.Analize;
var
  vReload, vCheckedEntities, vCheckedEncoding: Boolean;
  vNode: TsgNodeSample;
begin
  LoadDebugInfo;
  vCheckedEntities := False;
  vCheckedEncoding := False;
  repeat
    vReload := False;
    while CheckState do
    begin
      vNode := ReadNode;
      if vNode <> nil then
      begin
        vNode.Read(Self);
        case vNode.NodeType of
          ntDocType:
            begin
              if (not vCheckedEntities) and (UpperCase(vNode.Name) = cnstDOCTYPE)
              then
              begin
                vCheckedEntities := True;
                vReload := CheckEntities(TsgNodeDocType(vNode));
                if vReload then
                  Break;
              end
            end;
          ntDocument:
            begin
              if (not vCheckedEncoding) and (AnsiLowerCase(vNode.Name) = cnstXML)
              then
              begin
                vCheckedEncoding := True;
                vReload := CheckEncoding(TsgNodeDocument(vNode));
                if vReload then
                  Break;
              end;
            end;
        end;
        FROOT.Add(vNode);
      end
      else
        Break;
    end;
  until not vReload;
end;

procedure TsgParser.BeforeLoadFromStream;
begin
end;

procedure TsgParser.BeginAnalize;
begin
  FBufferIndex := 1;
  FBufferSize := Length(FBuffer);
  FROOT.ClearNodes;
  BeginUpdateProgress;
end;

function TsgParser.BufferCheckPos: Boolean;
begin
  Result := FBufferIndex <= FBufferSize;
end;

procedure TsgParser.BufferNext;
begin
  Inc(FBufferIndex);
end;

function TsgParser.BufferValue: Char;
begin
  Result := FBuffer[FBufferIndex];
end;

function TsgParser.BufferValueToChar(const AChar: Char): string;
var
  vIndex: Integer;
begin
  Result := '';
  vIndex := StringScan(AChar, FBuffer, FBufferIndex);
  if vIndex > -1 then
  begin
    Result := Copy(FBuffer, FBufferIndex, vIndex - FBufferIndex);
    FBufferIndex := vIndex;
  end;
end;

function TsgParser.BufferValueToStr(const AStr: string): string;
var
  vIndex: Integer;
begin
  Result := '';
  vIndex := StringPos(AStr, FBuffer, FBufferIndex);
  if vIndex > -1 then
  begin
    Result := Copy(FBuffer, FBufferIndex, vIndex - FBufferIndex);
    FBufferIndex := vIndex;
  end;
end;

function TsgParser.CheckEncoding(var ADocument: TsgNodeDocument): Boolean;
{$IFDEF SGDEL_2009}
var
  vAttrib: TsgNodeSample;
  vCodePage: Integer;
  vStrStream: TStringStream;
  vEnc: TEncoding;
{$ENDIF}
begin
  Result := False;
{$IFDEF SGDEL_2009}
  if (FSource = nil) or (not ADocument.HasAttributeNodes) then
    Exit;
  vCodePage := cnstDefCodePage;
  vAttrib := ADocument.GetAttributeByName(cnstEncoding);
  if (vAttrib <> nil) and GetCodePageByName(vAttrib.ValueAsStr, vCodePage) then
  begin
    Result := vCodePage <> cnstDefCodePage;
    if Result then
    begin
      vEnc := TEncoding.GetEncoding(vCodePage);
      vStrStream := TStringStream.Create('', vEnc, False);
      try
        vStrStream.LoadFromStream(FSource);
        FBuffer := vStrStream.DataString;
        FBufferSize := Length(FBuffer);
        FreeAndNil(ADocument);
        BeginAnalize;
      finally
        vStrStream.Free;
        if not TEncoding.IsStandardEncoding(vEnc) then
          vEnc.Free;
      end;
    end;
  end;
{$ENDIF}
end;

function TsgParser.CheckEntities(var ADocType: TsgNodeDocType): Boolean;
var
  I: Integer;
  vNode: TsgNodeSample;
begin
  Result := False;
  if ADocType.Values <> nil then
  begin
    for I := 0 to ADocType.Values.Count - 1 do
    begin
      vNode := ADocType.Values.Nodes[I];
      if vNode.NodeType = ntEntity then
      begin
        if StringReplace(FBuffer, '&' + vNode.Name + ';', vNode.ValueAsStr) then
          Result := True;
      end;
    end;
    if Result then
    begin
      FreeAndNil(ADocType);
      BeginAnalize;
    end;
  end;
end;

function TsgParser.CheckState: Boolean;
begin
  Result := (FBufferIndex <= FBufferSize) and (not Stopped);
end;

procedure TsgParser.Clear;
begin
  FROOT.ClearNodes;
  FROOT.Clear;
end;

procedure TsgParser.ClearBuffer;
begin
  FBuffer := '';
  FBufferIndex := 1;
  FBufferSize := 0;
end;

function TsgParser.CreateNodeSample(const AFullName: string; AClass: TClass)
  : TsgNodeSample;
var
  vIndex: Integer;
  vName: string;
begin
  vName := GetNameFromFullName(AFullName);
  vIndex := FRegisterNodes.IndexOf(vName);
  if vIndex > -1 then
    AClass := TClass(FRegisterNodes.Objects[vIndex]);
  Result := TsgClassOfNode(AClass).Create;
  Result.FullName := AFullName;
end;

class function TsgParser.FindNodeByAttrib(const AAttribName,
  AAttribValue: string; const AList: TsgNodeList; AFoundNodes: TList = nil;
  const AIgnoreValue: Boolean = False; const AFindCount: Integer = -1)
  : TsgNodeSample;
var
  vIterator: TsgNodeIterator;
begin
  Result := nil;
  if AList <> nil then
  begin
    vIterator := TsgNodeIterator.Create(AList,
      TsgFindByAttribute.Create(AAttribName, AAttribValue, AIgnoreValue));
    try
      vIterator.FindNotAll := not Assigned(AFoundNodes);
      if vIterator.Find(AFindCount) > 0 then
      begin
        Result := vIterator.FindResult.First;
        if Assigned(AFoundNodes) then
          CopyLists(AFoundNodes, vIterator.FindResult);
      end;
    finally
      vIterator.Free;
    end;
  end;
end;

function TsgParser.FindEqualNode(const ABase: TsgNodeSample;
  const ANode: TsgNodeSample; ANodeCompareFunc: TsgNodeCompareFunc = nil;
  const ATree: TList = nil): TsgNodeSample;
var
  I: Integer;
  vNodeCompareFunc: TsgNodeCompareFunc;
begin
  Result := nil;
  if (ANode = nil) and (not Assigned(ANodeCompareFunc)) then
    Exit;
  if ANode <> nil then
    vNodeCompareFunc := ANode.Compare
  else
    vNodeCompareFunc := ANodeCompareFunc;
  if Assigned(ABase) then
  begin
    if vNodeCompareFunc(ABase) = 0 then
      Result := ABase
    else
    begin
      if Assigned(ATree) then
        ATree.Add(ABase);
      for I := 0 to ABase.GetChildNodeCount - 1 do
      begin
        Result := FindEqualNode(ABase.GetChildNode(I), ANode,
          ANodeCompareFunc, ATree);
        if Assigned(Result) then
          Break;
      end;
      if Assigned(ATree) and (not Assigned(Result)) then
        ATree.Count := ATree.Count - 1;
    end;
  end
  else
  begin
    for I := 0 to ROOT.Count - 1 do
    begin
      Result := FindEqualNode(ROOT.Nodes[I], ANode, ANodeCompareFunc, ATree);
      if Assigned(Result) then
        Break;
    end;
  end;
end;

function TsgParser.FindNodeByAttribValue(const ANode: TsgNodeSample;
  const AAttribName, AAttribValue: string; const AFoundNodes: TList = nil;
  const AIgnoreValue: Boolean = False): TsgNodeSample;
var
  I: Integer;
  vAttrib: TsgNodeSample;
begin
  Result := nil;
  if ANode.HasAttributeNodes then
  begin
    vAttrib := ANode.GetAttributeByName(AAttribName);
    if (vAttrib <> nil) then
    begin
      if AIgnoreValue then
      begin
        if Assigned(AFoundNodes) then
          AFoundNodes.Add(ANode)
        else
          Result := ANode;
      end
      else
      begin
        if vAttrib.ValueAsStr = AAttribValue then
          Result := ANode;
      end;
    end;
  end;
  if (Result = nil) and ANode.HasChildNodes then
  begin
    for I := 0 to ANode.ChildNodesCount - 1 do
    begin
      Result := FindNodeByAttribValue(ANode.ChildNodes[I], AAttribName,
        AAttribValue, AFoundNodes, AIgnoreValue);
      if Result <> nil then
        if Assigned(AFoundNodes) then
          AFoundNodes.Add(Result)
        else
          Break;
    end;
  end;
end;

class function TsgParser.FindNodeByName(const ANodeName: string;
  const AList: TsgNodeList; AFoundNodes: TList = nil;
  const AFindCount: Integer = -1): TsgNodeSample;
var
  vIterator: TsgNodeIterator;
begin
  Result := nil;
  if AList <> nil then
  begin
    vIterator := TsgNodeIterator.Create(AList, TsgFindByNode.Create(ANodeName));
    try
      vIterator.FindNotAll := not Assigned(AFoundNodes);
      if vIterator.Find(AFindCount) > 0 then
      begin
        Result := vIterator.FindResult.First;
        if Assigned(AFoundNodes) then
          CopyLists(AFoundNodes, vIterator.FindResult);
      end;
    finally
      vIterator.Free;
    end;
  end;
end;

function TsgParser.GetDebugInfo: Boolean;
begin
  Result := Assigned(FDebugInfo);
end;

function TsgParser.GetXMLString: string;
begin
  Result := SaveToString;
end;

function TsgParser.InitFromStream: Boolean;
{$IFDEF SGDEL_2009}
var
  vBytes: TBytes;
  vBytesSize: Integer;
  vEncoding: TEncoding;
{$ENDIF}
begin
  Result := False;
  if FSource <> nil then
  begin
    FSource.Seek(0, soFromBeginning);
{$IFDEF SGDEL_2009}
    vEncoding := nil;
    try
      vBytesSize := FSource.Seek(0, soFromEnd);
      FSource.Seek(0, soFromBeginning);
      SetLength(vBytes, vBytesSize);
      FSource.Read(vBytes[0], vBytesSize);
      if TEncoding.GetBufferEncoding(vBytes, vEncoding) = 0 then
        vEncoding := TMBCSEncoding.Create(cnstDefCodePage)
        // vEncoding := TEncoding.GetEncoding(cnstDefCodePage)
      else
        FSource := nil;
      FBuffer := vEncoding.GetString(vBytes);
      FBufferSize := Length(FBuffer);
    finally
      SetLength(vBytes, 0);
      if (vEncoding <> nil) and (not TEncoding.IsStandardEncoding(vEncoding))
      then
        vEncoding.Free;
    end;
{$ELSE}
    FBufferSize := FSource.Seek(0, soFromEnd);
    FSource.Seek(0, soFromBeginning);
    SetLength(FBuffer, FBufferSize);
    FSource.Read(FBuffer[1], FBufferSize);
{$ENDIF}
    Result := FBufferSize > 0;
  end;
end;

function TsgParser.InitFromString: Boolean;
begin
  FBuffer := FSourceString;
  FBufferSize := Length(FBuffer);
  FSourceString := '';
  Result := FBufferSize > 0;
end;

{$IFDEF SGDEL_2009}

class function TsgParser.SaveEncoding;
begin
  Result := TEncoding.UTF8;
end;
{$ENDIF}

function TsgParser.ReadAttrib: TsgNodeSample; // for optimization
var
  vName: string;
begin
  Result := TsgNodeSample(TsgNodeAttrib.Create);
  vName := '';
  while (FBufferIndex <= FBufferSize) and IsName(FBuffer[FBufferIndex]) do
  begin
    vName := vName + FBuffer[FBufferIndex];
    BufferNext;
  end;
  Result.FullName := vName;
  SetNodePosition(Result);
end;

function TsgParser.ReadCData: string; // for optimization
begin
  Result := '';
  while (FBufferIndex <= FBufferSize) and
    (FBuffer[FBufferIndex] <> cnstCDATABegin) do
    BufferNext;
  BufferNext;
  while (FBufferIndex <= FBufferSize) and
    (FBuffer[FBufferIndex] <> cnstCDATAEnd) do
  begin
    Result := Result + FBuffer[FBufferIndex];
    BufferNext;
  end;
  BufferNext;
end;

function TsgParser.ReadComment: string; // for optimization
begin
  BufferNext;
  Result := BufferValueToChar('>');
end;

function TsgParser.ReadDocType: string;
begin
  Result := ReadComment;
end;

function TsgParser.ReadNode: TsgNodeSample;
var
  vName: string;
  vNodeType: TsgNodeType;
  vPostamble: Boolean;
begin
  Result := nil;
  while CheckState and (FBuffer[FBufferIndex] <> '<') do
    BufferNext;
  vName := ReadNodeName(vNodeType, vPostamble);
  case vNodeType of
    ntUndefined:
      begin
      end;
    ntComment:
      begin
        Result := TsgNode(TsgNodeComment.Create);
      end;
    ntDocType:
      begin
        if UpperCase(vName) = cnstDOCTYPE then
          Result := TsgNode(TsgNodeDocType.Create)
        else
        begin
          if UpperCase(vName) = cnstENTITY then
            Result := TsgNode(TsgNodeEntity.Create)
          else
          begin
            Result := TsgNode.Create;
            Result.SetNodeType(ntDocType);
            Result.FullName := vName;
          end;
        end;
      end;
    ntCData:
      begin
        Result := TsgNode(TsgNodeCDATA.Create);
      end;
    ntDocument:
      begin
        Result := TsgNode(TsgNodeDocument.Create);
        Result.FullName := vName;
      end;
  else
    if Length(vName) > 0 then
    begin
      Result := CreateNodeSample(vName, TsgNode);
      Result.SetNodeType(vNodeType);
      Result.Postamble := vPostamble;
    end;
  end;
  if Assigned(Result) then
    SetNodePosition(Result);
  UpdateProgress;
end;

function TsgParser.ReadNodeName(var ANodeType: TsgNodeType;
  var APostamble: Boolean): string;
begin
  Result := '';
  ANodeType := ntElement;
  APostamble := False;
  while CheckState do
  begin
    case FBuffer[FBufferIndex] of
      '/':
        APostamble := True;
      '?':
        ANodeType := ntDocument;
      '!':
        ANodeType := ntDocType;
      '[':
        ANodeType := ntCData;
      '-':
        begin
          if ANodeType = ntDocType then
          begin
            BufferNext;
            if CheckState and (FBuffer[FBufferIndex] = '-') then
            begin
              ANodeType := ntComment;
              Break;
            end;
            Dec(FBufferIndex);
          end;
          ANodeType := ntElement;
          Result := FBuffer[FBufferIndex];
          BufferNext;
          while CheckState and IsName(FBuffer[FBufferIndex]) do
          begin
            Result := Result + FBuffer[FBufferIndex];
            BufferNext;
          end;
          Break;
        end;
    else
      if IsLetter(FBuffer[FBufferIndex]) then
      begin
        Result := FBuffer[FBufferIndex];
        BufferNext;
        while CheckState and IsName(FBuffer[FBufferIndex]) do
        begin
          Result := Result + FBuffer[FBufferIndex];
          BufferNext;
        end;
        Break;
      end
    end;
    BufferNext;
  end;
end;

function TsgParser.ReadText: string; // for optimization
var
  vTmp: string;
begin
  vTmp := '';
  while (FBufferIndex <= FBufferSize) and (FBuffer[FBufferIndex] <> '<') do
  begin
    if FBuffer[FBufferIndex] > Char($D) then
      vTmp := vTmp + FBuffer[FBufferIndex];
    BufferNext;
  end;
  Result := vTmp;
end;

function TsgParser.ReadValue: string; // for optimization
var
  vValueKey: Char;
begin
  vValueKey := FBuffer[FBufferIndex];
  BufferNext;
  Result := BufferValueToChar(vValueKey);
end;

constructor TsgParser.Create;
begin
  inherited Create;
  FROOT := TsgNodeList.Create;
  FRegisterNodes := TsgStringList.Create;
  FRegisterNodes.Sorted := True;
  FRegisterNodes.CaseSensitive := False;
  FRegisterNodes.Duplicates := dupIgnore;
end;

function TsgParser.CreateRootNode(const AFullName: string = cnstXML;
  const ACreateDocument: Boolean = False): TsgNode;
var
  vDocument: TsgNodeDocument;
begin
  if ACreateDocument then
  begin
    vDocument := TsgNodeDocument.Create;
    vDocument.Name := cnstXML;
    ROOT.Add(vDocument);
    vDocument.AddAttribNV(cnstVersion).ValueAsStr := '1.0';
    vDocument.AddAttribNV(cnstEncoding).ValueAsStr := 'UTF-8';
    vDocument.AddAttribNV(cnstStandalone).ValueAsStr := 'no';
  end;
  Result := TsgNode.Create;
  Result.Name := AFullName;
  ROOT.Add(Result);
end;

destructor TsgParser.Destroy;
begin
  FreeAndNil(FDebugInfo);
  FRegisterNodes.Free;
  FROOT.Free;
  inherited Destroy;
end;

procedure TsgParser.Assign(const AParser: TsgParser);
begin
  FRegisterNodes.Clear;
  FRegisterNodes.Assign(AParser.FRegisterNodes);
end;

function TsgParser.FindNode(const AID: string; const AList: TsgNodeList)
  : TsgNodeSample;
begin
  Result := FindNodeByAttrib(cnstKeyWordsStr[idID], AID, AList);
end;

procedure TsgParser.LoadDebugInfo;
var
  I, vIndex, vLenLineBreak: Integer;
  vStrings: TsgStringList;
begin
  if not Assigned(FDebugInfo) then
    Exit;
  FDebugInfo.Count := 0;
  vStrings := TsgStringList.Create;
  try
    try
      vLenLineBreak := Length(vStrings.LineBreak);
      vStrings.Text := FBuffer;
      vIndex := 0;
      for I := 0 to vStrings.Count - 1 do
      begin
        Inc(vIndex, Length(vStrings[I]) + vLenLineBreak);
        FDebugInfo.Add(Pointer(vIndex));
      end;
    except
      FreeAndNil(FDebugInfo);
    end;
  finally
    vStrings.Free;
  end;
end;

function TsgParser.LoadFromBuffer(const AInitBuffer: TsgObjProcBool): Boolean;
begin
  Result := True;
  BeforeLoadFromStream;
  FROOT.ClearNodes;
  ClearBuffer;
  try
    if AInitBuffer then
    begin
      BeginUpdateProgress;
      try
        Analize;
      finally
        EndUpdateProgress;
      end;
    end;
    ClearBuffer;
    AfterLoadFromStream;
  except
    Result := False;
    ClearBuffer;
  end
end;

function TsgParser.LoadFromFile(const AFileName: string): Boolean;
var
  S: TFileStream;
  Dir: string;
begin
  Dir := GetCurrentDir;
  S := TFileStream.Create(AFileName, fmOpenRead or fmShareDenyNone);
  try
    SetCurrentDir(ExtractFilePath(AFileName));
    try
      try
        Result := LoadFromStream(S);
      except
        Result := False;
      end;
    finally
      S.Free;
      try
        SetCurrentDir(Dir);
      except
      end;
    end;
  except
    raise;
  end;
end;

function TsgParser.LoadFromStream(const S: TStream): Boolean;
begin
  FSource := S;
  try
    Result := LoadFromBuffer(InitFromStream);
  finally
    FSource := nil;
  end;
end;

function TsgParser.LoadFromString(const AString: string): Boolean;
begin
  FSourceString := AString;
  try
    Result := LoadFromBuffer(InitFromString);
  finally
    FSourceString := '';
  end;
end;

procedure TsgParser.RegisterClass(const AFullName: string;
  const AClass: TClass);
begin
  FRegisterNodes.AddObject(AFullName, TObject(AClass));
end;

function TsgParser.SaveToFile(const AFileName: string): Boolean;
var
  vFileStream: TFileStream;
begin
  vFileStream := TFileStream.Create(AFileName, fmCreate);
  try
    Result := SaveToStream(vFileStream);
  finally
    vFileStream.Free;
  end;
end;

function TsgParser.SaveToStream(const S: TStream): Boolean;
{$IFDEF SG_EXPORT_WITH_STRINGS}
var
  vBufferString: TStringList;
begin
  Result := False;
  vBufferString := TStringList.Create;
  try
    if SaveToStrings(vBufferString) then
    begin
      vBufferString.SaveToStream(S, SaveEncoding);
      Result := True;
    end;
  finally
    vBufferString.Free;
  end;
{$ELSE}

var
  I: Integer;
{$IFDEF SGDEL_2009}
  vBom: TBytes;
{$ENDIF}
begin
  Result := True;
  try
{$IFDEF SGDEL_2009}
    vBom := SaveEncoding.GetPreamble;
    S.Write(vBom[0], Length(vBom));
{$ENDIF}
    for I := 0 to FROOT.Count - 1 do
      FROOT.Nodes[I].SaveToStream(S);
  except
    Result := False;
  end;
{$ENDIF}
end;

function TsgParser.SaveToString: string;
var
  vBufferString: TStringList;
begin
  Result := '';
  vBufferString := TStringList.Create;
  try
    if SaveToStrings(vBufferString) then
      Result := vBufferString.Text;
  finally
    vBufferString.Free;
  end;
end;

function TsgParser.SaveToStrings(const AStrings: TStrings): Boolean;
{$IFDEF SG_EXPORT_WITH_STRINGS}
var
  I: Integer;
begin
  Result := True;
  try
    for I := 0 to FROOT.Count - 1 do
      FROOT.Nodes[I].SaveToStrings(AStrings);
  except
    Result := False;
  end;
{$ELSE}

var
  vStream: TMemoryStream;
begin
  Result := False;
  AStrings.Clear;
  vStream := TMemoryStream.Create;
  try
    if SaveToStream(vStream) then
    begin
      vStream.Position := 0;
      AStrings.LoadFromStream(vStream{$IFDEF SGDEL_2009}, SaveEncoding{$ENDIF});
      Result := True;
    end;
  finally
    vStream.Free;
  end;
{$ENDIF}
end;

procedure TsgParser.SetDebugInfo(const AValue: Boolean);
begin
  FreeAndNil(FDebugInfo);
  if AValue then
    FDebugInfo := TList.Create;
end;

procedure TsgParser.SetNodePosition(const ANode: TsgNodeSample);
var
  I, vPos1, vPos2: Integer;
  vPosition: TsgNodeSamplePosition;
begin
  if Assigned(FDebugInfo) and (FDebugInfo.Count > 0) then
  begin
    FillChar(vPosition, SizeOf(vPosition), 0);
    vPos1 := 0;
    for I := 0 to FDebugInfo.Count - 1 do
    begin
      vPos2 := Integer(FDebugInfo[I]);
      if vPos2 > FBufferIndex then
      begin
        vPosition.Y := I + 1;
        vPosition.X := FBufferIndex - vPos1;
        Dec(vPosition.X, Length(ANode.FullName));
        Break;
      end;
      vPos1 := vPos2;
    end;
    ANode.SetPosition(vPosition);
  end;
end;

procedure TsgParser.SetXMLString(const AValue: string);
begin
  LoadFromString(AValue);
end;

function TsgParser.Stopped: Boolean;
begin
  Result := Assigned(FStopped) and FStopped;
end;

procedure TsgParser.UnRegisterClass(const AFullName: string);
var
  vIndex: Integer;
begin
  vIndex := FRegisterNodes.IndexOf(AFullName);
  if vIndex > -1 then
    FRegisterNodes.Delete(vIndex);
end;

{$IFDEF SGDEL_2009}
{ TsgNodeEnumeratorBase }

constructor TsgNodeEnumeratorBase.Create(const ANodeList: TsgNodeList);
begin
  inherited Create;
  FList := ANodeList;
  FIndex := -1;
end;

function TsgNodeEnumeratorBase.GetCurrent: TObject;
begin
  Result := FList.List[FIndex];
end;

function TsgNodeEnumeratorBase.GetEnumerator: IEnumerator;
begin
  Result := Self;
end;

function TsgNodeEnumeratorBase.MoveNext: Boolean;
begin
  Result := (FList <> nil) and (FIndex < FList.Count - 1);
  if Result then
    Inc(FIndex);
end;

procedure TsgNodeEnumeratorBase.Reset;
begin
  FIndex := -1;
end;

{ TsgNodeEnumerator }

function TsgNodeEnumerator.GetCurrent: TsgNodeSample;
begin
  Result := FList.List[FIndex];
end;

function TsgNodeEnumerator.GetEnumerator: IEnumerator<TsgNodeSample>;
begin
  Result := Self;
end;

{$ENDIF}
{ TsgData }

procedure TsgData.ClearValue;
begin
{$IFDEF SG_XML_DATA_TYPE}
  case FValue.DType of
    dtString:
      begin
        FValue.AsString^ := '';
        Dispose(FValue.AsString);
      end;
    dtTF2DPoint:
      Dispose(FValue.AsF2DPoint);
    dtTFPoint:
      Dispose(FValue.AsFPoint);
    dtRect:
      Dispose(FValue.AsRect);
    dtTF2DRect:
      Dispose(FValue.AsF2DRect);
    dtTFRect:
      Dispose(FValue.AsFRect);
    dtVersion:
      Dispose(FValue.AsVersion);
    dtColorCAD:
      begin
        if FValue.AsColorCAD <> nil then
        begin
          FValue.AsColorCAD^.AlbumString := '';
          Dispose(FValue.AsColorCAD);
        end;
      end;
{$IFNDEF SG_DATA64}
    dtHandle:
      Dispose(FValue.AsHandle);
    dtInt64:
      Dispose(FValue.AsInt64);
    dtDouble:
      Dispose(FValue.AsDouble);
    dtPoint:
      Dispose(FValue.AsPoint);
{$ENDIF}
  end;
  FillChar(FValue, SizeOf(FValue), 0);
{$ELSE}
  FValue := '';
{$ENDIF}
end;

procedure TsgData.Assign(const AData: TsgData);
{$IFDEF SG_XML_DATA_TYPE}
var
  vValue: Pointer;
begin
  ClearValue;
  case AData.FValue.DType of
{$IFNDEF SG_DATA64}
    dtHandle, dtInt64, dtDouble, dtPoint,
{$ENDIF}
    dtString, dtTF2DPoint, dtTFPoint, dtRect, dtTF2DRect, dtTFRect, dtVersion,
      dtColorCAD:
      vValue := AData.FValue.Value;
  else
    vValue := @AData.FValue.Value;
  end;
  SetValueWithType(AData.FValue.DType, vValue);
{$ELSE}
begin
  FValue := AData.FValue;
{$ENDIF}
end;

function TsgData.Compare(const AData: TsgData;
  const ACaseSensitivity: Boolean = True): Integer;
begin
  if ACaseSensitivity then
    Result := CompareStr(ValueAsStr, AData.ValueAsStr)
  else
    Result := CompareText(ValueAsStr, AData.ValueAsStr);
end;

constructor TsgData.Create;
begin
  inherited Create;
end;

destructor TsgData.Destroy;
begin
  ClearValue;
  inherited Destroy;
end;

class procedure TsgData.DoError(const AMessage: string);
begin
  raise EConvertError.Create(AMessage);
end;

class function TsgData.GetAsBool(const AData: TsgData): Boolean;
begin
  if AData <> nil then
    Result := AData.ValueAsBool
  else
  begin
    Result := False;
    DoError('');
  end;
end;

class function TsgData.GetAsByte(const AData: TsgData): Byte;
begin
  if AData <> nil then
    Result := AData.ValueAsByte
  else
    Result := 0;
end;

class function TsgData.GetAsDouble(const AData: TsgData): Double;
begin
  if AData <> nil then
    Result := AData.ValueAsDouble
  else
    Result := 0;
end;

class function TsgData.GetAsF2DRect(const AData: TsgData): TF2DRect;
begin
  if AData <> nil then
    Result := AData.ValueAsF2DRect
  else
    Result := cnstF2DRectZero;
end;

class function TsgData.GetAsFPoint(const AData: TsgData): TFPoint;
begin
  if AData <> nil then
    Result := AData.ValueAsFPoint
  else
    Result := cnstFPointZero;
end;

class function TsgData.GetAsFRect(const AData: TsgData): TFRect;
begin
  if AData <> nil then
    Result := AData.ValueAsFRect
  else
    Result := cnstFRectZero;
end;

class function TsgData.GetAsHandle(const AData: TsgData): UInt64;
begin
  if AData <> nil then
    Result := AData.ValueAsHandle
  else
    Result := 0;
end;

class function TsgData.GetAsInt(const AData: TsgData): Integer;
begin
  if AData <> nil then
    Result := AData.ValueAsInt
  else
    Result := 0;
end;

class function TsgData.GetAsInt64(const AData: TsgData): Int64;
begin
  if AData <> nil then
    Result := AData.ValueAsInt64
  else
    Result := 0;
end;

class function TsgData.GetAsPointer(const AData: TsgData): Pointer;
begin
  if AData <> nil then
    Result := AData.ValueAsPointer
  else
    Result := nil;
end;

class function TsgData.GetAsStr(const AData: TsgData): string;
begin
  if AData <> nil then
    Result := AData.ValueAsStr
  else
    Result := '';
end;

class function TsgData.GetAsText(const AData: TsgData): string;
begin
  if AData <> nil then
    Result := AData.ValueAsText
  else
    Result := '';
end;

function TsgData.GetMeasurements: TsgUnitsOfMeasurements;
begin
  Result := umUndefined;
{$IFDEF SG_XML_DATA_TYPE}
  if FValue.DType = dtString then
    Result := GetUnitsMeasurements(GetValueAsString);
{$ELSE}
  if Length(FValue) > 0 then
    Result := GetUnitsMeasurements(FValue);
{$ENDIF}
end;

function TsgData.GetValueAsBool: Boolean;
begin
{$IFDEF SG_XML_DATA_TYPE}
  case Value.DType of
    dtBool:
      Result := Value.AsBool;
    dtString:
      begin
{$ENDIF}
        if not StrToBool(ValueAsStr, Result) then
          DoError(cnstIncorrectType);
{$IFDEF SG_XML_DATA_TYPE}
        SetValueAsBool(Result);
      end;
  else
    Result := False;
    DoError(cnstIncorrectType);
  end
{$ENDIF}
end;

function TsgData.GetValueAsByte: Byte;
begin
{$IFDEF SG_XML_DATA_TYPE}
  case Value.DType of
    dtByte:
      Result := Value.AsByte;
    dtString:
      begin
{$ENDIF}
        Result := GetValueAsInt;
{$IFDEF SG_XML_DATA_TYPE}
        if Measurements = umUndefined then
          SetValueAsByte(Result);
      end;
  else
    Result := 0;
    DoError(cnstIncorrectType);
  end
{$ENDIF}
end;

function TsgData.GetValueAsColor: TColor;
begin
{$IFDEF SG_XML_DATA_TYPE}
  case Value.DType of
    dtColor:
      Result := Value.AsColor;
    dtString:
      begin
{$ENDIF}
        Result := StrToColorHex(ValueAsStr);
{$IFDEF SG_XML_DATA_TYPE}
        SetValueAsColor(Result);
      end;
  else
    Result := 0;
    DoError(cnstIncorrectType);
  end
{$ENDIF}
end;

function TsgData.GetValueAsColorCAD: TsgColorCAD;
begin
{$IFDEF SG_XML_DATA_TYPE}
  case Value.DType of
    dtColorCAD:
      Result := Value.AsColorCAD^;
    dtString:
      begin
{$ENDIF}
        Result := StrToColorCAD(ValueAsStr);
{$IFDEF SG_XML_DATA_TYPE}
        SetValueAsColorCAD(Result);
      end;
  else
    DoError(cnstIncorrectType);
  end
{$ENDIF}
end;

function TsgData.GetValueAsDouble: Double;
var
  vStr: string;
begin
{$IFDEF SG_XML_DATA_TYPE}
  case Value.DType of
    dtDouble:
      Result := Value.AsDouble{$IFNDEF SG_DATA64}^{$ENDIF};
    dtString:
      begin
{$ENDIF}
        vStr := ValueAsStr;
        if Length(vStr) > 0 then
          Result := StrToVal(vStr)
        else
          Result := 0;
{$IFDEF SG_XML_DATA_TYPE}
        if Measurements = umUndefined then
          SetValueAsDouble(Result);
      end;
  else
    Result := 0;
    DoError(cnstIncorrectType);
  end
{$ENDIF}
end;

function TsgData.GetValueAsF2DRect: TF2DRect;
begin
{$IFDEF SG_XML_DATA_TYPE}
  case Value.DType of
    dtTF2DRect:
      Result := Value.AsF2DRect^;
    dtString:
      begin
{$ENDIF}
        Result := StrToF2DRect(ValueAsStr);
{$IFDEF SG_XML_DATA_TYPE}
        SetValueAsF2DRect(Result);
      end;
  else
    DoError(cnstIncorrectType);
  end
{$ENDIF}
end;

function TsgData.GetValueAsFPoint: TFPoint;
begin
{$IFDEF SG_XML_DATA_TYPE}
  case Value.DType of
    dtTFPoint:
      Result := Value.AsFPoint^;
    dtString:
      begin
{$ENDIF}
        Result := StrToFPoint(ValueAsStr);
{$IFDEF SG_XML_DATA_TYPE}
        SetValueAsFPoint(Result);
      end;
  else
    DoError(cnstIncorrectType);
  end
{$ENDIF}
end;

function TsgData.GetValueAsF2DPoint: TF2DPoint;
begin
{$IFDEF SG_XML_DATA_TYPE}
  case Value.DType of
    dtTF2DPoint:
      Result := Value.AsF2DPoint^;
    dtString:
      begin
{$ENDIF}
        Result := MakeF2DPointFrom3D(StrToFPoint(ValueAsStr));
{$IFDEF SG_XML_DATA_TYPE}
        SetValueAsF2DPoint(Result);
      end;
  else
    DoError(cnstIncorrectType);
  end
{$ENDIF}
end;

function TsgData.GetValueAsFRect: TFRect;
begin
{$IFDEF SG_XML_DATA_TYPE}
  case Value.DType of
    dtTFRect:
      Result := Value.AsFRect^;
    dtString:
      begin
{$ENDIF}
        Result := StrToFRect(ValueAsStr);
{$IFDEF SG_XML_DATA_TYPE}
        SetValueAsFRect(Result);
      end;
  else
    DoError(cnstIncorrectType);
  end
{$ENDIF}
end;

function TsgData.GetValueAsHandle: UInt64;
begin
{$IFDEF SG_XML_DATA_TYPE}
  case Value.DType of
    dtHandle:
      Result := Value.AsHandle{$IFNDEF SG_DATA64}^{$ENDIF};
    dtString:
      begin
{$ENDIF}
        Result := StrToHandle(ValueAsStr);
{$IFDEF SG_XML_DATA_TYPE}
        SetValueAsHandle(Result);
      end;
  else
    Result := 0;
    DoError(cnstIncorrectType);
  end
{$ENDIF}
end;

function TsgData.GetValueAsInt: Integer;
var
  vStr: string;
begin
{$IFDEF SG_XML_DATA_TYPE}
  case Value.DType of
    dtInteger:
      Result := Value.AsInteger;
    dtString:
      begin
{$ENDIF}
        vStr := ValueAsStr;
        if Length(vStr) > 0 then
          Result := StrToIntDef(vStr, 0)
        else
          Result := 0;
{$IFDEF SG_XML_DATA_TYPE}
        if Measurements = umUndefined then
          SetValueAsInt(Result);
      end;
  else
    Result := 0;
    DoError(cnstIncorrectType);
  end
{$ENDIF}
end;

function TsgData.GetValueAsInt64: Int64;
var
  vStr: string;
begin
{$IFDEF SG_XML_DATA_TYPE}
  case Value.DType of
    dtInt64:
      Result := Value.AsInt64{$IFNDEF SG_DATA64}^{$ENDIF};
    dtString:
      begin
{$ENDIF}
        vStr := ValueAsStr;
        if Length(vStr) > 0 then
          Result := StrToInt64Def(vStr, 0)
        else
          Result := 0;
{$IFDEF SG_XML_DATA_TYPE}
        SetValueAsInt64(Result);
      end;
  else
    Result := 0;
    DoError(cnstIncorrectType);
  end
{$ENDIF}
end;

function TsgData.GetValueAsPoint: TPoint;
begin
{$IFDEF SG_XML_DATA_TYPE}
  case Value.DType of
    dtPoint:
      Result := Value.AsPoint{$IFNDEF SG_DATA64}^{$ENDIF};
    dtString:
      begin
{$ENDIF}
        Result := StrToTPoint(ValueAsStr);
{$IFDEF SG_XML_DATA_TYPE}
        SetValueAsPoint(Result);
      end;
  else
    DoError(cnstIncorrectType);
  end
{$ENDIF}
end;

function TsgData.GetValueAsPointer: Pointer;
begin
{$IFDEF SG_XML_DATA_TYPE}
  case Value.DType of
    dtPointer:
      Result := Value.AsPointer;
    dtString:
      begin
{$ENDIF}
        Result := StrToPointer(ValueAsStr);
{$IFDEF SG_XML_DATA_TYPE}
        SetValueAsPointer(Result);
      end;
  else
    Result := nil;
    DoError(cnstIncorrectType);
  end
{$ENDIF}
end;

function TsgData.GetValueAsRect: TRect;
begin
{$IFDEF SG_XML_DATA_TYPE}
  case Value.DType of
    dtRect:
      Result := Value.AsRect^;
    dtString:
      begin
{$ENDIF}
        Result := StrToTRect(ValueAsStr);
{$IFDEF SG_XML_DATA_TYPE}
        SetValueAsRect(Result);
      end;
  else
    DoError(cnstIncorrectType);
  end
{$ENDIF}
end;

function TsgData.GetValueAsVersion: TsgVersion;
begin
{$IFDEF SG_XML_DATA_TYPE}
  case Value.DType of
    dtVersion:
      Result := Value.AsVersion^;
    dtString:
      begin
{$ENDIF}
        Result := StrToVersion(ValueAsStr);
{$IFDEF SG_XML_DATA_TYPE}
        SetValueAsVersion(Result);
      end;
  else
    DoError(cnstIncorrectType);
  end
{$ENDIF}
end;

function TsgData.GetValueAsWord: Word;
begin
{$IFDEF SG_XML_DATA_TYPE}
  case Value.DType of
    dtWord:
      Result := Value.AsWord;
    dtString:
      begin
{$ENDIF}
        Result := GetValueAsInt;
{$IFDEF SG_XML_DATA_TYPE}
        if Measurements = umUndefined then
          SetValueAsWord(Result);
      end;
  else
    Result := 0;
    DoError(cnstIncorrectType);
  end
{$ENDIF}
end;

function TsgData.GetValueAsSingle: Single;
begin
{$IFDEF SG_XML_DATA_TYPE}
  case Value.DType of
    dtSingle:
      Result := Value.AsSingle;
    dtDouble:
      Result := Value.AsDouble;
    dtString:
      begin
{$ENDIF}
        Result := GetValueAsDouble;
{$IFDEF SG_XML_DATA_TYPE}
        if Measurements = umUndefined then
          SetValueAsSingle(Result);
      end;
  else
    Result := 0;
    DoError(cnstIncorrectType);
  end
{$ENDIF}
end;

function TsgData.GetValueAsString: string;
begin
{$IFDEF SG_XML_DATA_TYPE}
  Result := '';
  case Value.DType of
    dtBool:
      Result := BoolToStr(Value.AsBool);
    dtByte:
      Result := IntToStr(Value.AsByte);
    dtWord:
      Result := IntToStr(Value.AsWord);
    dtInteger:
      Result := IntToStr(Value.AsInteger);
    dtColor:
      Result := ColorHexToStr(Value.AsColor);
    dtSingle:
      Result := ValToStr(Value.AsSingle);
    dtPointer:
      Result := PointerToStr(Value.AsPointer);
    dtString:
      Result := Value.AsString^;
    dtTF2DPoint:
      Result := FPointToStr(MakeFPointFrom2D(Value.AsF2DPoint^));
    dtTFPoint:
      Result := FPointToStr(Value.AsFPoint^);
    dtRect:
      Result := TRectToStr(Value.AsRect^);
    dtTF2DRect:
      Result := F2DRectToStr(Value.AsF2DRect^);
    dtTFRect:
      Result := FRectToStr(Value.AsFRect^);
    dtVersion:
      Result := VersionToStr(Value.AsVersion^);
    dtColorCAD:
      Result := ColorCADToStr(Value.AsColorCAD^);

    dtHandle:
      Result := HandleToStr(Value.AsHandle{$IFNDEF SG_DATA64}^{$ENDIF});
    dtInt64:
      Result := IntToStr(Value.AsInt64{$IFNDEF SG_DATA64}^{$ENDIF});
    dtDouble:
      Result := ValToStr(Value.AsDouble{$IFNDEF SG_DATA64}^{$ENDIF});
    dtPoint:
      Result := TPointToStr(Value.AsPoint{$IFNDEF SG_DATA64}^{$ENDIF});
  end;
{$ELSE}
    Result := Value;
{$ENDIF}
end;

function TsgData.GetValueAsText: string;
begin
  Result := ReplaceAnsiCodesEx(ValueAsStr);
end;

function TsgData.IsEqual(const AData: TsgData;
  const ACaseSensitivity: Boolean = True): Boolean;
begin
  Result := Compare(AData, ACaseSensitivity) = 0;
end;

class procedure TsgData.SetAsBool(const AData: TsgData; const AValue: Boolean);
begin
  if AData <> nil then
    AData.ValueAsBool := AValue;
end;

class procedure TsgData.SetAsByte(const AData: TsgData; const AValue: Byte);
begin
  if AData <> nil then
    AData.ValueAsByte := AValue;
end;

class procedure TsgData.SetAsDouble(const AData: TsgData; const AValue: Double);
begin
  if AData <> nil then
    AData.ValueAsDouble := AValue;
end;

class procedure TsgData.SetAsF2DRect(const AData: TsgData;
  const AValue: TF2DRect);
begin
  if AData <> nil then
    AData.ValueAsF2DRect := AValue;
end;

class procedure TsgData.SetAsFPoint(const AData: TsgData;
  const AValue: TFPoint);
begin
  if AData <> nil then
    AData.ValueAsFPoint := AValue;
end;

class procedure TsgData.SetAsFRect(const AData: TsgData; const AValue: TFRect);
begin
  if AData <> nil then
    AData.ValueAsFRect := AValue;
end;

class procedure TsgData.SetAsHandle(const AData: TsgData; const AValue: UInt64);
begin
  if AData <> nil then
    AData.ValueAsHandle := AValue;
end;

class procedure TsgData.SetAsInt(const AData: TsgData; const AValue: Integer);
begin
  if AData <> nil then
    AData.ValueAsInt := AValue;
end;

class procedure TsgData.SetAsInt64(const AData: TsgData; const AValue: Int64);
begin
  if AData <> nil then
    AData.ValueAsInt64 := AValue;
end;

class procedure TsgData.SetAsPointer(const AData: TsgData;
  const AValue: Pointer);
begin
  if AData <> nil then
    AData.ValueAsPointer := AValue;
end;

class procedure TsgData.SetAsStr(const AData: TsgData; const AValue: string);
begin
  if AData <> nil then
    AData.ValueAsStr := AValue;
end;

class procedure TsgData.SetAsText(const AData: TsgData; const AValue: string);
begin
  if AData <> nil then
    AData.ValueAsText := AValue;
end;

procedure TsgData.SetValueAsBool(const AValue: Boolean);
begin
{$IFDEF SG_XML_DATA_TYPE}
  SetValueWithType(dtBool, @AValue);
{$ELSE}
  Value := BoolToStr(AValue);
{$ENDIF}
end;

procedure TsgData.SetValueAsByte(const AValue: Byte);
begin
{$IFDEF SG_XML_DATA_TYPE}
  SetValueWithType(dtByte, @AValue);
{$ELSE}
  SetValueAsInt(AValue);
{$ENDIF}
end;

procedure TsgData.SetValueAsColor(const AValue: TColor);
begin
{$IFDEF SG_XML_DATA_TYPE}
  SetValueWithType(dtColor, @AValue);
{$ELSE}
  Value := ColorHexToStr(AValue);
{$ENDIF}
end;

procedure TsgData.SetValueAsColorCAD(const AValue: TsgColorCAD);
begin
{$IFDEF SG_XML_DATA_TYPE}
  SetValueWithType(dtColorCAD, @AValue);
{$ELSE}
  Value := ColorCADToStr(AValue);
{$ENDIF}
end;

procedure TsgData.SetValueAsDouble(const AValue: Double);
begin
{$IFDEF SG_XML_DATA_TYPE}
  SetValueWithType(dtDouble, @AValue);
{$ELSE}
  Value := ValToStr(AValue);
{$ENDIF}
end;

procedure TsgData.SetValueAsF2DRect(const AValue: TF2DRect);
begin
{$IFDEF SG_XML_DATA_TYPE}
  SetValueWithType(dtTF2DRect, @AValue);
{$ELSE}
  Value := F2DRectToStr(AValue);
{$ENDIF}
end;

procedure TsgData.SetValueAsFPoint(const AValue: TFPoint);
begin
{$IFDEF SG_XML_DATA_TYPE}
  SetValueWithType(dtTFPoint, @AValue);
{$ELSE}
  Value := FPointToStr(AValue);
{$ENDIF}
end;

procedure TsgData.SetValueAsF2DPoint(const AValue: TF2DPoint);
begin
{$IFDEF SG_XML_DATA_TYPE}
  SetValueWithType(dtTF2DPoint, @AValue);
{$ELSE}
  Value := FPointToStr(MakeFPointFrom2D(AValue));
{$ENDIF}
end;

procedure TsgData.SetValueAsFRect(const AValue: TFRect);
begin
{$IFDEF SG_XML_DATA_TYPE}
  SetValueWithType(dtTFRect, @AValue);
{$ELSE}
  Value := FRectToStr(AValue);
{$ENDIF}
end;

procedure TsgData.SetValueAsHandle(const AValue: UInt64);
begin
{$IFDEF SG_XML_DATA_TYPE}
  SetValueWithType(dtHandle, @AValue);
{$ELSE}
  Value := HandleToStr(AValue);
{$ENDIF}
end;

procedure TsgData.SetValueAsInt(const AValue: Integer);
begin
{$IFDEF SG_XML_DATA_TYPE}
  SetValueWithType(dtInteger, @AValue);
{$ELSE}
  Value := IntToStr(AValue);
{$ENDIF}
end;

procedure TsgData.SetValueAsInt64(const AValue: Int64);
begin
{$IFDEF SG_XML_DATA_TYPE}
  SetValueWithType(dtInt64, @AValue);
{$ELSE}
  Value := IntToStr(AValue);
{$ENDIF}
end;

procedure TsgData.SetValueAsPoint(const AValue: TPoint);
begin
{$IFDEF SG_XML_DATA_TYPE}
  SetValueWithType(dtPoint, @AValue);
{$ELSE}
  Value := TPointToStr(AValue);
{$ENDIF}
end;

procedure TsgData.SetValueAsPointer(const AValue: Pointer);
begin
{$IFDEF SG_XML_DATA_TYPE}
  SetValueWithType(dtPointer, @AValue);
{$ELSE}
  Value := PointerToStr(AValue);
{$ENDIF}
end;

procedure TsgData.SetValueAsVersion(const AValue: TsgVersion);
begin
{$IFDEF SG_XML_DATA_TYPE}
  SetValueWithType(dtVersion, @AValue);
{$ELSE}
  Value := VersionToStr(AValue);
{$ENDIF}
end;

procedure TsgData.SetValueAsSingle(const AValue: Single);
begin
{$IFDEF SG_XML_DATA_TYPE}
  SetValueWithType(dtSingle, @AValue);
{$ELSE}
  SetValueAsDouble(AValue);
{$ENDIF}
end;

procedure TsgData.SetValueAsString(const AValue: string);
begin
{$IFDEF SG_XML_DATA_TYPE}
  SetValueWithType(dtString, @AValue);
{$ELSE}
  Value := AValue;
{$ENDIF}
end;

procedure TsgData.SetValueAsText(const AValue: string);
begin
  ValueAsStr := ReplaceWebCodeEx(AValue);
end;

procedure TsgData.SetValueAsRect(const AValue: TRect);
begin
{$IFDEF SG_XML_DATA_TYPE}
  SetValueWithType(dtRect, @AValue);
{$ELSE}
  Value := TRectToStr(AValue);
{$ENDIF}
end;

procedure TsgData.SetValueAsWord(const AValue: Word);
begin
{$IFDEF SG_XML_DATA_TYPE}
  SetValueWithType(dtWord, @AValue);
{$ELSE}
  SetValueAsInt(AValue);
{$ENDIF}
end;

{$IFDEF SG_XML_DATA_TYPE}

procedure TsgData.NewValueWithType(const ADataType: TsgDataType);
begin
  ClearValue;
  FValue.DType := ADataType;
  case FValue.DType of
    dtString:
      New(FValue.AsString);
    dtTF2DPoint:
      New(FValue.AsF2DPoint);
    dtTFPoint:
      New(FValue.AsFPoint);
    dtRect:
      New(FValue.AsRect);
    dtTF2DRect:
      New(FValue.AsF2DRect);
    dtTFRect:
      New(FValue.AsFRect);
    dtVersion:
      New(FValue.AsVersion);
    dtColorCAD:
      New(FValue.AsColorCAD);
{$IFNDEF SG_DATA64}
    dtHandle:
      New(FValue.AsHandle);
    dtInt64:
      New(FValue.AsInt64);
    dtDouble:
      New(FValue.AsDouble);
    dtPoint:
      New(FValue.AsPoint);
{$ENDIF}
  end;
end;

procedure TsgData.SetValueWithType(const ADataType: TsgDataType;
  const AValue: Pointer);
begin
  if Value.DType = ADataType then
  begin
    case Value.DType of
      dtBool:
        FValue.AsBool := {$IFNDEF SGFPC} PBoolean(AValue)^
        {$ELSE} Boolean(PBoolean(AValue)^) {$ENDIF};
      dtByte:
        FValue.AsByte := PByte(AValue)^;
      dtWord:
        FValue.AsWord := PWord(AValue)^;
      dtInteger:
        FValue.AsInteger := PInteger(AValue)^;
      dtColor:
        FValue.AsColor := PColor(AValue)^;
      dtSingle:
        FValue.AsSingle := PSingle(AValue)^;
      dtPointer:
        FValue.AsPointer := PPointer(AValue)^;
      dtString:
        FValue.AsString^ := PString(AValue)^;
      dtTF2DPoint:
        FValue.AsF2DPoint^ := PF2DPoint(AValue)^;
      dtTFPoint:
        FValue.AsFPoint^ := PFPoint(AValue)^;
      dtRect:
        FValue.AsRect^ := PRect(AValue)^;
      dtTF2DRect:
        FValue.AsF2DRect^ := PF2DRect(AValue)^;
      dtTFRect:
        FValue.AsFRect^ := PFRect(AValue)^;
      dtVersion:
        FValue.AsVersion^ := PsgVersion(AValue)^;
      dtColorCAD:
        FValue.AsColorCAD^ := PsgColorCAD(AValue)^;

      dtHandle:
        FValue.AsHandle{$IFNDEF SG_DATA64}^{$ENDIF} := PUInt64(AValue)^;
      dtInt64:
        FValue.AsInt64{$IFNDEF SG_DATA64}^{$ENDIF} := PInt64(AValue)^;
      dtDouble:
        FValue.AsDouble{$IFNDEF SG_DATA64}^{$ENDIF} := PDouble(AValue)^;
      dtPoint:
        FValue.AsPoint{$IFNDEF SG_DATA64}^{$ENDIF} := PPoint(AValue)^;
    else
      FValue.Value := AValue;
    end;
  end
  else
  begin
    if ((ADataType = dtString) or (Value.DType in [dtUndefined, dtString])) or
      ((ADataType in [dtSingle, dtDouble]) and
      (Value.DType in [dtSingle, dtDouble])) then
    begin
      NewValueWithType(ADataType);
      SetValueWithType(ADataType, AValue);
    end
    else
      DoError(cnstIncorrectType);
  end;
end;
{$ENDIF}
{ TsgNodeIterator }

constructor TsgNodeIterator.Create(const ANode: TsgNodeSample;
  const AComparer: TsgFindNodeCustom);
var
  vNodes: TsgNodeList;
begin
  FROOT := ANode;
  vNodes := nil;
  if FROOT is TsgNode then
    vNodes := TsgNode(FROOT).FChilds;
  Create(vNodes, AComparer);
end;

function TsgNodeIterator.Compare(const ANode: TsgNodeSample): Boolean;
begin
  Result := FComparer.Compare(ANode);
end;

constructor TsgNodeIterator.Create(const ANodes: TsgNodeList;
  const AComparer: TsgFindNodeCustom);
begin
  FComparer := AComparer;
  FRootNotes := ANodes;
  FFindNodes := TList.Create;
  FFindTree := TList.Create;
end;

destructor TsgNodeIterator.Destroy;
begin
  FreeAndNil(FComparer);
  FreeAndNil(FFindNodes);
  FreeAndNil(FFindTree);
  inherited;
end;

function TsgNodeIterator.Find(const AFindCount: Integer = -1): Integer;
var
  I: Integer;
begin
  FFindCount := AFindCount;
  FFindNodes.Clear;
  if Assigned(FFindTree) then
  begin
    FFindCount := 1;
    FFindTree.Clear;
  end;
  try
    if Assigned(FRootNotes) then
    begin
      for I := 0 to FRootNotes.Count - 1 do
      begin
        if FindChildNode(FRootNotes.Nodes[I]) then
        begin
          if BreakFind then
            Break;
        end;
      end;
    end
    else
    begin
      if Assigned(FROOT) then
        FindChildNode(FROOT);
    end;
  finally
    Result := FFindNodes.Count;
  end;
end;

function TsgNodeIterator.FindChildNode(const ANode: TsgNodeSample): Boolean;
var
  I: Integer;
  vNode: TsgNodeSample;
begin
  Result := False;
  if Assigned(FFindTree) then
    FFindTree.Add(ANode);
  for I := 0 to ANode.ChildNodesCount - 1 do
  begin
    vNode := ANode.ChildNodes[I];
    if Compare(vNode) then
    begin
      FFindNodes.Add(vNode);
      Result := True;
      if BreakFind then
        Break;
    end;
    if FindChildNode(vNode) then
    begin
      Result := True;
      if BreakFind then
        Break;
    end;
  end;
  if (not Result) and Assigned(FFindTree) then
    FFindTree.Count := FFindTree.Count - 1;
end;

function TsgNodeIterator.BreakFind: Boolean;
begin
  Result := (FFindCount > -1) and (FFindCount <= FFindNodes.Count);
end;

function TsgNodeIterator.GetFindNotAll: Boolean;
begin
  Result := Assigned(FFindTree);
end;

procedure TsgNodeIterator.SetFindNotAll(const Value: Boolean);
begin
  FreeAndNil(FFindTree);
  if Value then
    FFindTree := TList.Create;
end;

{ TsgFindByAttribute }

function TsgFindByAttribute.Compare(const ANode: TsgNodeSample): Boolean;
var
  vAttrib: TsgNodeSample;
begin
  Result := False;
  vAttrib := ANode.GetAttributeByName(Name);
  if Assigned(vAttrib) then
  begin
    if IgnoreValue then
      Result := True
    else
    begin
      if CaseSensitivity then
        Result := vAttrib.ValueAsStr = Value
      else
        Result := CompareText(vAttrib.ValueAsStr, Value) = 0;
    end;
  end;
end;

constructor TsgFindByAttribute.Create(const AName, AValue: string;
  const AIgnore: Boolean = False; const ACaseSensitivity: Boolean = True);
begin
  inherited Create;
  Name := AName;
  Value := AValue;
  IgnoreValue := AIgnore;
  CaseSensitivity := ACaseSensitivity;
end;

{ TsgFindByNode }

function TsgFindByNode.Compare(const ANode: TsgNodeSample): Boolean;
begin
  if CaseSensitivity then
    Result := ANode.Name = Name
  else
    Result := CompareText(ANode.Name, Name) = 0;
end;

constructor TsgFindByNode.Create(const AName: string;
  const ACaseSensitivity: Boolean = True);
begin
  inherited Create;
  Name := AName;
  CaseSensitivity := ACaseSensitivity;
end;

initialization

KeyWords := nil;
TableColors := nil;
{$IFDEF SG_USE_DICTIONARY_BY_NAME}
DictionaryByNames := nil;
DictionaryByIndexes := nil;
{$IFDEF SG_OPENING_IN_THEADS}
InitializeCriticalSection(DictionaryLock);
CreateDictionaryNames;
{$ENDIF}
{$ENDIF}
{$IFDEF SG_OPENING_IN_THEADS}
CreateKeyWords;
CreateTableColor;
{$ELSE}
Spliter := TsgStringList.Create;
{$ENDIF}

finalization

FreeAndNil(KeyWords);
FreeAndNil(TableColors);
{$IFDEF SG_USE_DICTIONARY_BY_NAME}
FreeAndNil(DictionaryByNames);
FreeAndNil(DictionaryByIndexes);
{$IFDEF SG_OPENING_IN_THEADS}
DeleteCriticalSection(DictionaryLock);
{$ENDIF}
{$ENDIF}
{$IFDEF SG_OPENING_IN_THEADS}
{$ELSE}
FreeAndNil(Spliter);
{$ENDIF}

end.
