{ fpsxmlcommon.pas
  Unit shared by all xml-type reader/writer classes }

unit fpsxmlcommon;

{$mode objfpc}

interface

uses
  Classes, SysUtils,
  laz2_xmlread, laz2_DOM,
  fpspreadsheet;

type
  TsSpreadXMLReader = class(TsCustomSpreadReader)
  protected
    function GetAttrValue(ANode : TDOMNode; AAttrName : string) : string;
    function GetNodeValue(ANode: TDOMNode): String;
    procedure ReadXMLFile(out ADoc: TXMLDocument; AFileName: String);
  end;

implementation

uses
  fpsStreams;

{ Gets value for the specified attribute. Returns empty string if attribute
  not found. }
function TsSpreadXMLReader.GetAttrValue(ANode : TDOMNode; AAttrName : string) : string;
var
  i: LongWord;
  Found: Boolean;
begin
  Result := '';
  if ANode = nil then
    exit;

  Found := false;
  i := 0;
  while not Found and (i < ANode.Attributes.Length) do begin
    if ANode.Attributes.Item[i].NodeName = AAttrName then begin
      Found := true;
      Result := ANode.Attributes.Item[i].NodeValue;
    end;
    inc(i);
  end;
end;

{ Returns the text value of a node. Normally it would be sufficient to call
  "ANode.NodeValue", but since the DOMParser needs to preserve white space
  (for the spaces in date/time formats), we have to go more into detail. }
function TsSpreadXMLReader.GetNodeValue(ANode: TDOMNode): String;
var
  child: TDOMNode;
begin
  Result := '';
  child := ANode.FirstChild;
  if Assigned(child) and (child.NodeName = '#text') then
    Result := child.NodeValue;
end;

{ We have to use our own ReadXMLFile procedure (there is one in xmlread)
  because we have to preserve spaces in element text for date/time separator.
  As a side-effect we have to skip leading spaces by ourselves. }
procedure TsSpreadXMLReader.ReadXMLFile(out ADoc: TXMLDocument; AFileName: String);
var
  parser: TDOMParser;
  src: TXMLInputSource;
  stream: TStream;
begin
  if (boBufStream in Workbook.Options) then
    stream := TBufStream.Create(AFileName, fmOpenRead + fmShareDenyWrite)
  else
    stream := TFileStream.Create(AFileName, fmOpenRead + fmShareDenyWrite);

  try
    parser := TDOMParser.Create;
    try
      parser.Options.PreserveWhiteSpace := true;    // This preserves spaces!
      src := TXMLInputSource.Create(stream);
      try
        parser.Parse(src, ADoc);
      finally
        src.Free;
      end;
    finally
      parser.Free;
    end;
  finally
    stream.Free;
  end;
end;


end.

