home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / Issue50 / XML / XmlClasses.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1999-09-04  |  32.6 KB  |  1,206 lines

  1. unit XmlClasses;
  2.  
  3. interface
  4.  
  5. uses Classes, SysUtils, MSXML_TLB, Dialogs, ComCtrls, ImgList, Graphics;
  6.  
  7. type
  8.   TXmlNodeType = (xntDocument, xntElement, xntText,
  9.       xntComment, xntCDATASection);
  10.  
  11.   TCharEntity = (ceLt, ceGt, ceQuot, ceApos, ceAmp);
  12.  
  13.   TCharEntities = set of TCharEntity;
  14.  
  15.   TSubstituteCharEntitiesEvent = procedure(Sender: TObject;
  16.       var Text: String; var SkipTranslation: Boolean) of Object;
  17.  
  18. const
  19.   XmlNodeNames: array[xntDocument..xntCDATASection] of String =
  20.       ('#document', '', '#text', '#comment', '#cdata-section');
  21.  
  22. type
  23.   EXmlDError = class(Exception);
  24.  
  25.   EXmlDParseError = class(Exception)
  26.     FErrorCode:   Integer;
  27.     FReason:      String;
  28.     FSrcText:     String;
  29.     FLine:        Integer;
  30.     FLinePos:     Integer;
  31.   public
  32.     constructor Create(ParseError: IXMLDOMParseError);
  33.     procedure ShowParseError;
  34.     property ErrorCode: Integer read FErrorCode;
  35.     property Reason: String read FReason;
  36.     property SrcText: String read FSrcText;
  37.     property Line: Integer read FLine;
  38.     property LinePos: Integer read FLinePos;
  39.   end;
  40.  
  41.   TXmlName = String;
  42.  
  43.   TXmlDDocument = class;
  44.   TXmlDStructureNode = class;
  45.   TXmlDElement = class;
  46.   TXmlDCDATASection = class;
  47.   TXmlDComment = class;
  48.   TXmlDText = class;
  49.   TXmlDAttrList = class;
  50.  
  51.   TXmlDNode = class(TPersistent)
  52.     private
  53.       FPreviousSibling: TXmlDNode;
  54.       FNextSibling:     TXmlDNode;
  55.       FParentNode:      TXmlDStructureNode;
  56.       FNodeType:        TXmlNodeType;
  57.     protected
  58.       function GetFirstChild: TXmlDNode; virtual;
  59.       function GetLastChild: TXmlDNode; virtual;
  60.       function GetOwnerDocument: TXmlDDocument;
  61.       function GetNodeName: TXmlName; virtual;
  62.       function GetNodeValue: String; virtual;
  63.       procedure SetNodeName(const Value: TXmlName); virtual;
  64.       procedure SetNodeValue(const Value: String); virtual;
  65.       function GetLevel: Integer;
  66.       procedure WriteToStream(Stream: TStream;
  67.           FormattedForPrint: Boolean); virtual; abstract;
  68.       procedure WriteFormattedPrefix(Stream: TStream);
  69.       procedure WriteFormattedSuffix(Stream: TStream);
  70.     public
  71.       constructor Create;
  72.       function CloneNode(RecurseChildren: Boolean = False): TXmlDNode;
  73.           virtual; abstract;
  74.       procedure AppendChild(NewNode: TXmlDNode); virtual;
  75.       function ReplaceChild(NewNode: TXmlDNode; OldNode: TXmlDNode):
  76.           TXmlDNode; virtual;
  77.       procedure InsertBefore(NewNode: TXmlDNode; ThisNode: TXmlDNode);
  78.       function RemoveChild(ThisNode: TXmlDNode): TXmlDNode; virtual;
  79.       function HasChildNodes: Boolean; virtual;
  80.       property FirstChild: TXmlDNode read GetFirstChild;
  81.       property LastChild: TXmlDNode read GetLastChild;
  82.       property PreviousSibling: TXmlDNode read FPreviousSibling;
  83.       property NextSibling: TXmlDNode read FNextSibling;
  84.       property ParentNode: TXmlDStructureNode read FParentNode;
  85.       property OwnerDocument: TXmlDDocument read GetOwnerDocument;
  86.       property NodeName: TXmlName read GetNodeName write SetNodeName;
  87.       property NodeType: TXmlNodeType read FNodeType;
  88.       property NodeValue: String read GetNodeValue write SetNodeValue;
  89.       property Level: Integer read GetLevel;
  90.   end;
  91.  
  92.   TXmlDStructureNode = class(TXmlDNode)
  93.     private
  94.       FAttrList:        TXmlDAttrList;
  95.       FFirstChild:      TXmlDNode;
  96.       FLastChild:       TXmlDNode;
  97.     protected
  98.       procedure CloneChildren(FromNode: TXmlDStructureNode);
  99.       procedure WriteChildrenToStream(Stream: TStream;
  100.           FormattedForPrint:Boolean);
  101.     public
  102.       constructor Create;
  103.       destructor Destroy; override;
  104.       function GetFirstChild: TXmlDNode; override;
  105.       function GetLastChild: TXmlDNode; override;
  106.       procedure AppendChild(NewNode: TXmlDNode); override;
  107.       function ReplaceChild(NewNode: TXmlDNode; OldNode: TXmlDNode):
  108.           TXmlDNode; override;
  109.       function RemoveChild(ThisNode: TXmlDNode): TXmlDNode; override;
  110.       function HasChildNodes: Boolean; override;
  111.       property AttrList: TXmlDAttrList read FAttrList;
  112.     end;
  113.  
  114.   TXmlDContentNode = class(TXmlDNode)
  115.     private
  116.       FValue: String;
  117.     protected
  118.       function GetNodeValue: String; override;
  119.       procedure SetNodeValue(const Value: String); override;
  120.   end;
  121.  
  122.   TXmlDDocument = class(TXmlDStructureNode)
  123.     private
  124.       FDocumentElement: TXmlDElement;
  125.       FDocumentTypeDefinition: String;
  126.       FAttrCharEntities: TCharEntities;
  127.       FTextCharEntities: TCharEntities;
  128.       FOnOutputAttrValue: TSubstituteCharEntitiesEvent;
  129.       FOnOutputTextValue: TSubstituteCharEntitiesEvent;
  130.       DiscardUnsupportedItems: Boolean;
  131.     protected
  132.       procedure WriteToStream(Stream: TStream;
  133.           FormattedForPrint: Boolean); override;
  134.       procedure WritePrologToStream(Stream: TStream);
  135.       procedure DecodePrologAttrs(S: String);
  136.       procedure LoadFromDOMDocument(Doc: IXMLDOMDocument);
  137.       procedure LoadChildNodes(ParNode: TXmlDNode;
  138.           ParDOMNode: IXMLDOMNode);
  139.       procedure LoadAttributes(Node: TXmlDElement;
  140.           DOMNode: IXMLDOMNode);
  141.       procedure AssignNodeToTreeNode(XmlNode: TXmlDNode;
  142.         TreeNode: TTreeNode);
  143.       procedure AssignAttrNodesToTreeNodes(ParXmlNode: TXmlDNode;
  144.         ParTreeNode: TTreeNode);
  145.     public
  146.       constructor Create;
  147.       function CloneNode(RecurseChildren: Boolean): TXmlDNode;
  148.           override;
  149.       procedure Clear;
  150.       procedure AppendChild(NewNode: TXmlDNode); override;
  151.       function ReplaceChild(NewNode: TXmlDNode; OldNode: TXmlDNode):
  152.           TXmlDNode; override;
  153.       procedure InsertBefore(NewNode: TXmlDNode; ThisNode: TXmlDNode);
  154.       function RemoveChild(ThisNode: TXmlDNode): TXmlDNode; override;
  155.       function CreateCDATASection(const Text: String):
  156.           TXmlDCDATASection;
  157.       function CreateComment(const Text: String): TXmlDComment;
  158.       function CreateElement(const TagName: TXmlName): TXmlDElement;
  159.           overload;
  160.       function CreateElement(const TagName: TXmlName;
  161.           const Data: String): TXmlDElement; overload;
  162.       function CreateElement(const TagName: TXmlName;
  163.           const Data: String; const AttrName: TXmlName;
  164.           const AttrValue: String): TXmlDElement; overload;
  165.       function CreateElement(const TagName: TXmlName;
  166.           const Data: String; const AttrNames: array of TXmlName;
  167.           const AttrValues: array of String): TXmlDElement; overload;
  168.       function CreateTextNode(const Text: String): TXmlDText;
  169.       procedure AssignTo(Dest: TPersistent); override;
  170.       procedure LoadFromStream(Stream: TStream;
  171.           ValidateOnParse: Boolean = True;
  172.           DiscardUnsupportedItems: Boolean = False);
  173.       procedure LoadFromFile(const FileName: String;
  174.           ValidateOnParse: Boolean = True;
  175.           DiscardUnsupportedItems: Boolean = False);
  176.       procedure SaveToStream(Stream: TStream;
  177.           FormattedForPrint: Boolean = False);
  178.       procedure SaveToFile(const FileName: String;
  179.           FormattedForPrint: Boolean = False);
  180.       property DocumentElement: TXmlDElement read FDocumentElement;
  181.       property DocumentTypeDefinition: String
  182.           read FDocumentTypeDefinition write FDocumentTypeDefinition;
  183.       property AttrCharEntities: TCharEntities read FAttrCharEntities
  184.           write FAttrCharEntities;
  185.       property TextCharEntities: TCharEntities read FTextCharEntities
  186.           write FTextCharEntities;
  187.       property OnOutputAttrValue: TSubstituteCharEntitiesEvent
  188.           read FOnOutputAttrValue write FOnOutputAttrValue;
  189.       property OnOutputTextValue: TSubstituteCharEntitiesEvent
  190.           read FOnOutputTextValue write FOnOutputTextValue;
  191.   end;
  192.  
  193.   TXmlDElement = class(TXmlDStructureNode)
  194.     private
  195.       FNodeName:        TXmlName;
  196.     protected
  197.       function GetNodeName: TXmlName; override;
  198.       procedure SetNodeName(const Value: TXmlName); override;
  199.       procedure WriteToStream(Stream: TStream;
  200.           FormattedForPrint: Boolean); override;
  201.     public
  202.       constructor Create;
  203.       function CloneNode(RecurseChildren: Boolean): TXmlDNode;
  204.           override;
  205.   end;
  206.  
  207.   TXmlDText = class(TXmlDContentNode)
  208.   protected
  209.     procedure WriteToStream(Stream: TStream;
  210.         FormattedForPrint: Boolean); override;
  211.   public
  212.     constructor Create;
  213.     function CloneNode(RecurseChildren: Boolean): TXmlDNode; override;
  214.   end;
  215.  
  216.   TXmlDComment = class(TXmlDContentNode)
  217.   protected
  218.     procedure WriteToStream(Stream: TStream;
  219.         FormattedForPrint: Boolean); override;
  220.   public
  221.     constructor Create;
  222.     function CloneNode(RecurseChildren: Boolean): TXmlDNode; override;
  223.   end;
  224.  
  225.   TXmlDCDATASection = class(TXmlDContentNode)
  226.   protected
  227.     procedure WriteToStream(Stream: TStream;
  228.         FormattedForPrint: Boolean); override;
  229.   public
  230.     constructor Create;
  231.     function CloneNode(RecurseChildren: Boolean): TXmlDNode; override;
  232.   end;
  233.  
  234.   TXmlDAttrList = class(TPersistent)
  235.   private
  236.     List:     TStringList;
  237.     FOwnerNode: TXmlDStructureNode;
  238.   protected
  239.     function Add(const S: String): Integer;
  240.     function GetCount: Integer;
  241.     function GetValues(const Name: TXmlName): String;
  242.     function GetNames(Index: Integer): TXmlName;
  243.     procedure SetValues(const Name: TXmlName; const Value: String);
  244.     procedure WriteToStream(Stream: TStream);
  245.   public
  246.     constructor Create;
  247.     destructor Destroy; override;
  248.     procedure Assign(Source: TPersistent); override;
  249.     procedure Clear;
  250.     property Count: Integer read GetCount;
  251.     property OwnerNode: TXmlDStructureNode read FOwnerNode;
  252.     property Names[Index: Integer]: TXmlName read GetNames;
  253.     property Values[const Name: TXmlName]: String read GetValues
  254.         write SetValues; default;
  255.   end;
  256.  
  257. implementation
  258.  
  259. {$R XMLTreeView.res}
  260.  
  261. { Utility Function }
  262.  
  263. function CharEntitiesReplace(const S: String; CE: TCharEntities):
  264.     String;
  265. begin
  266.   Result := S;
  267.   if ceAmp in CE then
  268.     Result := StringReplace(Result, '&', '&', [rfReplaceAll]);
  269.   if ceLt in CE then
  270.     Result := StringReplace(Result, '<', '<', [rfReplaceAll]);
  271.   if ceGt in CE then
  272.     Result := StringReplace(Result, '>', '>', [rfReplaceAll]);
  273.   if ceApos in CE then
  274.     Result := StringReplace(Result, '''', ''', [rfReplaceAll]);
  275.   if ceQuot in CE then
  276.     Result := StringReplace(Result, '"', '"', [rfReplaceAll]);
  277. end;
  278.  
  279. { EXmlDParseError }
  280.  
  281. constructor EXmlDParseError.Create(ParseError: IXMLDOMParseError);
  282. begin
  283.   inherited Create('XML Parse Error');
  284.   FErrorCode := ParseError.errorCode;
  285.   FReason := ParseError.reason;
  286.   FSrcText := ParseError.srcText;
  287.   FLine := ParseError.line;
  288.   FLinePos := ParseError.linePos;
  289. end;
  290.  
  291. procedure EXmlDParseError.ShowParseError;
  292. var
  293.   S:  String;
  294. begin
  295.   S := 'XML Parse Error:' + FReason +
  296.       'Line=' + IntToStr(FLine) +
  297.       ' LinePos=' + IntToStr(FLinePos);
  298.   MessageDlg(S, mtError, [mbOK], 0);
  299. end;
  300.  
  301. { TXmlDNode }
  302.  
  303. procedure TXmlDNode.AppendChild(NewNode: TXmlDNode);
  304. begin
  305.   raise EXmlDError.Create('AppendChild operation requested on ' +
  306.       'invalid node type');
  307. end;
  308.  
  309. constructor TXmlDNode.Create;
  310. begin
  311.   inherited Create;
  312. end;
  313.  
  314. function TXmlDNode.GetFirstChild: TXmlDNode;
  315. begin
  316.   Result := nil;
  317. end;
  318.  
  319. function TXmlDNode.GetLastChild: TXmlDNode;
  320. begin
  321.   Result := nil;
  322. end;
  323.  
  324. function TXmlDNode.GetLevel: Integer;
  325. var
  326.   ParentNode: TXmlDStructureNode;
  327. begin
  328.   Result := 0;
  329.   ParentNode := FParentNode;
  330.   while ParentNode <> nil do
  331.   begin
  332.     Inc(Result);
  333.     ParentNode := ParentNode.FParentNode;
  334.   end;
  335. end;
  336.  
  337. function TXmlDNode.GetNodeName: TXmlName;
  338. begin
  339.   Result := XmlNodeNames[FNodeType];
  340. end;
  341.  
  342. function TXmlDNode.GetNodeValue: String;
  343. begin
  344.   Result := '';
  345. end;
  346.  
  347. function TXmlDNode.GetOwnerDocument: TXmlDDocument;
  348. var
  349.   ANode: TXmlDStructureNode;
  350. begin
  351.   ANode := TXmlDStructureNode(Self);
  352.   while ANode.FParentNode <> nil do
  353.     ANode := ANode.FParentNode;
  354.   Result := ANode as TXmlDDocument;
  355. end;
  356.  
  357. function TXmlDNode.HasChildNodes: Boolean;
  358. begin
  359.   Result := False;
  360. end;
  361.  
  362. procedure TXmlDNode.InsertBefore(NewNode, ThisNode: TXmlDNode);
  363. begin
  364.   if ThisNode = FParentNode.FFirstChild then
  365.     FParentNode.FFirstChild := NewNode;
  366.   if ThisNode.FPreviousSibling <> nil then
  367.     ThisNode.FPreviousSibling.FNextSibling := NewNode;
  368.   NewNode.FPreviousSibling := ThisNode.FPreviousSibling;
  369.   ThisNode.FPreviousSibling := NewNode;
  370.   NewNode.FNextSibling := ThisNode;
  371. end;
  372.  
  373. function TXmlDNode.RemoveChild(ThisNode: TXmlDNode): TXmlDNode;
  374. begin
  375.   raise EXmlDError.Create('RemoveChild operation requested on ' +
  376.       'invalid node type');
  377. end;
  378.  
  379. function TXmlDNode.ReplaceChild(NewNode, OldNode: TXmlDNode): TXmlDNode;
  380. begin
  381.   raise EXmlDError.Create('ReplaceChild operation requested on ' +
  382.       'invalid node type');
  383. end;
  384.  
  385. procedure TXmlDNode.SetNodeName(const Value: TXmlName);
  386. begin
  387. end;
  388.  
  389. procedure TXmlDNode.SetNodeValue(const Value: String);
  390. begin
  391. end;
  392.  
  393. procedure TXmlDNode.WriteFormattedPrefix(Stream: TStream);
  394. var
  395.   S:  String;
  396. begin
  397.   S := StringOfChar(' ', (Level - 1) * 2);
  398.   Stream.WriteBuffer(Pointer(S)^, Length(S));
  399. end;
  400.  
  401. procedure TXmlDNode.WriteFormattedSuffix(Stream: TStream);
  402. const
  403.   CRLF:  String[3] = #13#10;
  404. begin
  405.   Stream.WriteBuffer(CRLF[1], 2);
  406. end;
  407.  
  408. { TXmlDStructureNode }
  409.  
  410. procedure TXmlDStructureNode.AppendChild(NewNode: TXmlDNode);
  411. begin
  412.   NewNode.FParentNode := Self;
  413.   if FFirstChild = nil then
  414.   begin
  415.     FFirstChild := NewNode;
  416.     FLastChild := NewNode;
  417.   end
  418.   else
  419.   begin
  420.     FLastChild.FNextSibling := NewNode;
  421.     NewNode.FPreviousSibling := FLastChild;
  422.     FLastChild := NewNode;
  423.   end;
  424. end;
  425.  
  426. procedure TXmlDStructureNode.CloneChildren(FromNode: TXmlDStructureNode);
  427. var
  428.   N:  TXmlDNode;
  429. begin
  430.   N := FromNode.FFirstChild;
  431.   while N <> nil do
  432.   begin
  433.     AppendChild(N.CloneNode(True));
  434.     N := N.NextSibling;
  435.   end;
  436. end;
  437.  
  438. constructor TXmlDStructureNode.Create;
  439. begin
  440.   FAttrList := TXmlDAttrList.Create;
  441.   FAttrList.FOwnerNode := Self;
  442. end;
  443.  
  444. destructor TXmlDStructureNode.Destroy;
  445. var
  446.   Node: TXmlDNode;
  447.   NextNode: TXmlDNode;
  448. begin
  449.   Node := FFirstChild;
  450.   while (Node <> nil) do
  451.   begin
  452.     NextNode := Node.FNextSibling;
  453.     Node.Free;
  454.     Node := NextNode;
  455.   end;
  456.   FAttrList.Free;
  457.   inherited Destroy;
  458. end;
  459.  
  460. function TXmlDStructureNode.GetFirstChild: TXmlDNode;
  461. begin
  462.   Result := FFirstChild;
  463. end;
  464.  
  465. function TXmlDStructureNode.GetLastChild: TXmlDNode;
  466. begin
  467.   Result := FLastChild;
  468. end;
  469.  
  470. function TXmlDStructureNode.HasChildNodes: Boolean;
  471. begin
  472.   Result := FFirstChild <> nil;
  473. end;
  474.  
  475. function TXmlDStructureNode.RemoveChild(ThisNode: TXmlDNode): TXmlDNode;
  476. begin
  477.   Result := FFirstChild;
  478.   while ((Result <> nil) and (Result <> ThisNode)) do
  479.     Result := Result.FNextSibling;
  480.   if Result <> nil then
  481.   begin
  482.     if FFirstChild = FLastChild then
  483.     begin
  484.       FFirstChild := nil;
  485.       FLastChild := nil;
  486.     end
  487.     else if Result = FFirstChild then
  488.     begin
  489.       FFirstChild := FFirstChild.FNextSibling;
  490.       FFirstChild.FPreviousSibling := nil;
  491.     end
  492.     else if Result = FLastChild then
  493.     begin
  494.       FLastChild := FLastChild.FPreviousSibling;
  495.       FLastChild.FNextSibling := nil;
  496.     end
  497.     else
  498.     begin
  499.       Result.FPreviousSibling.FNextSibling := Result.FNextSibling;
  500.       Result.FNextSibling.FPreviousSibling := Result.FPreviousSibling;
  501.     end;
  502.     Result.FNextSibling := nil;
  503.     Result.FPreviousSibling := nil;
  504.     Result.FParentNode := nil;
  505.   end;
  506. end;
  507.  
  508. function TXmlDStructureNode.ReplaceChild(NewNode,
  509.   OldNode: TXmlDNode): TXmlDNode;
  510. var
  511.   NextNode: TXmlDNode;
  512. begin
  513.   if OldNode = FLastChild then
  514.   begin
  515.     Result := RemoveChild(OldNode);
  516.     AppendChild(NewNode);
  517.   end
  518.   else
  519.   begin
  520.     NextNode := OldNode.FNextSibling;
  521.     Result := RemoveChild(OldNode);
  522.     InsertBefore(NewNode, NextNode);
  523.   end;
  524. end;
  525.  
  526. procedure TXmlDStructureNode.WriteChildrenToStream(Stream: TStream;
  527.     FormattedForPrint: Boolean);
  528. var
  529.   N:  TXmlDNode;
  530. begin
  531.   N := FFirstChild;
  532.   while (N <> nil) do
  533.   begin
  534.     N.WriteToStream(Stream, FormattedForPrint);
  535.     N := N.FNextSibling;
  536.   end;
  537. end;
  538.  
  539. { TXmlDContentNode }
  540.  
  541. function TXmlDContentNode.GetNodeValue: String;
  542. begin
  543.   Result := FValue;
  544. end;
  545.  
  546. procedure TXmlDContentNode.SetNodeValue(const Value: String);
  547. begin
  548.   FValue := Value;
  549. end;
  550.  
  551. { TXmlDDocument }
  552.  
  553. procedure TXmlDDocument.AppendChild(NewNode: TXmlDNode);
  554. begin
  555.   if NewNode.NodeType = xntElement then
  556.   begin
  557.     if FDocumentElement <> nil then
  558.       raise EXmlDError.Create('Second document element add attempted');
  559.     FDocumentElement := TXmlDElement(NewNode);
  560.   end;
  561.   inherited AppendChild(NewNode);
  562. end;
  563.  
  564. procedure TXmlDDocument.AssignAttrNodesToTreeNodes(ParXmlNode: TXmlDNode;
  565.   ParTreeNode: TTreeNode);
  566. var
  567.   I:  Integer;
  568.   S:  String;
  569.   TreeNode: TTreeNode;
  570.   XSN: TXmlDStructureNode;
  571. begin
  572.   XSN := ParXmlNode as TXmlDStructureNode;
  573.   for I := 0 to (XSN.FAttrList.Count - 1) do
  574.   begin
  575.     S := StringReplace(XSN.FAttrList.List.Strings[I],
  576.         '=', '="', []) + '"';
  577.     TreeNode := ParTreeNode.Owner.AddChild(ParTreeNode, S);
  578.     if ParXmlNode.NodeType = xntDocument then
  579.       TreeNode.ImageIndex := 1
  580.     else
  581.       TreeNode.ImageIndex := 3;
  582.     TreeNode.SelectedIndex := TreeNode.ImageIndex;
  583.     TreeNode.Data := XSN.FAttrList;
  584.   end;
  585. end;
  586.  
  587. procedure TXmlDDocument.AssignNodeToTreeNode(XmlNode: TXmlDNode;
  588.     TreeNode: TTreeNode);
  589. begin
  590.   case XmlNode.NodeType of
  591.     xntDocument:
  592.     begin
  593.       TreeNode.Text := 'XML Document';
  594.       TreeNode.ImageIndex := 0;
  595.       AssignAttrNodesToTreeNodes(XmlNode, TreeNode);
  596.     end;
  597.     xntElement:
  598.     begin
  599.       TreeNode.Text := XmlNode.NodeName;
  600.       TreeNode.ImageIndex := 2;
  601.       AssignAttrNodesToTreeNodes(XmlNode, TreeNode);
  602.     end;
  603.     xntText:
  604.     begin
  605.       TreeNode.Text := XmlNode.NodeValue;
  606.       TreeNode.ImageIndex := 4;
  607.     end;
  608.     xntCDATASection:
  609.     begin
  610.       TreeNode.Text := XmlNode.NodeValue;
  611.       TreeNode.ImageIndex := 5;
  612.     end;
  613.     xntComment:
  614.     begin
  615.       TreeNode.Text := XmlNode.NodeValue;
  616.       TreeNode.ImageIndex := 6;
  617.     end;
  618.   end;
  619.   TreeNode.SelectedIndex := TreeNode.ImageIndex;
  620.   TreeNode.Data := XmlNode;
  621. end;
  622.  
  623. procedure TXmlDDocument.AssignTo(Dest: TPersistent);
  624. var
  625.   TV: TTreeView;
  626.   TN: TTreeNodes;
  627.   TreeNode: TTreeNode;
  628.  
  629.   procedure AddChildNodes(ParXmlNode: TXmlDNode;
  630.       ParTreeNode: TTreeNode);
  631.   var
  632.     XmlNode:  TXmlDNode;
  633.     TreeNode: TTreeNode;
  634.   begin
  635.     XmlNode := ParXmlNode.FirstChild;
  636.     while (XmlNode <> nil) do
  637.     begin
  638.       TreeNode := TN.AddChild(ParTreeNode, '');
  639.       AssignNodeToTreeNode(XmlNode, TreeNode);
  640.       AddChildNodes(XmlNode, TreeNode);
  641.       XmlNode := XmlNode.NextSibling;
  642.     end;
  643.   end;
  644.  
  645. begin
  646.   if Dest is TTreeNodes then
  647.   begin
  648.     TN := TTreeNodes(Dest);
  649.     TV := TTreeView(TN.Owner);
  650.     TV.SortType := stNone;
  651.     TV.ReadOnly := True;
  652.     if TV.Images = nil then
  653.       TV.Images := TCustomImageList.Create(TV);
  654.     TV.Images.Clear;
  655.     TV.Images.GetResource(rtBitmap,
  656.         'XMLTREEVIEWNODES', 0, [], 0);
  657.     TV.Images.BkColor := clBlack;
  658.     TN.BeginUpdate;
  659.     TreeNode := TN.AddChild(nil, '');
  660.     AssignNodeToTreeNode(Self, TreeNode);
  661.     AddChildNodes(Self, TreeNode);
  662.     TN.EndUpdate;
  663.   end
  664.   else
  665.     inherited AssignTo(Dest);
  666. end;
  667.  
  668. procedure TXmlDDocument.Clear;
  669. var
  670.   Node: TXmlDNode;
  671.   NextNode: TXmlDNode;
  672. begin
  673.   Node := FFirstChild;
  674.   while (Node <> nil) do
  675.   begin
  676.     NextNode := Node.FNextSibling;
  677.     Node.Free;
  678.     Node := NextNode;
  679.   end;
  680.   FFirstChild := nil;
  681.   FLastChild := nil;
  682.   FDocumentElement := nil;
  683. end;
  684.  
  685. function TXmlDDocument.CloneNode(RecurseChildren: Boolean): TXmlDNode;
  686. var
  687.   Clone: TXmlDDocument;
  688. begin
  689.   Clone := TXmlDDocument.Create;
  690.   if RecurseChildren then
  691.     Clone.CloneChildren(Self);
  692.   Result := Clone;
  693. end;
  694.  
  695. constructor TXmlDDocument.Create;
  696. begin
  697.   inherited Create;
  698.   FNodeType := xntDocument;
  699.   FAttrCharEntities := [ceQuot, ceAmp];
  700.   FTextCharEntities := [ceLt, ceAmp];
  701. end;
  702.  
  703. function TXmlDDocument.CreateCDATASection(
  704.   const Text: String): TXmlDCDATASection;
  705. begin
  706.   Result := TXmlDCDATASection.Create;
  707.   Result.NodeValue := Text;
  708. end;
  709.  
  710. function TXmlDDocument.CreateComment(const Text: String): TXmlDComment;
  711. begin
  712.   Result := TXmlDComment.Create;
  713.   Result.NodeValue := Text;
  714. end;
  715.  
  716. function TXmlDDocument.CreateElement(
  717.   const TagName: TXmlName): TXmlDElement;
  718. begin
  719.   Result := TXmlDElement.Create;
  720.   Result.NodeName := TagName;
  721. end;
  722.  
  723. function TXmlDDocument.CreateElement(
  724.   const TagName: TXmlName; const Data: String): TXmlDElement;
  725. begin
  726.   Result := TXmlDElement.Create;
  727.   Result.NodeName := TagName;
  728.   if Data <> '' then
  729.     Result.AppendChild(OwnerDocument.CreateTextNode(Data));
  730. end;
  731.  
  732. function TXmlDDocument.CreateElement(const TagName: TXmlName;
  733.   const Data: String; const AttrName: TXmlName;
  734.   const AttrValue: String): TXmlDElement;
  735. begin
  736.   Result := TXmlDElement.Create;
  737.   Result.NodeName := TagName;
  738.   if AttrName <> '' then
  739.     Result.FAttrList.Values[AttrName] := AttrValue;
  740.   if Data <> '' then
  741.     Result.AppendChild(OwnerDocument.CreateTextNode(Data));
  742. end;
  743.  
  744. function TXmlDDocument.CreateElement(const TagName: TXmlName;
  745.   const Data: String; const AttrNames: array of TXmlName;
  746.   const AttrValues: array of String): TXmlDElement;
  747. var
  748.   I:  Integer;
  749. begin
  750.   if (Low(AttrNames) <> Low(AttrValues)) or
  751.       (High(AttrNames) <> High(AttrValues)) then
  752.     raise EXmlDError.Create('Invalid CreateElement call');
  753.   Result := TXmlDElement.Create;
  754.   Result.NodeName := TagName;
  755.   for I := Low(AttrNames) to High(AttrNames) do
  756.     if AttrNames[I] <> '' then
  757.       Result.FAttrList.Values[AttrNames[I]] := AttrValues[I];
  758.   if Data <> '' then
  759.     Result.AppendChild(OwnerDocument.CreateTextNode(Data));
  760. end;
  761.  
  762. function TXmlDDocument.CreateTextNode(const Text: String): TXmlDText;
  763. begin
  764.   Result := TXmlDText.Create;
  765.   Result.NodeValue := Text;
  766. end;
  767.  
  768. procedure TXmlDDocument.DecodePrologAttrs(S: String);
  769. var
  770.   I: Integer;
  771. begin
  772.   I := Pos(' ', S);
  773.   while I > 0 do
  774.   begin
  775.     FAttrList.Add(StringReplace(
  776.         Copy(S, 1, I - 1), '"', '', [rfReplaceAll]));
  777.     S := TrimLeft(Copy(S, I + 1, $7FFF));
  778.     I := Pos(' ', S);
  779.   end;
  780.   FAttrList.Add(StringReplace(S, '"', '', [rfReplaceAll]));
  781. end;
  782.  
  783. procedure TXmlDDocument.InsertBefore(NewNode, ThisNode: TXmlDNode);
  784. begin
  785.   if NewNode.NodeType = xntElement then
  786.   begin
  787.     if FDocumentElement <> nil then
  788.       raise EXmlDError.Create('Second document element add attempted');
  789.     FDocumentElement := TXmlDElement(NewNode);
  790.   end;
  791.   inherited InsertBefore(NewNode, ThisNode);
  792. end;
  793.  
  794. procedure TXmlDDocument.LoadAttributes(Node: TXmlDElement;
  795.   DOMNode: IXMLDOMNode);
  796. var
  797.   I:  Integer;
  798.   Attributes: IXMLDOMNamedNodeMap;
  799.   Item: IXMLDOMNode;
  800. begin
  801.   Attributes := DOMNode.attributes;
  802.   for I := 0 to (Attributes.length - 1) do
  803.   begin
  804.     Item := Attributes[I];
  805.     Node.FAttrList[Item.nodeName] := Item.nodeValue;
  806.   end;
  807. end;
  808.  
  809. procedure TXmlDDocument.LoadChildNodes(ParNode: TXmlDNode;
  810.   ParDOMNode: IXMLDOMNode);
  811. var
  812.   ChildDOMNode: IXMLDOMNode;
  813.   NewNode:  TXmlDNode;
  814. begin
  815.   ChildDOMNode := ParDOMNode.firstChild;
  816.   while ChildDOMNode <> nil do
  817.   begin
  818.     NewNode := nil;
  819.     case ChildDOMNode.nodeType of
  820.       NODE_ELEMENT:
  821.       begin
  822.         NewNode := CreateElement(ChildDOMNode.nodeName);
  823.         LoadAttributes(TXmlDElement(NewNode), ChildDOMNode);
  824.       end;
  825.       NODE_TEXT:
  826.         NewNode := CreateTextNode(ChildDOMNode.nodeValue);
  827.       NODE_CDATA_SECTION:
  828.         NewNode := CreateCDataSection(ChildDOMNode.nodeValue);
  829.       NODE_PROCESSING_INSTRUCTION:
  830.         DecodePrologAttrs(ChildDOMNode.nodeValue);
  831.       NODE_COMMENT:
  832.         NewNode := CreateComment(ChildDOMNode.nodeValue);
  833.       NODE_DOCUMENT_TYPE:
  834.         TXmlDDocument(ParNode).DocumentTypeDefinition :=
  835.             ChildDOMNode.xml;
  836.       else
  837.         if not DiscardUnsupportedItems then
  838.           raise EXmlDError('XML document contains unsupported ' +
  839.               'node type of ' + ChildDOMNode.nodeTypeString);
  840.     end;
  841.     if (NewNode <> nil) and (ParNode <> nil) then
  842.       ParNode.AppendChild(NewNode);
  843.     LoadChildNodes(NewNode, ChildDOMNode);
  844.     ChildDOMNode := ChildDOMNode.NextSibling;
  845.   end;
  846. end;
  847.  
  848. procedure TXmlDDocument.LoadFromDOMDocument(Doc: IXMLDOMDocument);
  849. var
  850.   Err:  IXMLDOMParseError;
  851. begin
  852.   Clear;
  853.   Err := Doc.parseError;
  854.   if Err.errorCode <> 0 then
  855.     raise EXmlDParseError.Create(Err);
  856.   NodeName := Doc.nodeName;
  857.   LoadChildNodes(Self, Doc);
  858. end;
  859.  
  860. procedure TXmlDDocument.LoadFromFile(const FileName: String;
  861.   ValidateOnParse, DiscardUnsupportedItems: Boolean);
  862. var
  863.   Doc:  IXMLDOMDocument;
  864. begin
  865.   Doc := CoDOMDocument.Create;
  866.   Doc.validateOnParse := ValidateOnParse;
  867.   Self.DiscardUnsupportedItems := DiscardUnsupportedItems;
  868.   Doc.load(FileName);
  869.   LoadFromDOMDocument(Doc);
  870. end;
  871.  
  872. procedure TXmlDDocument.LoadFromStream(Stream: TStream; ValidateOnParse,
  873.   DiscardUnsupportedItems: Boolean);
  874. var
  875.   Doc:  IXMLDOMDocument;
  876.   SS: TStringStream;
  877. begin
  878.   Doc := CoDOMDocument.Create;
  879.   Doc.validateOnParse := ValidateOnParse;
  880.   Self.DiscardUnsupportedItems := DiscardUnsupportedItems;
  881.   if Stream is TStringStream then
  882.     SS := TStringStream(Stream)
  883.   else
  884.   begin
  885.     SS := TStringStream.Create('');
  886.     SS.CopyFrom(Stream, Stream.Size);
  887.   end;
  888.   SS.Position := 0;
  889.   Doc.loadXML(PChar(SS.DataString));
  890.   LoadFromDOMDocument(Doc);
  891.   if SS <> Stream then
  892.     SS.Free
  893.   else
  894.     SS.Position := 0;
  895. end;
  896.  
  897. function TXmlDDocument.RemoveChild(ThisNode: TXmlDNode): TXmlDNode;
  898. begin
  899.   if ThisNode = FDocumentElement then
  900.     FDocumentElement := nil;
  901.   Result := inherited RemoveChild(ThisNode);
  902. end;
  903.  
  904. function TXmlDDocument.ReplaceChild(NewNode,
  905.   OldNode: TXmlDNode): TXmlDNode;
  906. begin
  907.   if OldNode = FDocumentElement then
  908.     FDocumentElement := nil;
  909.   if NewNode.NodeType = xntElement then
  910.     FDocumentElement := TXmlDElement(NewNode);
  911.   Result := inherited ReplaceChild(NewNode, OldNode);
  912. end;
  913.  
  914. procedure TXmlDDocument.SaveToFile(const FileName: String;
  915.     FormattedForPrint: Boolean);
  916. var
  917.   Stream: TStream;
  918. begin
  919.   Stream := TFileStream.Create(FileName, fmCreate);
  920.   try
  921.     SaveToStream(Stream);
  922.   finally
  923.     Stream.Free;
  924.   end;
  925. end;
  926.  
  927. procedure TXmlDDocument.SaveToStream(Stream: TStream;
  928.     FormattedForPrint: Boolean = False);
  929. begin
  930.   WriteToStream(Stream, FormattedForPrint);
  931. end;
  932.  
  933. procedure TXmlDDocument.WritePrologToStream(Stream: TStream);
  934. var
  935.   S:  String;
  936.   AttrVal:  String;
  937. begin
  938.   S := '<?xml version=';
  939.   AttrVal := FAttrList['version'];
  940.   if AttrVal <> '' then
  941.     AppendStr(S, '"' + AttrVal + '"')
  942.   else
  943.     AppendStr(S, '"1.0"');
  944.   AttrVal := FAttrList['encoding'];
  945.   if AttrVal <> '' then
  946.     AppendStr(S, ' encoding=' + '"' + AttrVal + '"');
  947.   AttrVal := FAttrList['standalone'];
  948.   if AttrVal <> '' then
  949.     AppendStr(S, ' standalone=' + '"' + AttrVal + '"');
  950.   AppendStr(S, '?>');
  951.   Stream.WriteBuffer(Pointer(S)^, Length(S));
  952. end;
  953.  
  954. procedure TXmlDDocument.WriteToStream(Stream: TStream;
  955.     FormattedForPrint: Boolean);
  956. begin
  957.   WritePrologToStream(Stream);
  958.   if FormattedForPrint then
  959.     WriteFormattedSuffix(Stream);
  960.   if FDocumentTypeDefinition <> '' then
  961.   begin
  962.     Stream.WriteBuffer(Pointer(FDocumentTypeDefinition)^,
  963.         Length(FDocumentTypeDefinition));
  964.     if FormattedForPrint then
  965.       WriteFormattedSuffix(Stream);
  966.   end;
  967.   WriteChildrenToStream(Stream, FormattedForPrint);
  968. end;
  969.  
  970. { TXmlDElement }
  971.  
  972. function TXmlDElement.CloneNode(RecurseChildren: Boolean): TXmlDNode;
  973. var
  974.   Clone:  TXmlDElement;
  975. begin
  976.   Clone := TXmlDElement.Create;
  977.   Clone.FNodeName := FNodeName;
  978.   Clone.FAttrList.Assign(FAttrList);
  979.   if RecurseChildren then
  980.     Clone.CloneChildren(Self);
  981.   Result := Clone;
  982. end;
  983.  
  984. constructor TXmlDElement.Create;
  985. begin
  986.   inherited Create;
  987.   FNodeType := xntElement;
  988. end;
  989.  
  990. function TXmlDElement.GetNodeName: TXmlName;
  991. begin
  992.   Result := FNodeName;
  993. end;
  994.  
  995. procedure TXmlDElement.SetNodeName(const Value: TXmlName);
  996. begin
  997.   FNodeName := Value;
  998. end;
  999.  
  1000. procedure TXmlDElement.WriteToStream(Stream: TStream;
  1001.     FormattedForPrint: Boolean);
  1002. var
  1003.   S:  String;
  1004.   Formatted: Boolean;
  1005. begin
  1006.   Formatted := FormattedForPrint;
  1007.   if Formatted then
  1008.   begin
  1009.     if (FFirstChild <> nil) and (FFirstChild = FLastChild) and
  1010.         (FFirstChild.NodeType = xntText) and
  1011.         (Length(FFirstChild.NodeValue) < 48) then
  1012.       Formatted := False;
  1013.     WriteFormattedPrefix(Stream);
  1014.   end;
  1015.   S := '<' + FNodeName;
  1016.   Stream.WriteBuffer(Pointer(S)^, Length(S));
  1017.   if FAttrList.Count > 0 then
  1018.     FAttrList.WriteToStream(Stream);
  1019.   if FFirstChild <> nil then
  1020.   begin
  1021.     S := '>';
  1022.     Stream.WriteBuffer(Pointer(S)^, 1);
  1023.     if Formatted then
  1024.       WriteFormattedSuffix(Stream);
  1025.   end;
  1026.   if FFirstChild = nil then
  1027.     S := '/>'
  1028.   else
  1029.   begin
  1030.     WriteChildrenToStream(Stream, Formatted);
  1031.     if Formatted then
  1032.       WriteFormattedPrefix(Stream);
  1033.     S := '</' + FNodeName + '>';
  1034.   end;
  1035.   Stream.WriteBuffer(Pointer(S)^, Length(S));
  1036.   if FormattedForPrint then
  1037.     WriteFormattedSuffix(Stream);
  1038. end;
  1039.  
  1040. { TXmlDText }
  1041.  
  1042. function TXmlDText.CloneNode(RecurseChildren: Boolean): TXmlDNode;
  1043. begin
  1044.   Result := TXmlDText.Create;
  1045.   Result.NodeValue := NodeValue;
  1046. end;
  1047.  
  1048. constructor TXmlDText.Create;
  1049. begin
  1050.   inherited Create;
  1051.   FNodeType := xntText;
  1052. end;
  1053.  
  1054. procedure TXmlDText.WriteToStream(Stream: TStream;
  1055.     FormattedForPrint: Boolean);
  1056. var
  1057.   S:  String;
  1058.   Skip: Boolean;
  1059.   D:  TXmlDDocument;
  1060. begin
  1061.   if FormattedForPrint then
  1062.     WriteFormattedPrefix(Stream);
  1063.   S := FValue;
  1064.   Skip := False;
  1065.   D := OwnerDocument;
  1066.   if Assigned(D.FOnOutputTextValue) then
  1067.     D.FOnOutputTextValue(Self, S, Skip);
  1068.   if (not Skip) and (D.FTextCharEntities <> []) then
  1069.     S := CharEntitiesReplace(S, D.FTextCharEntities);
  1070.   Stream.WriteBuffer(Pointer(S)^, Length(S));
  1071.   if FormattedForPrint then
  1072.     WriteFormattedSuffix(Stream);
  1073. end;
  1074.  
  1075. { TXmlDComment }
  1076.  
  1077. function TXmlDComment.CloneNode(RecurseChildren: Boolean): TXmlDNode;
  1078. begin
  1079.   Result := TXmlDComment.Create;
  1080.   Result.NodeValue := NodeValue;
  1081. end;
  1082.  
  1083. constructor TXmlDComment.Create;
  1084. begin
  1085.   inherited Create;
  1086.   FNodeType := xntComment;
  1087. end;
  1088.  
  1089. procedure TXmlDComment.WriteToStream(Stream: TStream;
  1090.   FormattedForPrint: Boolean);
  1091. var
  1092.   S:  String;
  1093. begin
  1094.   if FormattedForPrint then
  1095.     WriteFormattedPrefix(Stream);
  1096.   S := '<!--' + FValue + '-->';
  1097.   Stream.WriteBuffer(Pointer(S)^, Length(S));
  1098.   if FormattedForPrint then
  1099.     WriteFormattedSuffix(Stream);
  1100. end;
  1101.  
  1102. { TXmlCDATASection }
  1103.  
  1104. function TXmlDCDATASection.CloneNode(RecurseChildren: Boolean): TXmlDNode;
  1105. begin
  1106.   Result := TXmlDCDATASection.Create;
  1107.   Result.NodeValue := NodeValue;
  1108. end;
  1109.  
  1110. constructor TXmlDCDATASection.Create;
  1111. begin
  1112.   inherited Create;
  1113.   FNodeType := xntCDATASection;
  1114. end;
  1115.  
  1116. procedure TXmlDCDATASection.WriteToStream(Stream: TStream;
  1117.     FormattedForPrint: Boolean);
  1118. var
  1119.   S:  String;
  1120. begin
  1121.   if FormattedForPrint then
  1122.     WriteFormattedPrefix(Stream);
  1123.   S := '<![CDATA[' + FValue + ']]>';
  1124.   Stream.WriteBuffer(Pointer(S)^, Length(S));
  1125.   if FormattedForPrint then
  1126.     WriteFormattedSuffix(Stream);
  1127. end;
  1128.  
  1129. { TXmlDAttrList }
  1130.  
  1131. function TXmlDAttrList.Add(const S: String): Integer;
  1132. begin
  1133.   Result := List.Add(S);
  1134. end;
  1135.  
  1136. procedure TXmlDAttrList.Assign(Source: TPersistent);
  1137. begin
  1138.   if Source is TXmlDAttrList then
  1139.     List.Assign(TXmlDAttrList(Source).List);
  1140. end;
  1141.  
  1142. procedure TXmlDAttrList.Clear;
  1143. begin
  1144.   List.Clear;
  1145. end;
  1146.  
  1147. constructor TXmlDAttrList.Create;
  1148. begin
  1149.   inherited Create;
  1150.   List := TStringList.Create;
  1151. end;
  1152.  
  1153. destructor TXmlDAttrList.Destroy;
  1154. begin
  1155.   List.Free;
  1156.   inherited Destroy;
  1157. end;
  1158.  
  1159. function TXmlDAttrList.GetCount: Integer;
  1160. begin
  1161.   Result := List.Count;
  1162. end;
  1163.  
  1164. function TXmlDAttrList.GetNames(Index: Integer): TXmlName;
  1165. begin
  1166.   Result := List.Names[Index];
  1167. end;
  1168.  
  1169. function TXmlDAttrList.GetValues(const Name: TXmlName): String;
  1170. begin
  1171.   Result := List.Values[Name];
  1172. end;
  1173.  
  1174. procedure TXmlDAttrList.SetValues(const Name: TXmlName;
  1175.   const Value: String);
  1176. begin
  1177.   List.Values[Name] := Value;
  1178. end;
  1179.  
  1180. procedure TXmlDAttrList.WriteToStream(Stream: TStream);
  1181. var
  1182.   I:  Integer;
  1183.   J:  Integer;
  1184.   S:  String;
  1185.   Val: String;
  1186.   Skip: Boolean;
  1187.   D:  TXmlDDocument;
  1188. begin
  1189.   D := FOwnerNode.OwnerDocument;
  1190.   for I := 0 to (List.Count - 1) do
  1191.   begin
  1192.     S := List[I];
  1193.     J := Pos('=', S);
  1194.     Val := Copy(S, J + 1, $7FFF);
  1195.     Skip := False;
  1196.     if Assigned(D.FOnOutputAttrValue) then
  1197.       D.FOnOutputAttrValue(Self, Val, Skip);
  1198.     if (not Skip) and (D.FAttrCharEntities <> []) then
  1199.       Val := CharEntitiesReplace(Val, D.FAttrCharEntities);
  1200.     S := ' ' + Copy(S, 1, J) + '"' + Val + '"';
  1201.     Stream.WriteBuffer(Pointer(S)^, Length(S));
  1202.   end;
  1203. end;
  1204.  
  1205. end.
  1206.