nativexml.pas
上传用户:yjb1804
上传日期:2021-01-30
资源大小:3105k
文件大小:199k
源码类别:

Email服务器

开发平台:

Delphi

  1. function TXmlNode.FromWidestring(const W: widestring): string;
  2. begin
  3.   if Utf8Encoded then
  4.     Result := sdUnicodeToUtf8(W)
  5.   else
  6.     Result := W;
  7. end;
  8. function TXmlNode.GetAttributeByName(const AName: string): string;
  9. begin
  10.   if assigned(FAttributes) then
  11.     Result := UnEscapeString(UnQuoteString(FAttributes.Values[AName]))
  12.   else
  13.     Result := '';
  14. end;
  15. function TXmlNode.GetAttributeByNameWide(const AName: string): widestring;
  16. begin
  17.   Result := ToWidestring(GetAttributeByName(AName));
  18. end;
  19. function TXmlNode.GetAttributeCount: integer;
  20. begin
  21.   if assigned(FAttributes) then
  22.     Result := FAttributes.Count
  23.   else
  24.     Result := 0;
  25. end;
  26. function TXmlNode.GetAttributeName(Index: integer): string;
  27. begin
  28.   if (Index >= 0) and (Index < AttributeCount) then
  29.     Result := FAttributes.Names[Index];
  30. end;
  31. function TXmlNode.GetAttributePair(Index: integer): string;
  32. begin
  33.   if (Index >= 0) and (Index < AttributeCount) then
  34.     Result := FAttributes[Index];
  35. end;
  36. function TXmlNode.GetAttributeValue(Index: integer): string;
  37. var
  38.   P: integer;
  39.   S: string;
  40. begin
  41.   Result := '';
  42.   if (Index >= 0) and (Index < AttributeCount) then begin
  43.     S := FAttributes[Index];
  44.     P := AnsiPos('=', S);
  45.     if P > 0 then
  46.       Result := UnEscapeString(UnQuoteString(Copy(S, P + 1, MaxInt)));
  47.   end;
  48. end;
  49. function TXmlNode.GetAttributeValueAsInteger(Index: integer): integer;
  50. begin
  51.   Result := StrToIntDef(GetAttributeValue(Index), 0);
  52. end;
  53. function TXmlNode.GetAttributeValueAsWidestring(Index: integer): widestring;
  54. begin
  55.   Result := ToWidestring(GetAttributeValue(Index));
  56. end;
  57. function TXmlNode.GetBinaryEncoding: TBinaryEncodingType;
  58. begin
  59.   Result := xbeBinHex;
  60.   if assigned(Document) then
  61.     Result := Document.BinaryEncoding;
  62. end;
  63. function TXmlNode.GetBinaryString: string;
  64. // Get the binary contents of this node as Base64 and return it as a string
  65. var
  66.   OldEncoding: TBinaryEncodingType;
  67. {$IFDEF CLR}
  68.   Buffer: TBytes;
  69. {$ENDIF}
  70. begin
  71.   // Set to base64
  72.   OldEncoding := BinaryEncoding;
  73.   try
  74.     BinaryEncoding := xbeBase64;
  75.     {$IFDEF CLR}
  76.     SetLength(Buffer, BufferLength);
  77.     if length(Buffer) > 0 then
  78.       BufferRead(Buffer, length(Buffer));
  79.     Result := Buffer;
  80.     {$ELSE}
  81.     SetLength(Result, BufferLength);
  82.     if length(Result) > 0 then
  83.       BufferRead(Result[1], length(Result));
  84.     {$ENDIF}
  85.   finally
  86.     BinaryEncoding := OldEncoding;
  87.   end;
  88. end;
  89. function TXmlNode.GetCascadedName: string;
  90. // Return the name+index and all predecessors with underscores to separate, in
  91. // order to get a unique reference that can be used in filenames
  92. var
  93.   AName: string;
  94. begin
  95.   AName :=  Format('%s%.4d', [Name, StrToIntDef(AttributeByName['Index'], 0)]);
  96.   if assigned(Parent) then
  97.     Result := Format('%s_%s', [Parent.CascadedName, AName])
  98.   else
  99.     Result := AName;
  100. end;
  101. function TXmlNode.GetFullPath: string;
  102. // GetFullpath will return the complete path of the node from the root, e.g.
  103. // /Root/SubNode1/SubNode2/ThisNode
  104. begin
  105.   Result := '/' + Name;
  106.   if Treedepth > 0 then
  107.     // Recursive call
  108.     Result := Parent.GetFullPath + Result;
  109. end;
  110. function TXmlNode.GetIndent: string;
  111. var
  112.   i: integer;
  113. begin
  114.   if assigned(Document) then
  115.     case Document.XmlFormat of
  116.     xfCompact: Result := '';
  117.     xfReadable:
  118.       for i := 0 to TreeDepth - 1 do
  119.         Result := Result + Document.IndentString;
  120.     end
  121.   else
  122.     Result := ''
  123. end;
  124. function TXmlNode.GetLineFeed: string;
  125. begin
  126.   if assigned(Document) then
  127.     case Document.XmlFormat of
  128.     xfCompact: Result := '';
  129.     xfReadable: Result := #13#10;
  130.     else
  131.       Result := #10;
  132.     end
  133.   else
  134.     Result := '';
  135. end;
  136. function TXmlNode.GetNodeCount: integer;
  137. begin
  138.   if Assigned(FNodes) then
  139.     Result := FNodes.Count
  140.   else
  141.     Result := 0;
  142. end;
  143. function TXmlNode.GetNodes(Index: integer): TXmlNode;
  144. begin
  145.   if (Index >= 0) and (Index < NodeCount) then
  146.     Result := TXmlNode(FNodes[Index])
  147.   else
  148.     Result := nil;
  149. end;
  150. function TXmlNode.GetTotalNodeCount: integer;
  151. var
  152.   i: integer;
  153. begin
  154.   Result := NodeCount;
  155.   for i := 0 to NodeCount - 1 do
  156.     inc(Result, Nodes[i].TotalNodeCount);
  157. end;
  158. function TXmlNode.GetTreeDepth: integer;
  159. begin
  160.   Result := -1;
  161.   if assigned(Parent) then
  162.     Result := Parent.TreeDepth + 1;
  163. end;
  164. function TXmlNode.GetValueAsBool: boolean;
  165. begin
  166.   Result := sdStringToBool(FValue);
  167. end;
  168. function TXmlNode.GetValueAsDateTime: TDateTime;
  169. begin
  170.   Result := sdDateTimeFromString(ValueAsString);
  171. end;
  172. function TXmlNode.GetValueAsFloat: double;
  173. var
  174.   Code: integer;
  175. begin
  176.   val(StringReplace(FValue, ',', '.', []), Result, Code);
  177.   if Code > 0 then
  178.     raise Exception.Create(sxeCannotConvertToFloat);
  179. end;
  180. function TXmlNode.GetValueAsInt64: int64;
  181. begin
  182.   Result := StrToInt64(FValue);
  183. end;
  184. function TXmlNode.GetValueAsInteger: integer;
  185. begin
  186.   Result := StrToInt(FValue);
  187. end;
  188. function TXmlNode.GetValueAsString: string;
  189. begin
  190.   Result := UnEscapeString(FValue);
  191. end;
  192. function TXmlNode.GetValueAsWidestring: widestring;
  193. begin
  194.   Result := ToWidestring(ValueAsString);
  195. end;
  196. function TXmlNode.GetWriteOnDefault: boolean;
  197. begin
  198.   Result := True;
  199.   if assigned(Document) then
  200.     Result := Document.WriteOnDefault;
  201. end;
  202. function TXmlNode.HasAttribute(const AName: string): boolean;
  203. var
  204.   i: integer;
  205. begin
  206.   Result := False;
  207.   for i := 0 to AttributeCount - 1 do
  208.     if AnsiCompareText(AName, AttributeName[i]) = 0 then begin
  209.       Result := True;
  210.       exit;
  211.     end;
  212. end;
  213. function TXmlNode.IndexInParent: integer;
  214. // Retrieve our index in the parent's nodelist
  215. var
  216.   i: integer;
  217. begin
  218.   Result := -1;
  219.   if assigned(Parent) then
  220.     for i := 0 to Parent.NodeCount - 1 do
  221.       if Self = Parent.Nodes[i] then begin
  222.         Result := i;
  223.         exit;
  224.       end;
  225. end;
  226. function TXmlNode.IsClear: boolean;
  227. begin
  228.   Result := (Length(FName) = 0) and IsEmpty;
  229. end;
  230. function TXmlNode.IsEmpty: boolean;
  231. begin
  232.   Result := (Length(FValue) = 0) and (NodeCount = 0) and (AttributeCount = 0);
  233. end;
  234. function TXmlNode.IsEqualTo(ANode: TXmlNode; Options: TXmlCompareOptions;
  235.   MismatchNodes: TList): boolean;
  236. var
  237.   i, AIndex: integer;
  238.   NodeResult, ChildResult: boolean;
  239. begin
  240.   // Start with a negative result
  241.   Result := False;
  242.   NodeResult := False;
  243.   if not assigned(ANode) then exit;
  244.   // Assume childs equals other node's childs
  245.   ChildResult := True;
  246.   // child node names and values - this comes first to assure the lists are filled
  247.   if (xcChildNames in Options) or (xcChildValues in Options) or (xcRecursive in Options) then
  248.     for i := 0 to NodeCount - 1 do begin
  249.       // Do child name check
  250.       AIndex := ANode.NodeIndexByName(Nodes[i].Name);
  251.       // Do we have the childnode in the other?
  252.       if AIndex < 0 then begin
  253.         // No we dont have it
  254.         if xcChildNames in Options then begin
  255.           if assigned(MismatchNodes) then MismatchNodes.Add(Nodes[i]);
  256.           ChildResult := False;
  257.         end;
  258.       end else begin
  259.         // Do child value check
  260.         if xcChildValues in Options then
  261.           if AnsiCompareText(Nodes[i].ValueAsString, ANode.Nodes[AIndex].ValueAsString) <> 0 then begin
  262.             if assigned(MismatchNodes) then MismatchNodes.Add(Nodes[i]);
  263.             ChildResult := False;
  264.           end;
  265.         // Do recursive check
  266.         if xcRecursive in Options then
  267.           if not Nodes[i].IsEqualTo(ANode.Nodes[AIndex], Options, MismatchNodes) then
  268.             ChildResult := False;
  269.       end;
  270.     end;
  271.   try
  272.     // We assume there are differences
  273.     NodeResult := False;
  274.     // Node name, type and value
  275.     if xcNodeName in Options then
  276.       if AnsiCompareText(Name, ANode.Name) <> 0 then exit;
  277.     if xcNodeType in Options then
  278.       if ElementType <> ANode.ElementType then exit;
  279.     if xcNodeValue in Options then
  280.       if AnsiCompareText(ValueAsString, ANode.ValueAsString) <> 0 then exit;
  281.     // attribute count
  282.     if xcAttribCount in Options then
  283.       if AttributeCount <> ANode.AttributeCount then exit;
  284.     // attribute names and values
  285.     if (xcAttribNames in Options) or (xcAttribValues in Options) then
  286.       for i := 0 to AttributeCount - 1 do begin
  287.         AIndex := ANode.AttributeIndexByName(AttributeName[i]);
  288.         if AIndex < 0 then
  289.           if xcAttribNames in Options then
  290.             exit
  291.           else
  292.             continue;
  293.         if xcAttribValues in Options then
  294.           if AnsiCompareText(AttributeValue[i], ANode.AttributeValue[AIndex]) <> 0 then
  295.             exit;
  296.       end;
  297.     // child node count
  298.     if xcChildCount in Options then
  299.       if NodeCount <> ANode.NodeCount then exit;
  300.     // If we arrive here, it means no differences were found, return True
  301.     NodeResult := True;
  302.   finally
  303.     Result := ChildResult and NodeResult;
  304.     if (not NodeResult) and assigned(MismatchNodes) then
  305.       MismatchNodes.Insert(0, Self);
  306.   end;
  307. end;
  308. function TXmlNode.NodeAdd(ANode: TXmlNode): integer;
  309. begin
  310.   if assigned(ANode) then begin
  311.     ANode.Parent := Self;
  312.     if not assigned(FNodes) then FNodes := TList.Create;
  313.     Result := FNodes.Add(ANode);
  314.   end else
  315.     Result := -1;
  316. end;
  317. function TXmlNode.NodeByAttributeValue(const NodeName, AttribName, AttribValue: string;
  318.   ShouldRecurse: boolean): TXmlNode;
  319. // This function returns a pointer to the first subnode that has an attribute with
  320. // name AttribName and value AttribValue.
  321. var
  322.   i: integer;
  323.   ANode: TXmlNode;
  324. begin
  325.   Result := nil;
  326.   // Find all nodes that are potential results
  327.   for i := 0 to NodeCount - 1 do begin
  328.     ANode := Nodes[i];
  329.     if (AnsiCompareText(ANode.Name, NodeName) = 0) and
  330.         ANode.HasAttribute(AttribName) and
  331.        (AnsiCompareText(ANode.AttributeByName[AttribName], AttribValue) = 0) then begin
  332.       Result := ANode;
  333.       exit;
  334.     end;
  335.     // Recursive call
  336.     if ShouldRecurse then
  337.       Result := ANode.NodeByAttributeValue(NodeName, AttribName, AttribValue, True);
  338.     if assigned(Result) then exit;
  339.   end;
  340. end;
  341. function TXmlNode.NodeByElementType(
  342.   ElementType: TXmlElementType): TXmlNode;
  343. var
  344.   i: integer;
  345. begin
  346.   Result := nil;
  347.   for i := 0 to NodeCount - 1 do
  348.     if Nodes[i].ElementType = ElementType then begin
  349.       Result := Nodes[i];
  350.       exit;
  351.     end;
  352. end;
  353. function TXmlNode.NodeByName(const AName: string): TXmlNode;
  354. var
  355.   i: integer;
  356. begin
  357.   Result := nil;
  358.   for i := 0 to NodeCount - 1 do
  359.     if AnsiCompareText(Nodes[i].Name, AName) = 0 then begin
  360.       Result := Nodes[i];
  361.       exit;
  362.     end;
  363. end;
  364. procedure TXmlNode.NodeDelete(Index: integer);
  365. begin
  366.   if (Index >= 0) and (Index < NodeCount) then begin
  367.     TXmlNode(FNodes[Index]).Free;
  368.     FNodes.Delete(Index);
  369.   end;
  370. end;
  371. procedure TXmlNode.NodeExchange(Index1, Index2: integer);
  372. begin
  373.   if (Index1 >= 0) and (Index1 < Nodecount) and
  374.      (Index2 >= 0) and (Index2 < Nodecount) then
  375.     FNodes.Exchange(Index1, Index2);
  376. end;
  377. function TXmlNode.NodeExtract(ANode: TXmlNode): TXmlNode;
  378. var
  379.   AIndex: integer;
  380. begin
  381.   // Compatibility with Delphi4
  382.   Result := nil;
  383.   if assigned(FNodes) then begin
  384.     AIndex := FNodes.IndexOf(ANode);
  385.     if AIndex >= 0 then begin
  386.       Result := ANode;
  387.       FNodes.Delete(AIndex);
  388.     end;
  389.   end;
  390. end;
  391. function TXmlNode.NodeFindOrCreate(const AName: string): TXmlNode;
  392. // Find the node with AName, and if not found, add new one
  393. begin
  394.   Result := NodeByName(AName);
  395.   if not assigned(Result) then
  396.     Result := NodeNew(AName);
  397. end;
  398. function TXmlNode.NodeIndexByName(const AName: string): integer;
  399. begin
  400.   Result := 0;
  401.   while Result < NodeCount do begin
  402.     if AnsiCompareText(Nodes[Result].Name, AName) = 0 then exit;
  403.     inc(Result);
  404.   end;
  405.   if Result = NodeCount then Result := -1;
  406. end;
  407. function TXmlNode.NodeIndexByNameFrom(const AName: string;
  408.   AFrom: integer): integer;
  409. begin
  410.   Result := AFrom;
  411.   while Result < NodeCount do begin
  412.     if AnsiCompareText(Nodes[Result].Name, AName) = 0 then exit;
  413.     inc(Result);
  414.   end;
  415.   if Result = NodeCount then Result := -1;
  416. end;
  417. function TXmlNode.NodeIndexOf(ANode: TXmlNode): integer;
  418. begin
  419.   if assigned(ANode) and assigned(FNodes) then
  420.     Result := FNodes.IndexOf(ANode)
  421.   else
  422.     Result := -1;
  423. end;
  424. procedure TXmlNode.NodeInsert(Index: integer; ANode: TXmlNode);
  425. // Insert the node ANode at location Index in the list.
  426. begin
  427.   if not assigned(ANode) then exit;
  428.   if (Index >=0) and (Index <= NodeCount) then begin
  429.     if not assigned(FNodes) then FNodes := TList.Create;
  430.     ANode.Parent := Self;
  431.     FNodes.Insert(Index, ANode);
  432.   end;
  433. end;
  434. function TXmlNode.NodeNew(const AName: string): TXmlNode;
  435. // Add a new child node and return its pointer
  436. begin
  437.   Result := Nodes[NodeAdd(TXmlNode.CreateName(Document, AName))];
  438. end;
  439. function TXmlNode.NodeNewAtIndex(Index: integer;
  440.   const AName: string): TXmlNode;
  441. // Create a new node with AName, and insert it into the subnode list at location
  442. // Index, and return a pointer to it.
  443. begin
  444.   if (Index >= 0) and (Index <= NodeCount) then begin
  445.     Result := TXmlNode.CreateName(Document, AName);
  446.     NodeInsert(Index, Result);
  447.   end else
  448.     Result := nil;
  449. end;
  450. function TXmlNode.NodeRemove(ANode: TxmlNode): integer;
  451. begin
  452.   Result := NodeIndexOf(ANode);
  453.   if Result >= 0 then
  454.     NodeDelete(Result);
  455. end;
  456. procedure TXmlNode.NodesByName(const AName: string; const AList: TList);
  457. // Fill AList with nodes that have name AName
  458. var
  459.   i: integer;
  460. begin
  461.   if not assigned(AList) then exit;
  462.   AList.Clear;
  463.   for i := 0 to NodeCount - 1 do
  464.     if AnsiCompareText(Nodes[i].Name, AName) = 0 then
  465.       AList.Add(Nodes[i]);
  466. end;
  467. procedure TXmlNode.NodesClear;
  468. var
  469.   i: integer;
  470. begin
  471.   for i := 0 to NodeCount - 1 do
  472.     TXmlNode(FNodes[i]).Free;
  473.   FreeAndNil(FNodes);
  474. end;
  475. procedure TXmlNode.ParseTag(const AValue: string; TagStart,
  476.   TagClose: integer);
  477. var
  478.   FItems: TStringList;
  479. begin
  480.   // Create a list to hold string items
  481.   FItems := TStringList.Create;
  482.   try
  483.     ParseAttributes(AValue, TagStart, TagClose, FItems);
  484.     // Determine name, attributes or value for each element type
  485.     case ElementType of
  486.     xeDeclaration:
  487.       FName := 'xml';
  488.     xeStyleSheet:
  489.       begin
  490.         FName := 'xml-stylesheet';
  491.         // We also set this as the value for use in "StyleSheetString"
  492.         ValueDirect := trim(copy(AValue, TagStart, TagClose - TagStart));
  493.       end;
  494.     else
  495.       // First item is the name - is it there?
  496.       if FItems.Count = 0 then
  497.         raise EFilerError.Create(sxeMissingElementName);
  498.       // Set the name - using the element instead of property for speed
  499.       FName := FItems[0];
  500.       FItems.Delete(0);
  501.     end;//case
  502.     // Any attributes?
  503.     if FItems.Count > 0 then begin
  504.       CheckCreateAttributesList;
  505.       FAttributes.Assign(FItems);
  506.     end;
  507.   finally
  508.     FItems.Free;
  509.   end;
  510. end;
  511. function TXmlNode.QualifyAsDirectNode: boolean;
  512. // If this node qualifies as a direct node when writing, we return True.
  513. // A direct node may have attributes, but no value or subnodes. Furhtermore,
  514. // the root node will never be displayed as a direct node.
  515. begin
  516.   Result :=
  517.     (Length(FValue) = 0) and
  518.     (NodeCount = 0) and
  519.     (ElementType = xeNormal) and
  520.     not UseFullNodes and
  521.     (TreeDepth > 0);
  522. end;
  523. function TXmlNode.ReadAttributeBool(const AName: string;
  524.   ADefault: boolean): boolean;
  525. var
  526.   AValue: string;
  527. begin
  528.   AValue := AttributeByName[AName];
  529.   try
  530.     Result := sdStringToBool(AValue);
  531.   except
  532.     Result := ADefault;
  533.   end;
  534. end;
  535. function TXmlNode.ReadAttributeFloat(const AName: string;
  536.   ADefault: double): double;
  537. var
  538.   AValue: string;
  539.   Code: integer;
  540. begin
  541.   AValue := AttributeByName[AName];
  542.   val(StringReplace(AValue, ',', '.', []), Result, Code);
  543.   if Code > 0 then
  544.     Result := ADefault;
  545. end;
  546. function TXmlNode.ReadAttributeInteger(const AName: string;
  547.   ADefault: integer): integer;
  548. begin
  549.   Result := StrToIntDef(AttributeByName[AName], ADefault);
  550. end;
  551. function TXmlNode.ReadAttributeInt64(const AName: string;
  552.   ADefault: int64): int64;
  553. begin
  554.   Result := StrToInt64Def(AttributeByName[AName], ADefault);
  555. end;
  556. function TXmlNode.ReadAttributeString(const AName: string; const ADefault: string): string;
  557. begin
  558.   Result := AttributeByName[AName];
  559.   if length(Result) = 0 then
  560.     Result := ADefault;
  561. end;
  562. function TXmlNode.ReadBool(const AName: string;
  563.   ADefault: boolean): boolean;
  564. var
  565.   AIndex: integer;
  566. begin
  567.   Result := ADefault;
  568.   AIndex := NodeIndexByName(AName);
  569.   if AIndex >= 0 then
  570.     Result := Nodes[AIndex].ValueAsBoolDef(ADefault);
  571. end;
  572. {$IFDEF USEGRAPHICS}
  573. procedure TXmlNode.ReadBrush(const AName: string; ABrush: TBrush);
  574. var
  575.   AChild: TXmlNode;
  576. begin
  577.   AChild := NodeByName(AName);
  578.   if assigned(AChild) then with AChild do begin
  579.     // Read values
  580.     ABrush.Color  := ReadColor('Color', clWhite);
  581.     ABrush.Style  := TBrushStyle(ReadInteger('Style', integer(bsSolid)));
  582.   end else begin
  583.     // Defaults
  584.     ABrush.Bitmap := nil;
  585.     ABrush.Color  := clWhite;
  586.     ABrush.Style  := bsSolid;
  587.   end;
  588. end;
  589. function TXmlNode.ReadColor(const AName: string; ADefault: TColor): TColor;
  590. var
  591.   AIndex: integer;
  592. begin
  593.   Result := ADefault;
  594.   AIndex := NodeIndexByName(AName);
  595.   if AIndex >= 0 then
  596.     Result := StrToInt(Nodes[AIndex].ValueAsString);
  597. end;
  598. {$ENDIF}
  599. function TXmlNode.ReadDateTime(const AName: string;
  600.   ADefault: TDateTime): TDateTime;
  601. // Date MUST always be written in this format:
  602. // YYYY-MM-DD (if just date) or
  603. // YYYY-MM-DDThh:mm:ss.sssZ (if date and time. The Z stands for universal time
  604. // zone. Since Delphi's TDateTime does not give us a clue about the timezone,
  605. // this is the easiest solution)
  606. // This format SHOULD NOT be changed, to avoid all kinds of
  607. // conversion errors in future.
  608. // This format is compatible with the W3C date/time specification as found here:
  609. // http://www.w3.org/TR/NOTE-datetime
  610. begin
  611.   Result := sdDateTimeFromStringDefault(ReadString(AName, ''), ADefault);
  612. end;
  613. function TXmlNode.ReadFloat(const AName: string; ADefault: double): double;
  614. var
  615.   AIndex: integer;
  616. begin
  617.   Result := ADefault;
  618.   AIndex := NodeIndexByName(AName);
  619.   if AIndex >= 0 then
  620.     Result := Nodes[AIndex].ValueAsFloatDef(ADefault);
  621. end;
  622. {$IFDEF USEGRAPHICS}
  623. procedure TXmlNode.ReadFont(const AName: string; AFont: TFont);
  624. var
  625.   AChild: TXmlNode;
  626. begin
  627.   AChild := NodeByName(AName);
  628.   AFont.Style := [];
  629.   if assigned(AChild) then with AChild do begin
  630.     // Read values
  631.     AFont.Name  := ReadString('Name', 'Arial');
  632.     AFont.Color := ReadColor('Color', clBlack);
  633.     AFont.Size  := ReadInteger('Size', 14);
  634.     if ReadBool('Bold', False)      then AFont.Style := AFont.Style + [fsBold];
  635.     if ReadBool('Italic', False)    then AFont.Style := AFont.Style + [fsItalic];
  636.     if ReadBool('Underline', False) then AFont.Style := AFont.Style + [fsUnderline];
  637.     if ReadBool('Strikeout', False) then AFont.Style := AFont.Style + [fsStrikeout];
  638.   end else begin
  639.     // Defaults
  640.     AFont.Name  := 'Arial';
  641.     AFont.Color := clBlack;
  642.     AFont.Size  := 14;
  643.   end;
  644. end;
  645. {$ENDIF}
  646. procedure TXmlNode.ReadFromStream(S: TStream);
  647. // Read the node from the starting "<" until the closing ">" from the stream in S.
  648. // This procedure also calls OnNodeNew and OnNodeLoaded events
  649. var
  650.   Ch: Char;
  651.   i: integer;
  652.   ATagIndex: integer;
  653.   AValue: string;
  654.   ALength: integer;
  655.   ANode: TXmlNode;
  656.   ANodeValue: string;
  657.   AValuePos, AValueLength: integer;
  658.   AClose: integer;
  659.   HasCR: boolean;
  660.   HasSubtags: boolean;
  661.   Words: TStringList;
  662.   IsDirect: boolean;
  663.   Reader: TsdSurplusReader;
  664. // local
  665. procedure AddCharDataNode;
  666. var
  667.   AValue: string;
  668.   ANode: TXmlNode;
  669. begin
  670.   // Add all text up till now as xeCharData
  671.   if AValuePos > 0 then begin
  672.     AValue := copy(ANodeValue, 1, AValuePos);
  673.     if length(trim(AValue)) > 0 then begin
  674.       ANode := TXmlNode.CreateType(Document, xeCharData);
  675.       ANode.ValueDirect := AValue;
  676.       NodeAdd(ANode);
  677.     end;
  678.     AValuePos := 0;
  679.   end;
  680. end;
  681. // Main
  682. begin
  683.   // Check if we aborted parsing
  684.   if assigned(Document) and Document.AbortParsing then exit;
  685.   // Initial reserve textual value: just 80 characters which is OK for most short values
  686.   AValuePos := 0;
  687.   AValueLength := 80;
  688.   SetLength(ANodeValue, AValueLength);
  689.   HasCR := False;
  690.   HasSubTags := False;
  691.   Reader := TsdSurplusReader.Create(S);
  692.   try
  693.     // Trailing blanks/controls chars?
  694.     if not Reader.ReadCharSkipBlanks(Ch) then exit;
  695.     // What is it?
  696.     if Ch = '<' then begin
  697.       // A tag - which one?
  698.       ATagIndex := ReadOpenTag(Reader);
  699.       if ATagIndex >= 0 then begin
  700.         try
  701.           ElementType := cTags[ATagIndex].FStyle;
  702.           case ElementType of
  703.           xeNormal, xeDeclaration, xeStyleSheet:
  704.             begin
  705.               // These tags we will process
  706.               ReadStringFromStreamUntil(Reader, cTags[ATagIndex].FClose, AValue, True);
  707.               ALength := length(AValue);
  708.               // Is it a direct tag?
  709.               IsDirect := False;
  710.               if (ElementType = xeNormal) and (ALength > 0) then
  711.                 if AValue[ALength] = '/' then begin
  712.                   dec(ALength);
  713.                   IsDirect := True;
  714.                 end;
  715.               ParseTag(AValue, 1, ALength + 1);
  716.               // Here we know our name so good place to call OnNodeNew event
  717.               if assigned(Document) then begin
  718.                 Document.DoNodeNew(Self);
  719.                 if Document.AbortParsing then exit;
  720.               end;
  721.               // Now the tag can be a direct close - in that case we're finished
  722.               if IsDirect or (ElementType in [xeDeclaration, xeStyleSheet]) then exit;
  723.               // Process rest of tag
  724.               repeat
  725.                 // Read character from stream
  726.                 if S.Read(Ch, 1) <> 1 then
  727.                   raise EFilerError.CreateFmt(sxeMissingCloseTag, [Name]);
  728.                 // Is there a subtag?
  729.                 if Ch = '<' then begin
  730.                   if not Reader.ReadCharSkipBlanks(Ch) then
  731.                     raise EFilerError.CreateFmt(sxeMissingDataAfterGreaterThan, [Name]);
  732.                   if Ch = '/' then begin
  733.                     // This seems our closing tag
  734.                     if not ReadStringFromStreamUntil(Reader, '>', AValue, True) then
  735.                       raise EFilerError.CreateFmt(sxeMissingLessThanInCloseTag, [Name]);
  736.                     if AnsiCompareText(trim(AValue), Name) <> 0 then
  737.                       raise EFilerError.CreateFmt(sxeIncorrectCloseTag, [Name]);
  738.                     AValue := '';
  739.                     break;
  740.                   end else begin
  741.                     // Add all text up till now as xeCharData
  742.                     AddCharDataNode;
  743.                     // Reset the HasCR flag if we add node, we only want to detect
  744.                     // the CR after last subnode
  745.                     HasCR := False;
  746.                     // This is a subtag... so create it and let it process
  747.                     HasSubTags := True;
  748.                     S.Seek(-2, soCurrent);
  749.                     ANode := TXmlNode.Create(Document);
  750.                     NodeAdd(ANode);
  751.                     ANode.ReadFromStream(S);
  752.                     // Check for dropping comments
  753.                     if assigned(Document) and Document.DropCommentsOnParse and
  754.                        (ANode.ElementType = xeComment) then
  755.                       NodeDelete(NodeIndexOf(ANode));
  756.                   end;
  757.                 end else begin
  758.                   // If we detect a CR we will set the flag. This will signal the fact
  759.                   // that this XML file was saved with xfReadable
  760.                   if Ch = #13 then HasCR := True;
  761.                   // Add the character to the node value buffer.
  762.                   inc(AValuePos);
  763.                   if AValuePos > AValueLength then begin
  764.                     inc(AValueLength, cNodeValueBuf);
  765.                     SetLength(ANodeValue, AValueLength);
  766.                   end;
  767.                   ANodeValue[AValuePos] := Ch;
  768.                 end;
  769.               until False;
  770.               // Add all text up till now as xeText
  771.               AddCharDataNode;
  772.               // Check CharData nodes, remove trailing CRLF + indentation if we
  773.               // were in xfReadable mode
  774.               if HasSubtags and HasCR then begin
  775.                 for i := 0 to NodeCount - 1 do
  776.                   if Nodes[i].ElementType = xeCharData then begin
  777.                     AClose := length(Nodes[i].FValue);
  778.                     while (AClose > 0) and (Nodes[i].FValue[AClose] in [#10, #13, ' ']) do
  779.                       dec(AClose);
  780.                     Nodes[i].FValue := copy(Nodes[i].FValue, 1, AClose);
  781.                   end;
  782.               end;
  783.               // If the first node is xeCharData we use it as ValueDirect
  784.               if NodeCount > 0 then
  785.                 if Nodes[0].ElementType = xeCharData then begin
  786.                   ValueDirect := Nodes[0].ValueDirect;
  787.                   NodeDelete(0);
  788.                 end;
  789.             end;
  790.           xeDocType:
  791.             begin
  792.               Name := 'DTD';
  793.               if assigned(Document) then begin
  794.                 Document.DoNodeNew(Self);
  795.                 if Document.AbortParsing then exit;
  796.               end;
  797.               // Parse DTD
  798.               if assigned(Document) then Document.ParseDTD(Self, S);
  799.             end;
  800.           xeElement, xeAttList, xeEntity, xeNotation:
  801.             begin
  802.               // DTD elements
  803.               ReadStringFromStreamWithQuotes(S, cTags[ATagIndex].FClose, AValue);
  804.               ALength := length(AValue);
  805.               Words := TStringList.Create;
  806.               try
  807.                 ParseAttributes(AValue, 1, ALength + 1, Words);
  808.                 if Words.Count > 0 then begin
  809.                   Name := Words[0];
  810.                   Words.Delete(0);
  811.                 end;
  812.                 ValueDirect := trim(Words.Text);
  813.               finally
  814.                 Words.Free;
  815.               end;
  816.               if assigned(Document) then begin
  817.                 Document.DoNodeNew(Self);
  818.                 if Document.AbortParsing then exit;
  819.               end;
  820.             end;
  821.           else
  822.             case ElementType of
  823.             xeComment:  Name := 'Comment';
  824.             xeCData:    Name := 'CData';
  825.             xeExclam:   Name := 'Special';
  826.             xeQuestion: Name := 'Special';
  827.             else
  828.               Name := 'Unknown';
  829.             end;
  830.             // Here we know our name so good place to call OnNodeNew
  831.             if assigned(Document) then begin
  832.               Document.DoNodeNew(Self);
  833.               if Document.AbortParsing then exit;
  834.             end;
  835.             // In these cases just get all data up till the closing tag
  836.             ReadStringFromStreamUntil(Reader, cTags[ATagIndex].FClose, AValue, False);
  837.             ValueDirect := AValue;
  838.           end;//case
  839.         finally
  840.           // Call the OnNodeLoaded and OnProgress events
  841.           if assigned(Document) and not Document.AbortParsing then begin
  842.             Document.DoProgress(S.Position);
  843.             Document.DoNodeLoaded(Self);
  844.           end;
  845.         end;
  846.       end;
  847.     end;
  848.   finally
  849.     Reader.Free;
  850.   end;
  851. end;
  852. procedure TXmlNode.ReadFromString(const AValue: string);
  853. var
  854.   S: TStream;
  855. begin
  856.   S := TsdStringStream.Create(AValue);
  857.   try
  858.     ReadFromStream(S);
  859.   finally
  860.     S.Free;
  861.   end;
  862. end;
  863. {$IFDEF D4UP}
  864. function TXmlNode.ReadInt64(const AName: string; ADefault: int64): int64;
  865. var
  866.   AIndex: integer;
  867. begin
  868.   Result := ADefault;
  869.   AIndex := NodeIndexByName(AName);
  870.   if AIndex >= 0 then
  871.     Result := Nodes[AIndex].ValueAsInt64Def(ADefault);
  872. end;
  873. {$ENDIF}
  874. function TXmlNode.ReadInteger(const AName: string; ADefault: integer): integer;
  875. var
  876.   AIndex: integer;
  877. begin
  878.   Result := ADefault;
  879.   AIndex := NodeIndexByName(AName);
  880.   if AIndex >= 0 then
  881.     Result := Nodes[AIndex].ValueAsIntegerDef(ADefault);
  882. end;
  883. {$IFDEF USEGRAPHICS}
  884. procedure TXmlNode.ReadPen(const AName: string; APen: TPen);
  885. var
  886.   AChild: TXmlNode;
  887. begin
  888.   AChild := NodeByName(AName);
  889.   if assigned(AChild) then with AChild do begin
  890.     // Read values
  891.     APen.Color := ReadColor('Color', clBlack);
  892.     APen.Mode  := TPenMode(ReadInteger('Mode', integer(pmCopy)));
  893.     APen.Style := TPenStyle(ReadInteger('Style', integer(psSolid)));
  894.     APen.Width := ReadInteger('Width', 1);
  895.   end else begin
  896.     // Defaults
  897.     APen.Color := clBlack;
  898.     APen.Mode := pmCopy;
  899.     APen.Style := psSolid;
  900.     APen.Width := 1;
  901.   end;
  902. end;
  903. {$ENDIF}
  904. function TXmlNode.ReadString(const AName: string;
  905.   const ADefault: string): string;
  906. var
  907.   AIndex: integer;
  908. begin
  909.   Result := ADefault;
  910.   AIndex := NodeIndexByName(AName);
  911.   if AIndex >= 0 then
  912.     Result := Nodes[AIndex].ValueAsString;
  913. end;
  914. function TXmlNode.ReadWidestring(const AName: string;
  915.   const ADefault: widestring): widestring;
  916. begin
  917.   Result := ToWidestring(ReadString(AName, FromWidestring(ADefault)));
  918. end;
  919. procedure TXmlNode.ResolveEntityReferences;
  920. // Replace any entity references by the entities, and parse the new content if any
  921. // local
  922. function SplitReference(const AValue: string; var Text1, Text2: string): string;
  923. var
  924.   APos: integer;
  925. begin
  926.   Result := '';
  927.   APos := Pos('&', AValue);
  928.   Text1 := '';
  929.   Text2 := AValue;
  930.   if APos = 0 then exit;
  931.   Text1 := copy(AValue, 1, APos - 1);
  932.   Text2 := copy(AValue, APos + 1, length(AValue));
  933.   APos := Pos(';', Text2);
  934.   if APos = 0 then exit;
  935.   Result := copy(Text2, 1, APos - 1);
  936.   Text2 := copy(Text2, APos + 1, length(Text2));
  937. end;
  938. // local
  939. function ReplaceEntityReferenceByNodes(ARoot: TXmlNode; const AValue: string; var InsertPos: integer; var Text1, Text2: string): boolean;
  940. var
  941.   Reference: string;
  942.   Entity: string;
  943.   ANode: TXmlNode;
  944.   S: TStream;
  945. begin
  946.   Result := False;
  947.   Reference := SplitReference(AValue, Text1, Text2);
  948.   if (length(Reference) = 0) or not assigned(Document) then exit;
  949.   // Lookup entity references
  950.   Entity := Document.EntityByName[Reference];
  951.   // Does the entity contain markup?
  952.   if (length(Entity) > 0) and (Pos('<', Entity) > 0) then begin
  953.     S := TsdStringStream.Create(Entity);
  954.     try
  955.       while S.Position < S.Size do begin
  956.         ANode := TXmlNode.Create(Document);
  957.         ANode.ReadFromStream(S);
  958.         if ANode.IsEmpty then
  959.           ANode.Free
  960.         else begin
  961.           ARoot.NodeInsert(InsertPos, ANode);
  962.           inc(InsertPos);
  963.           Result := True;
  964.         end;
  965.       end;
  966.     finally
  967.       S.Free;
  968.     end;
  969.   end;
  970. end;
  971. // main
  972. var
  973.   i: integer;
  974.   InsertPos: integer;
  975.   Text1, Text2: string;
  976.   ANode: TXmlNode;
  977.   AValue, Reference, Replace, Entity, First, Last: string;
  978. begin
  979.   if length(FValue) > 0 then begin
  980.     // Different behaviour for xeNormal and xeCharData
  981.     if ElementType = xeNormal then begin
  982.       InsertPos := 0;
  983.       if ReplaceEntityReferenceByNodes(Self, FValue, InsertPos, Text1, Text2) then begin
  984.         FValue := Text1;
  985.         if length(trim(Text2)) > 0 then begin
  986.           ANode := TXmlNode.CreateType(Document, xeCharData);
  987.           ANode.ValueDirect := Text2;
  988.           NodeInsert(InsertPos, ANode);
  989.         end;
  990.       end;
  991.     end else if (ElementType = xeCharData) and assigned(Parent) then begin
  992.       InsertPos := Parent.NodeIndexOf(Self);
  993.       if ReplaceEntityReferenceByNodes(Parent, FValue, InsertPos, Text1, Text2) then begin
  994.         FValue := Text1;
  995.         if length(trim(FValue)) = 0 then FValue := '';
  996.         if length(trim(Text2)) > 0 then begin
  997.           ANode := TXmlNode.CreateType(Document, xeCharData);
  998.           ANode.ValueDirect := Text2;
  999.           Parent.NodeInsert(InsertPos, ANode);
  1000.         end;
  1001.       end;
  1002.     end;
  1003.   end;
  1004.   // Do attributes
  1005.   for i := 0 to AttributeCount - 1 do begin
  1006.     Last := AttributeValue[i];
  1007.     AValue := '';
  1008.     repeat
  1009.       Reference := SplitReference(Last, First, Last);
  1010.       Replace := '';
  1011.       if length(Reference) > 0 then begin
  1012.         Entity := Document.EntityByName[Reference];
  1013.         if length(Entity) > 0 then
  1014.            Replace := Entity
  1015.         else
  1016.           Replace := '&' + Reference + ';';
  1017.       end;
  1018.       AValue := AValue + First + Replace;
  1019.     until length(Reference) = 0;
  1020.     AValue := AValue + Last;
  1021.     AttributeValue[i] := AValue;
  1022.   end;
  1023.   // Do childnodes too
  1024.   i := 0;
  1025.   while i < NodeCount do begin
  1026.     Nodes[i].ResolveEntityReferences;
  1027.     inc(i);
  1028.   end;
  1029.   // Check for empty CharData nodes
  1030.   for i := NodeCount - 1 downto 0 do
  1031.     if (Nodes[i].ElementType = xeCharData) and (length(Nodes[i].ValueDirect) = 0) then
  1032.       NodeDelete(i);
  1033. end;
  1034. procedure TXmlNode.SetAttributeByName(const AName, Value: string);
  1035. begin
  1036.   CheckCreateAttributesList;
  1037.   FAttributes.Values[AName] := QuoteString(EscapeString(Value));
  1038. end;
  1039. procedure TXmlNode.SetAttributeByNameWide(const AName: string; const Value: widestring);
  1040. begin
  1041.   SetAttributeByName(AName, FromWidestring(Value));
  1042. end;
  1043. procedure TXmlNode.SetAttributeName(Index: integer; const Value: string);
  1044. var
  1045.   S: string;
  1046.   P: integer;
  1047. begin
  1048.   if (Index >= 0) and (Index < AttributeCount) then begin
  1049.     S := FAttributes[Index];
  1050.     P := AnsiPos('=', S);
  1051.     if P > 0 then
  1052.       FAttributes[Index] := Format('%s=%s', [Value, Copy(S, P + 1, MaxInt)]);
  1053.   end;
  1054. end;
  1055. procedure TXmlNode.SetAttributeValue(Index: integer; const Value: string);
  1056. begin
  1057.   if (Index >= 0) and (Index < AttributeCount) then
  1058.     FAttributes[Index] := Format('%s=%s', [AttributeName[Index],
  1059.       QuoteString(EscapeString(Value))]);
  1060. end;
  1061. procedure TXmlNode.SetAttributeValueAsInteger(Index: integer;
  1062.   const Value: integer);
  1063. begin
  1064.   SetAttributeValue(Index, IntToStr(Value));
  1065. end;
  1066. procedure TXmlNode.SetAttributeValueAsWidestring(Index: integer;
  1067.   const Value: widestring);
  1068. begin
  1069.   SetAttributeValue(Index, FromWidestring(Value));
  1070. end;
  1071. procedure TXmlNode.SetBinaryEncoding(const Value: TBinaryEncodingType);
  1072. begin
  1073.   if assigned(Document) then
  1074.     Document.BinaryEncoding := Value;
  1075. end;
  1076. procedure TXmlNode.SetBinaryString(const Value: string);
  1077. var
  1078.   OldEncoding: TBinaryEncodingType;
  1079. begin
  1080.   // Set to base64
  1081.   OldEncoding := BinaryEncoding;
  1082.   try
  1083.     BinaryEncoding := xbeBase64;
  1084.     if length(Value) = 0 then begin
  1085.       ValueAsString := '';
  1086.       exit;
  1087.     end;
  1088.     // fill the buffer
  1089.     {$IFDEF CLR}
  1090.     BufferWrite(BytesOf(Value), length(Value));
  1091.     {$ELSE}
  1092.     BufferWrite(Value[1], length(Value));
  1093.     {$ENDIF}
  1094.   finally
  1095.     BinaryEncoding := OldEncoding;
  1096.   end;
  1097. end;
  1098. procedure TXmlNode.SetName(const Value: string);
  1099. var
  1100.   i: integer;
  1101. begin
  1102.   if FName <> Value then begin
  1103.     // Check if the name abides the rules. We will be very forgiving here and
  1104.     // just accept any name that at least does not contain control characters
  1105.     for i := 1 to length(Value) do
  1106.       if Value[i] in cControlChars then
  1107.         raise Exception.Create(Format(sxeIllegalCharInNodeName, [Value]));
  1108.     FName := Value;
  1109.   end;
  1110. end;
  1111. procedure TXmlNode.SetValueAsBool(const Value: boolean);
  1112. begin
  1113.   FValue := sdStringFromBool(Value);
  1114. end;
  1115. procedure TXmlNode.SetValueAsDateTime(const Value: TDateTime);
  1116. begin
  1117.   ValueAsString := sdDateTimeToString(Value);
  1118. end;
  1119. procedure TXmlNode.SetValueAsFloat(const Value: double);
  1120. begin
  1121.   FValue := sdWriteNumber(Value, FloatSignificantDigits, FloatAllowScientific);
  1122. end;
  1123. procedure TXmlNode.SetValueAsInt64(const Value: int64);
  1124. begin
  1125.   FValue := IntToStr(Value);
  1126. end;
  1127. procedure TXmlNode.SetValueAsInteger(const Value: integer);
  1128. begin
  1129.   FValue := IntToStr(Value);
  1130. end;
  1131. procedure TXmlNode.SetValueAsString(const AValue: string);
  1132. begin
  1133.   FValue := EscapeString(AValue);
  1134. end;
  1135. procedure TXmlNode.SetValueAsWidestring(const Value: widestring);
  1136. begin
  1137.   ValueAsString := FromWidestring(Value);
  1138. end;
  1139. procedure TXmlNode.SortChildNodes(Compare: TXMLNodeCompareFunction;
  1140.   Info: TPointer);
  1141. // Sort the child nodes using the quicksort algorithm
  1142. //local
  1143. function DoNodeCompare(Node1, Node2: TXmlNode): integer;
  1144. begin
  1145.   if assigned(Compare) then
  1146.     Result := Compare(Node1, Node2, Info)
  1147.   else
  1148.     if assigned(Document) and assigned(Document.OnNodeCompare) then
  1149.       Result := Document.OnNodeCompare(Document, Node1, Node2, Info)
  1150.     else
  1151.       Result := AnsiCompareText(Node1.Name, Node2.Name);
  1152. end;
  1153. // local
  1154. procedure QuickSort(iLo, iHi: Integer);
  1155. var
  1156.   Lo, Hi, Mid: longint;
  1157. begin
  1158.   Lo := iLo;
  1159.   Hi := iHi;
  1160.   Mid:= (Lo + Hi) div 2;
  1161.   repeat
  1162.     while DoNodeCompare(Nodes[Lo], Nodes[Mid]) < 0 do
  1163.       Inc(Lo);
  1164.     while DoNodeCompare(Nodes[Hi], Nodes[Mid]) > 0 do
  1165.       Dec(Hi);
  1166.     if Lo <= Hi then begin
  1167.       // Swap pointers;
  1168.       NodeExchange(Lo, Hi);
  1169.       if Mid = Lo then
  1170.         Mid := Hi
  1171.       else
  1172.         if Mid = Hi then
  1173.           Mid := Lo;
  1174.       Inc(Lo);
  1175.       Dec(Hi);
  1176.     end;
  1177.   until Lo > Hi;
  1178.   if Hi > iLo then QuickSort(iLo, Hi);
  1179.   if Lo < iHi then QuickSort(Lo, iHi);
  1180. end;
  1181. // main
  1182. begin
  1183.   if NodeCount > 1 then
  1184.     QuickSort(0, NodeCount - 1);
  1185. end;
  1186. function TXmlNode.ToAnsiString(const s: string): string;
  1187. begin
  1188.   if Utf8Encoded then
  1189.     Result := sdUtf8ToAnsi(s)
  1190.   else
  1191.     Result := s;
  1192. end;
  1193. function TXmlNode.ToWidestring(const s: string): widestring;
  1194. begin
  1195.   if Utf8Encoded then
  1196.     Result := sdUtf8ToUnicode(s)
  1197.   else
  1198.     Result := s;
  1199. end;
  1200. function TXmlNode.UnescapeString(const AValue: string): string;
  1201. begin
  1202.   if Utf8Encoded then
  1203.     Result := UnescapeStringUTF8(AValue)
  1204.   else
  1205.     Result := UnescapeStringAnsi(AValue);
  1206. end;
  1207. function TXmlNode.UseFullNodes: boolean;
  1208. begin
  1209.   Result := False;
  1210.   if assigned(Document) then Result := Document.UseFullNodes;
  1211. end;
  1212. function TXmlNode.Utf8Encoded: boolean;
  1213. begin
  1214.   Result := False;
  1215.   if assigned(Document) then
  1216.     Result := Document.Utf8Encoded;
  1217. end;
  1218. function TXmlNode.ValueAsBoolDef(ADefault: boolean): boolean;
  1219. var
  1220.   Ch: Char;
  1221. begin
  1222.   Result := ADefault;
  1223.   if Length(FValue) = 0 then exit;
  1224.   Ch := UpCase(FValue[1]);
  1225.   if Ch in ['T', 'Y'] then begin
  1226.     Result := True;
  1227.     exit;
  1228.   end;
  1229.   if Ch in ['F', 'N'] then begin
  1230.     Result := False;
  1231.     exit;
  1232.   end;
  1233. end;
  1234. function TXmlNode.ValueAsDateTimeDef(ADefault: TDateTime): TDateTime;
  1235. begin
  1236.   Result := sdDateTimeFromStringDefault(ValueAsString, ADefault);
  1237. end;
  1238. function TXmlNode.ValueAsFloatDef(ADefault: double): double;
  1239. var
  1240.   Code: integer;
  1241. begin
  1242.   try
  1243.     val(StringReplace(FValue, ',', '.', []), Result, Code);
  1244.     if Code > 0 then
  1245.       Result := ADefault;
  1246.   except
  1247.     Result := ADefault;
  1248.   end;
  1249. end;
  1250. function TXmlNode.ValueAsInt64Def(ADefault: int64): int64;
  1251. begin
  1252.   Result := StrToInt64Def(FValue, ADefault);
  1253. end;
  1254. function TXmlNode.ValueAsIntegerDef(ADefault: integer): integer;
  1255. begin
  1256.   Result := StrToIntDef(FValue, ADefault);
  1257. end;
  1258. procedure TXmlNode.WriteAttributeBool(const AName: string; AValue: boolean;
  1259.   ADefault: boolean);
  1260. var
  1261.   AIndex: integer;
  1262. begin
  1263.   if WriteOnDefault or (AValue <> ADefault) then begin
  1264.     AIndex := AttributeIndexByName(AName);
  1265.     if AIndex >= 0 then
  1266.       AttributeValue[AIndex] := sdStringFromBool(AValue)
  1267.     else
  1268.       AttributeAdd(AName, sdStringFromBool(AValue));
  1269.   end;
  1270. end;
  1271. procedure TXmlNode.WriteAttributeFloat(const AName: string; AValue, ADefault: double);
  1272. var
  1273.   AIndex: integer;
  1274.   S: string;
  1275. begin
  1276.   if WriteOnDefault or (AValue <> ADefault) then begin
  1277.     AIndex := AttributeIndexByName(AName);
  1278.     S := sdWriteNumber(AValue, FloatSignificantDigits, FloatAllowScientific);
  1279.     if AIndex >= 0 then
  1280.       AttributeValue[AIndex] := S
  1281.     else
  1282.       AttributeAdd(AName, S);
  1283.   end;
  1284. end;
  1285. procedure TXmlNode.WriteAttributeInteger(const AName: string; AValue: integer; ADefault: integer);
  1286. var
  1287.   AIndex: integer;
  1288. begin
  1289.   if WriteOnDefault or (AValue <> ADefault) then begin
  1290.     AIndex := AttributeIndexByName(AName);
  1291.     if AIndex >= 0 then
  1292.       AttributeValue[AIndex] := IntToStr(AValue)
  1293.     else
  1294.       AttributeAdd(AName, IntToStr(AValue));
  1295.   end;
  1296. end;
  1297. procedure TXmlNode.WriteAttributeString(const AName, AValue,
  1298.   ADefault: string);
  1299. var
  1300.   AIndex: integer;
  1301. begin
  1302.   if WriteOnDefault or (AValue <> ADefault) then begin
  1303.     AIndex := AttributeIndexByName(AName);
  1304.     if AIndex >= 0 then
  1305.       AttributeValue[AIndex] := AValue
  1306.     else
  1307.       AttributeAdd(AName, AValue);
  1308.   end;
  1309. end;
  1310. procedure TXmlNode.WriteBool(const AName: string; AValue: boolean; ADefault: boolean);
  1311. const
  1312.   cBoolValues: array[boolean] of string = ('False', 'True');
  1313. begin
  1314.   if WriteOnDefault or (AValue <> ADefault) then
  1315.     with NodeFindOrCreate(AName) do
  1316.       ValueAsString := cBoolValues[AValue];
  1317. end;
  1318. {$IFDEF USEGRAPHICS}
  1319. procedure TXmlNode.WriteBrush(const AName: string; ABrush: TBrush);
  1320. begin
  1321.   with NodeFindOrCreate(AName) do begin
  1322.     WriteColor('Color', ABrush.Color, clBlack);
  1323.     WriteInteger('Style', integer(ABrush.Style), 0);
  1324.   end;
  1325. end;
  1326. procedure TXmlNode.WriteColor(const AName: string; AValue, ADefault: TColor);
  1327. begin
  1328.   if WriteOnDefault or (AValue <> ADefault) then
  1329.     WriteHex(AName, ColorToRGB(AValue), 8, 0);
  1330. end;
  1331. {$ENDIF}
  1332. procedure TXmlNode.WriteDateTime(const AName: string; AValue,
  1333.   ADefault: TDateTime);
  1334. // Date MUST always be written in this format:
  1335. // YYYY-MM-DD (if just date) or
  1336. // YYYY-MM-DDThh:mm:ss.sssZ (if date and time. The Z stands for universal time
  1337. // zone. Since Delphi's TDateTime does not give us a clue about the timezone,
  1338. // this is the easiest solution)
  1339. // This format SHOULD NOT be changed, to avoid all kinds of
  1340. // conversion errors in future.
  1341. // This format is compatible with the W3C date/time specification as found here:
  1342. // http://www.w3.org/TR/NOTE-datetime
  1343. begin
  1344.   if WriteOnDefault or (AValue <> ADefault) then
  1345.     WriteString(AName, sdDateTimeToString(AValue), '');
  1346. end;
  1347. procedure TXmlNode.WriteFloat(const AName: string; AValue: double; ADefault: double);
  1348. begin
  1349.   if WriteOnDefault or (AValue <> ADefault) then
  1350.     with NodeFindOrCreate(AName) do
  1351.       ValueAsString := sdWriteNumber(AValue, FloatSignificantDigits, FloatAllowScientific);
  1352. end;
  1353. {$IFDEF USEGRAPHICS}
  1354. procedure TXmlNode.WriteFont(const AName: string; AFont: TFont);
  1355. begin
  1356.   with NodeFindOrCreate(AName) do begin
  1357.     WriteString('Name', AFont.Name, 'Arial');
  1358.     WriteColor('Color', AFont.Color, clBlack);
  1359.     WriteInteger('Size', AFont.Size, 14);
  1360.     WriteBool('Bold', fsBold in AFont.Style, False);
  1361.     WriteBool('Italic', fsItalic in AFont.Style, False);
  1362.     WriteBool('Underline', fsUnderline in AFont.Style, False);
  1363.     WriteBool('Strikeout', fsStrikeout in AFont.Style, False);
  1364.   end;
  1365. end;
  1366. {$ENDIF}
  1367. procedure TXmlNode.WriteHex(const AName: string; AValue, Digits: integer; ADefault: integer);
  1368. begin
  1369.   if WriteOnDefault or (AValue <> ADefault) then
  1370.     with NodeFindOrCreate(AName) do
  1371.       ValueAsString := '$' + IntToHex(AValue, Digits);
  1372. end;
  1373. function TXmlNode.WriteInnerTag: string;
  1374. // Write the inner part of the tag, the one that contains the attributes
  1375. var
  1376.   i: integer;
  1377. begin
  1378.   Result := '';
  1379.   // Attributes
  1380.   for i := 0 to AttributeCount - 1 do
  1381.     // Here we used to prevent empty attributes, but in fact, empty attributes
  1382.     // should be allowed because sometimes they're required
  1383.     Result := Result + ' ' + AttributePair[i];
  1384.   // End of tag - direct nodes get an extra "/"
  1385.   if QualifyAsDirectNode then
  1386.     Result := Result + '/';
  1387. end;
  1388. {$IFDEF D4UP}
  1389. procedure TXmlNode.WriteInt64(const AName: string; AValue, ADefault: int64);
  1390. begin
  1391.   if WriteOnDefault or (AValue <> ADefault) then
  1392.     with NodeFindOrCreate(AName) do
  1393.       ValueAsString := IntToStr(AValue);
  1394. end;
  1395. {$ENDIF}
  1396. procedure TXmlNode.WriteInteger(const AName: string; AValue: integer; ADefault: integer);
  1397. begin
  1398.   if WriteOnDefault or (AValue <> ADefault) then
  1399.     with NodeFindOrCreate(AName) do
  1400.       ValueAsString := IntToStr(AValue);
  1401. end;
  1402. {$IFDEF USEGRAPHICS}
  1403. procedure TXmlNode.WritePen(const AName: string; APen: TPen);
  1404. begin
  1405.   with NodeFindOrCreate(AName) do begin
  1406.     WriteColor('Color', APen.Color, clBlack);
  1407.     WriteInteger('Mode', integer(APen.Mode), 0);
  1408.     WriteInteger('Style', integer(APen.Style), 0);
  1409.     WriteInteger('Width', APen.Width, 0);
  1410.   end;
  1411. end;
  1412. {$ENDIF}
  1413. procedure TXmlNode.WriteString(const AName, AValue: string; const ADefault: string);
  1414. begin
  1415.   if WriteOnDefault or (AValue <> ADefault) then
  1416.     with NodeFindOrCreate(AName) do
  1417.       ValueAsString := AValue;
  1418. end;
  1419. procedure TXmlNode.WriteToStream(S: TStream);
  1420. var
  1421.   i: integer;
  1422.   AIndent: string;
  1423.   ALineFeed: string;
  1424.   ALine: string;
  1425.   ThisNode, NextNode: TXmlNode;
  1426.   AddLineFeed: boolean;
  1427. begin
  1428.   AIndent   := GetIndent;
  1429.   ALineFeed := GetLineFeed;
  1430.   // Write indent
  1431.   ALine := AIndent;
  1432.   // Write the node - distinguish node type
  1433.   case ElementType of
  1434.   xeDeclaration: // XML declaration <?xml{declaration}?>
  1435.     ALine := AIndent + Format('<?xml%s?>', [WriteInnerTag]);
  1436.   xeStylesheet: // Stylesheet <?xml-stylesheet{stylesheet}?>
  1437.     ALine := AIndent + Format('<?xml-stylesheet%s?>', [WriteInnerTag]);
  1438.   xeDoctype:
  1439.     begin
  1440.       if NodeCount = 0 then
  1441.         ALine := AIndent + Format('<!DOCTYPE %s %s>', [Name, ValueDirect])
  1442.       else begin
  1443.         ALine := AIndent + Format('<!DOCTYPE %s %s [', [Name, ValueDirect]) + ALineFeed;
  1444.         WriteStringToStream(S, ALine);
  1445.         for i := 0 to NodeCount - 1 do begin
  1446.           Nodes[i].WriteToStream(S);
  1447.           WriteStringToStream(S, ALineFeed);
  1448.         end;
  1449.         ALine := ']>';
  1450.       end;
  1451.     end;
  1452.   xeElement:
  1453.     ALine := AIndent + Format('<!ELEMENT %s %s>', [Name, ValueDirect]);
  1454.   xeAttList:
  1455.     ALine := AIndent + Format('<!ATTLIST %s %s>', [Name, ValueDirect]);
  1456.   xeEntity:
  1457.     ALine := AIndent + Format('<!ENTITY %s %s>', [Name, ValueDirect]);
  1458.   xeNotation:
  1459.     ALine := AIndent + Format('<!NOTATION %s %s>', [Name, ValueDirect]);
  1460.   xeComment: // Comment <!--{comment}-->
  1461.     ALine := AIndent + Format('<!--%s-->', [ValueDirect]);
  1462.   xeCData: // literal data <![CDATA[{data}]]>
  1463.     ALine := AIndent + Format('<![CDATA[%s]]>', [ValueDirect]);
  1464.   xeExclam: // Any <!data>
  1465.     ALine := AIndent + Format('<!%s>', [ValueDirect]);
  1466.   xeQuestion: // Any <?data?>
  1467.     ALine := AIndent + Format('<?%s?>', [ValueDirect]);
  1468.   xeCharData:
  1469.     ALine := FValue;
  1470.   xeUnknown: // Any <data>
  1471.     ALine := AIndent + Format('<%s>', [ValueDirect]);
  1472.   xeNormal: // normal nodes (xeNormal)
  1473.     begin
  1474.       // Write tag
  1475.       ALine := ALine + Format('<%s%s>', [FName, WriteInnerTag]);
  1476.       // Write value (if any)
  1477.       ALine := ALine + FValue;
  1478.       if (NodeCount > 0) then
  1479.         // ..and a linefeed
  1480.         ALine := ALine + ALineFeed;
  1481.       WriteStringToStream(S, ALine);
  1482.       // Write child elements
  1483.       for i := 0 to NodeCount - 1 do begin
  1484.         ThisNode := Nodes[i];
  1485.         NextNode := Nodes[i + 1];
  1486.         ThisNode.WriteToStream(S);
  1487.         AddLineFeed := True;
  1488.         if ThisNode.ElementType = xeCharData then
  1489.           AddLineFeed := False;
  1490.         if assigned(NextNode) then
  1491.           if NextNode.ElementType = xeCharData then
  1492.             AddLineFeed := False;
  1493.         if AddLineFeed then
  1494.           WriteStringToStream(S, ALineFeed);
  1495.       end;
  1496.       // Write end tag
  1497.       ALine := '';
  1498.       if not QualifyAsDirectNode then begin
  1499.         if NodeCount > 0 then
  1500.           ALine := AIndent;
  1501.         ALine := ALine + Format('</%s>', [FName]);
  1502.       end;
  1503.     end;
  1504.   else
  1505.     raise EFilerError.Create(sxeIllegalElementType);
  1506.   end;//case
  1507.   WriteStringToStream(S, ALine);
  1508.   // Call the onprogress
  1509.   if assigned(Document) then Document.DoProgress(S.Position);
  1510. end;
  1511. function TXmlNode.WriteToString: string;
  1512. var
  1513.   S: TsdStringStream;
  1514. begin
  1515.   // We will simply call WriteToStream and collect the result as string using
  1516.   // a string stream
  1517.   S := TsdStringStream.Create('');
  1518.   try
  1519.     WriteToStream(S);
  1520.     Result := S.DataString;
  1521.   finally
  1522.     S.Free;
  1523.   end;
  1524. end;
  1525. procedure TXmlNode.WriteWidestring(const AName: string;
  1526.   const AValue: widestring; const ADefault: widestring);
  1527. begin
  1528.   WriteString(AName, FromWidestring(AValue), ADefault);
  1529. end;
  1530. { TXmlNodeList }
  1531. function TXmlNodeList.GetItems(Index: Integer): TXmlNode;
  1532. begin
  1533.   Result := TXmlNode(Get(Index));
  1534. end;
  1535. procedure TXmlNodeList.SetItems(Index: Integer; const Value: TXmlNode);
  1536. begin
  1537.   Put(Index, TPointer(Value));
  1538. end;
  1539. { TNativeXml }
  1540. procedure TNativeXml.Assign(Source: TPersistent);
  1541. // local
  1542. procedure SetDocumentRecursively(ANode: TXmlNode; ADocument: TNativeXml);
  1543. var
  1544.   i: integer;
  1545. begin
  1546.   ANode.Document := ADocument;
  1547.   for i := 0 to ANode.NodeCount - 1 do
  1548.     SetDocumentRecursively(ANode.Nodes[i], ADocument);
  1549. end;
  1550. // main
  1551. begin
  1552.   if Source is TNativeXml then begin
  1553.     // Copy private members
  1554.     FBinaryEncoding := TNativeXml(Source).FBinaryEncoding;
  1555.     FDropCommentsOnParse := TNativeXml(Source).FDropCommentsOnParse;
  1556.     FExternalEncoding := TNativeXml(Source).FExternalEncoding;
  1557.     FParserWarnings := TNativeXml(Source).FParserWarnings;
  1558.     FIndentString := TNativeXml(Source).FIndentString;
  1559.     FUseFullNodes := TNativeXml(Source).FUseFullNodes;
  1560.     FUtf8Encoded := TNativeXml(Source).FUtf8Encoded;
  1561.     FWriteOnDefault := TNativeXml(Source).FWriteOnDefault;
  1562.     FXmlFormat := TNativeXml(Source).FXmlFormat;
  1563.     FSortAttributes := TNativeXml(Source).FSortAttributes;
  1564.     // Assign root
  1565.     FRootNodes.Assign(TNativeXml(Source).FRootNodes);
  1566.     // Set Document property recursively
  1567.     SetDocumentRecursively(FRootNodes, Self);
  1568.   end else if Source is TXmlNode then begin
  1569.     // Assign this node to the FRootNodes property
  1570.     FRootNodes.Assign(Source);
  1571.     // Set Document property recursively
  1572.     SetDocumentRecursively(FRootNodes, Self);
  1573.   end else
  1574.     inherited;
  1575. end;
  1576. procedure TNativeXml.Clear;
  1577. var
  1578.   ANode: TXmlNode;
  1579. begin
  1580.   // Reset defaults
  1581.   SetDefaults;
  1582.   // Clear root
  1583.   FRootNodes.Clear;
  1584.   // Build default items in RootNodes
  1585.   // - first the declaration
  1586.   ANode := TXmlNode.CreateType(Self, xeDeclaration);
  1587.   ANode.Name := 'xml';
  1588.   ANode.AttributeAdd('version', cDefaultVersionString);
  1589.   ANode.AttributeAdd('encoding', cDefaultEncodingString);
  1590.   FRootNodes.NodeAdd(ANode);
  1591.   // - then the root node
  1592.   FRootNodes.NodeNew('');
  1593. end;
  1594. procedure TNativeXml.CopyFrom(Source: TNativeXml);
  1595. begin
  1596.   if not assigned(Source) then exit;
  1597.   Assign(Source);
  1598. end;
  1599. constructor TNativeXml.Create;
  1600. begin
  1601.   inherited Create;
  1602.   FRootNodes := TXmlNode.Create(Self);
  1603.   Clear;
  1604. end;
  1605. constructor TNativeXml.CreateName(const ARootName: string);
  1606. begin
  1607.   Create;
  1608.   Root.Name := ARootName;
  1609. end;
  1610. destructor TNativeXml.Destroy;
  1611. begin
  1612.   FreeAndNil(FRootNodes);
  1613.   inherited;
  1614. end;
  1615. procedure TNativeXml.DoNodeLoaded(Node: TXmlNode);
  1616. begin
  1617.   if assigned(FOnNodeLoaded) then
  1618.     FOnNodeLoaded(Self, Node);
  1619. end;
  1620. procedure TNativeXml.DoNodeNew(Node: TXmlNode);
  1621. begin
  1622.   if assigned(FOnNodeNew) then
  1623.     FOnNodeNew(Self, Node);
  1624. end;
  1625. procedure TNativeXml.DoProgress(Size: integer);
  1626. begin
  1627.   if assigned(FOnProgress) then FOnProgress(Self, Size);
  1628. end;
  1629. procedure TNativeXml.DoUnicodeLoss(Sender: TObject);
  1630. begin
  1631.   if assigned(FOnUnicodeLoss) then FOnUnicodeLoss(Self);
  1632. end;
  1633. function TNativeXml.GetCommentString: string;
  1634. // Get the first comment node, and return its value
  1635. var
  1636.   ANode: TXmlNode;
  1637. begin
  1638.   Result := '';
  1639.   ANode := FRootNodes.NodeByElementType(xeComment);
  1640.   if assigned(ANode) then
  1641.     Result := ANode.ValueAsString;
  1642. end;
  1643. function TNativeXml.GetEncodingString: string;
  1644. begin
  1645.   Result := '';
  1646.   if FRootNodes.NodeCount > 0 then
  1647.     if FRootNodes[0].ElementType = xeDeclaration then
  1648.       Result := FRootNodes[0].AttributeByName['encoding'];
  1649. end;
  1650. function TNativeXml.GetEntityByName(AName: string): string;
  1651. var
  1652.   i, j: integer;
  1653. begin
  1654.   Result := '';
  1655.   for i := 0 to FRootNodes.NodeCount - 1 do
  1656.     if FRootNodes[i].ElementType = xeDoctype then with FRootNodes[i] do begin
  1657.       for j := 0 to NodeCount - 1 do
  1658.         if (Nodes[j].ElementType = xeEntity) and (Nodes[j].Name = AName) then begin
  1659.           Result := UnQuoteString(Trim(Nodes[j].ValueDirect));
  1660.           exit;
  1661.         end;
  1662.     end;
  1663. end;
  1664. function TNativeXml.GetRoot: TXmlNode;
  1665. begin
  1666.   Result := FRootNodes.NodeByElementType(xeNormal);
  1667. end;
  1668. function TNativeXml.GetStyleSheetNode: TXmlNode;
  1669. begin
  1670.   Result := FRootNodes.NodeByElementType(xeStylesheet);
  1671.   if not assigned(Result) then begin
  1672.     // Add a stylesheet node as second one if none present
  1673.     Result := TXmlNode.CreateType(Self, xeStyleSheet);
  1674.     FRootNodes.NodeInsert(1, Result);
  1675.   end;
  1676. end;
  1677. function TNativeXml.GetVersionString: string;
  1678. begin
  1679.   Result := '';
  1680.   if FRootNodes.NodeCount > 0 then
  1681.     if FRootNodes[0].ElementType = xeDeclaration then
  1682.       Result := FRootNodes[0].AttributeByName['version'];
  1683. end;
  1684. function TNativeXml.IsEmpty: boolean;
  1685. var
  1686.   ARoot: TXmlNode;
  1687. begin
  1688.   Result := True;
  1689.   ARoot := GetRoot;
  1690.   if assigned(ARoot) then Result := ARoot.IsClear;
  1691. end;
  1692. function TNativeXml.LineFeed: string;
  1693. begin
  1694.   case XmlFormat of
  1695.   xfReadable: Result := #13#10;
  1696.   xfCompact:  Result := #10;
  1697.   else
  1698.     Result := #10;
  1699.   end;
  1700. end;
  1701. procedure TNativeXml.LoadFromFile(const FileName: string);
  1702. var
  1703.   S: TStream;
  1704. begin
  1705.   S := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
  1706.   try
  1707.     LoadFromStream(S);
  1708.   finally
  1709.     S.Free;
  1710.   end;
  1711. end;
  1712. procedure TNativeXml.LoadFromStream(Stream: TStream);
  1713. var
  1714.   B: TsdBufferedReadStream;
  1715. begin
  1716.   // Create buffer filter. Since we read from the original stream a buffer at a
  1717.   // time, this speeds up the reading process for disk-based files.
  1718.   B := TsdBufferedReadStream.Create(Stream, False);
  1719.   try
  1720.     // We will create a conversion stream as intermediate
  1721.     if Utf8Encoded then
  1722.       FCodecStream := TsdUtf8Stream.Create(B)
  1723.     else
  1724.       FCodecStream := TsdAnsiStream.Create(B);
  1725.     try
  1726.       // Connect events
  1727.       FCodecStream.OnUnicodeLoss := DoUnicodeLoss;
  1728.       // Read from stream
  1729.       ReadFromStream(FCodecStream);
  1730.       // Set our external encoding
  1731.       FExternalEncoding := FCodecStream.Encoding;
  1732.       // Set internal encoding
  1733.       if (ExternalEncoding = seUtf8) or (EncodingString = 'UTF-8') then
  1734.         FUtf8Encoded := True;
  1735.     finally
  1736.       FreeAndNil(FCodecStream);
  1737.     end;
  1738.   finally
  1739.     B.Free;
  1740.   end;
  1741. end;
  1742. procedure TNativeXml.ParseDTD(ANode: TXmlNode; S: TStream);
  1743. // DTD parsing is quite different from normal node parsing so it is brought
  1744. // under in the main NativeXml object
  1745. procedure ParseMarkupDeclarations;
  1746. var
  1747.   Ch: char;
  1748. begin
  1749.   repeat
  1750.     ANode.NodeNew('').ReadFromStream(S);
  1751.     // Read character, exit if none available
  1752.     repeat
  1753.       if S.Read(Ch, 1) = 0 then exit;
  1754.     // Read until end markup declaration or end
  1755.     until not (Ch in cControlChars);
  1756.     if Ch = ']' then break;
  1757.     S.Seek(-1, soCurrent);
  1758.   until False;
  1759. end;
  1760. // main
  1761. var
  1762.   Prework: string;
  1763.   Ch: char;
  1764.   Words: TStringList;
  1765. begin
  1766.   // Get the name and external ID
  1767.   Prework := '';
  1768.   repeat
  1769.     // Read character, exit if none available
  1770.     if S.Read(Ch, 1) = 0 then exit;
  1771.     // Read until markup declaration or end
  1772.     if Ch in ['[', '>'] then break;
  1773.     Prework := Prework + Ch;
  1774.   until False;
  1775.   Words := TStringList.Create;
  1776.   try
  1777.     ParseAttributes(Prework, 1, length(Prework) + 1, Words);
  1778.     // First word is name
  1779.     if Words.Count > 0 then begin
  1780.       ANode.Name := Words[0];
  1781.       Words.Delete(0);
  1782.       // Put the rest in the valuedirect
  1783.       ANode.ValueDirect := Trim(StringReplace(Words.Text, #13#10, ' ', [rfReplaceAll]));
  1784.     end;
  1785.   finally
  1786.     Words.Free;
  1787.   end;
  1788.   if Ch = '[' then begin
  1789.     // Parse any !ENTITY nodes and such
  1790.     ParseMarkupDeclarations;
  1791.     // read final tag
  1792.     repeat
  1793.       if S.Read(Ch, 1) = 0 then exit;
  1794.       if Ch = '>' then break;
  1795.     until False;
  1796.   end;
  1797. end;
  1798. procedure TNativeXml.ReadFromStream(S: TStream);
  1799. var
  1800.   i: integer;
  1801.   ANode: TXmlNode;
  1802.   AEncoding: string;
  1803.   NormalCount, DeclarationCount,
  1804.   DoctypeCount, CDataCount: integer;
  1805.   NormalPos, DoctypePos: integer;
  1806. begin
  1807.   FAbortParsing := False;
  1808.   with FRootNodes do begin
  1809.     // Clear the old root nodes - we do not reset the defaults
  1810.     Clear;
  1811.     DoProgress(0);
  1812.     repeat
  1813.       ANode := NodeNew('');
  1814.       ANode.ReadFromStream(S);
  1815.       if AbortParsing then exit;
  1816.       // XML declaration
  1817.       if ANode.ElementType = xeDeclaration then begin
  1818.         if ANode.HasAttribute('encoding') then
  1819.           AEncoding := ANode.AttributeByName['encoding'];
  1820.         // Check encoding
  1821.         if assigned(FCodecStream) and (AEncoding = 'UTF-8') then
  1822.           FCodecStream.Encoding := seUTF8;
  1823.       end;
  1824.       // Skip clear nodes
  1825.       if ANode.IsClear then
  1826.         NodeDelete(NodeCount - 1);
  1827.     until S.Position >= S.Size;
  1828.     DoProgress(S.Size);
  1829.     // Do some checks
  1830.     NormalCount      := 0;
  1831.     DeclarationCount := 0;
  1832.     DoctypeCount     := 0;
  1833.     CDataCount       := 0;
  1834.     NormalPos        := -1;
  1835.     DoctypePos       := -1;
  1836.     for i := 0 to NodeCount - 1 do begin
  1837.       // Count normal elements - there may be only one
  1838.       case Nodes[i].ElementType of
  1839.       xeNormal:
  1840.         begin
  1841.           inc(NormalCount);
  1842.           NormalPos := i;
  1843.         end;
  1844.       xeDeclaration: inc(DeclarationCount);
  1845.       xeDoctype:
  1846.         begin
  1847.           inc(DoctypeCount);
  1848.           DoctypePos := i;
  1849.         end;
  1850.       xeCData: inc(CDataCount);
  1851.       end;//case
  1852.     end;
  1853.     // We *must* have a root node
  1854.     if NormalCount = 0 then
  1855.       raise EFilerError.Create(sxeNoRootElement);
  1856.     // Do some validation if we allow parser warnings
  1857.     if FParserWarnings then begin
  1858.       // Check for more than one root node
  1859.       if NormalCount > 1 then raise EFilerError.Create(sxeMoreThanOneRootElement);
  1860.       // Check for more than one xml declaration
  1861.       if DeclarationCount > 1 then raise EFilerError.Create(sxeMoreThanOneDeclaration);
  1862.       // Declaration must be first element if present
  1863.       if DeclarationCount = 1 then
  1864.         if Nodes[0].ElementType <> xeDeclaration then
  1865.           raise EFilerError.Create(sxeDeclarationMustBeFirstElem);
  1866.       // Check for more than one DTD
  1867.       if DoctypeCount > 1 then raise EFilerError.Create(sxeMoreThanOneDoctype);
  1868.       // Check if DTD is after root, this is not allowed
  1869.       if (DoctypeCount = 1) and (DoctypePos > NormalPos) then
  1870.         raise EFilerError.Create(sxeDoctypeAfterRootElement);
  1871.       // No CDATA in root allowed
  1872.       if CDataCount > 0 then
  1873.         raise EFilerError.Create(sxeCDataInRoot);
  1874.     end;
  1875.   end;//with
  1876. end;
  1877. procedure TNativeXml.ReadFromString(const AValue: string);
  1878. var
  1879.   S: TStream;
  1880. begin
  1881.   S := TsdStringStream.Create(AValue);
  1882.   try
  1883.     ReadFromStream(S);
  1884.   finally
  1885.     S.Free;
  1886.   end;
  1887. end;
  1888. procedure TNativeXml.ResolveEntityReferences;
  1889. begin
  1890.   if assigned(Root) then
  1891.     Root.ResolveEntityReferences;
  1892. end;
  1893. procedure TNativeXml.SaveToFile(const FileName: string);
  1894. var
  1895.   S: TStream;
  1896. begin
  1897.   S := TFileStream.Create(FileName, fmCreate);
  1898.   try
  1899.     SaveToStream(S);
  1900.   finally
  1901.     S.Free;
  1902.   end;
  1903. end;
  1904. procedure TNativeXml.SaveToStream(Stream: TStream);
  1905. var
  1906.   B: TsdBufferedWriteStream;
  1907. begin
  1908.   // Create buffer filter. Since we write a buffer at a time to the destination
  1909.   // stream, this speeds up the writing process for disk-based files.
  1910.   B := TsdBufferedWriteStream.Create(Stream, False);
  1911.   try
  1912.     // Create conversion stream
  1913.     if Utf8Encoded then
  1914.       FCodecStream := TsdUtf8Stream.Create(B)
  1915.     else
  1916.       FCodecStream := TsdAnsiStream.Create(B);
  1917.     try
  1918.       // Set External encoding
  1919.       FCodecStream.Encoding := FExternalEncoding;
  1920.       WriteToStream(FCodecStream);
  1921.     finally
  1922.       FreeAndNil(FCodecStream);
  1923.     end;
  1924.   finally
  1925.     B.Free;
  1926.   end;
  1927. end;
  1928. procedure TNativeXml.SetCommentString(const Value: string);
  1929. // Find first comment node and set it's value, otherwise add new comment node
  1930. // right below the xml declaration
  1931. var
  1932.   ANode: TXmlNode;
  1933. begin
  1934.   ANode := FRootNodes.NodeByElementType(xeComment);
  1935.   if not assigned(ANode) and (length(Value) > 0) then begin
  1936.     ANode := TXmlNode.CreateType(Self, xeComment);
  1937.     FRootNodes.NodeInsert(1, ANode);
  1938.   end;
  1939.   if assigned(ANode) then ANode.ValueAsString := Value;
  1940. end;
  1941. procedure TNativeXml.SetDefaults;
  1942. begin
  1943.   // Defaults
  1944.   FExternalEncoding       := cDefaultExternalEncoding;
  1945.   FXmlFormat              := cDefaultXmlFormat;
  1946.   FWriteOnDefault         := cDefaultWriteOnDefault;
  1947.   FBinaryEncoding         := cDefaultBinaryEncoding;
  1948.   FUtf8Encoded            := cDefaultUtf8Encoded;
  1949.   FIndentString           := cDefaultIndentString;
  1950.   FDropCommentsOnParse    := cDefaultDropCommentsOnParse;
  1951.   FUseFullNodes           := cDefaultUseFullNodes;
  1952.   FSortAttributes         := cDefaultSortAttributes;
  1953.   FFloatAllowScientific   := cDefaultFloatAllowScientific;
  1954.   FFloatSignificantDigits := cDefaultFloatSignificantDigits;
  1955. end;
  1956. procedure TNativeXml.SetEncodingString(const Value: string);
  1957. var
  1958.   ANode: TXmlNode;
  1959. begin
  1960.   if Value = GetEncodingString then exit;
  1961.   ANode := FRootNodes[0];
  1962.   if not assigned(ANode) or (ANode.ElementType <> xeDeclaration) then begin
  1963.     if length(Value) > 0 then begin
  1964.       ANode := TXmlNode.CreateType(Self, xeDeclaration);
  1965.       FRootNodes.NodeInsert(0, ANode);
  1966.     end;
  1967.   end;
  1968.   if assigned(ANode) then
  1969.     ANode.AttributeByName['encoding'] := Value;
  1970. end;
  1971. procedure TNativeXml.SetVersionString(const Value: string);
  1972. var
  1973.   ANode: TXmlNode;
  1974. begin
  1975.   if Value = GetVersionString then exit;
  1976.   ANode := FRootNodes[0];
  1977.   if not assigned(ANode) or (ANode.ElementType <> xeDeclaration) then begin
  1978.     if length(Value) > 0 then begin
  1979.       ANode := TXmlNode.CreateType(Self, xeDeclaration);
  1980.       FRootNodes.NodeInsert(0, ANode);
  1981.     end;
  1982.   end;
  1983.   if assigned(ANode) then
  1984.     ANode.AttributeByName['version'] := Value;
  1985. end;
  1986. procedure TNativeXml.WriteToStream(S: TStream);
  1987. var
  1988.   i: integer;
  1989. begin
  1990.   if not assigned(Root) and FParserWarnings then
  1991.     raise EFilerError.Create(sxeRootElementNotDefined);
  1992.   DoProgress(0);
  1993.   // write the root nodes
  1994.   for i := 0 to FRootNodes.NodeCount - 1 do begin
  1995.     FRootNodes[i].WriteToStream(S);
  1996.     WriteStringToStream(S, LineFeed);
  1997.   end;
  1998.   DoProgress(S.Size);
  1999. end;
  2000. function TNativeXml.WriteToString: string;
  2001. var
  2002.   S: TsdStringStream;
  2003. begin
  2004.   S := TsdStringStream.Create('');
  2005.   try
  2006.     WriteToStream(S);
  2007.     Result := S.DataString;
  2008.   finally
  2009.     S.Free;
  2010.   end;
  2011. end;
  2012. { TsdCodecStream }
  2013. constructor TsdCodecStream.Create(AStream: TStream);
  2014. begin
  2015.   inherited Create;
  2016.   FStream := AStream;
  2017. end;
  2018. function TsdCodecStream.InternalRead(var Buffer{$IFDEF CLR}: array of Byte{$ENDIF}; Offset, Count: Longint): Longint;
  2019. // Read from FStream and pass back data
  2020. var
  2021.   i, j: integer;
  2022.   BOM: array[0..3] of byte;
  2023.   BytesRead: integer;
  2024.   Found: boolean;
  2025. begin
  2026.   Result := 0;
  2027.   if FMode = umUnknown then begin
  2028.     FMode := umRead;
  2029.     // Check FStream
  2030.     if not assigned(FStream) then
  2031.       raise EStreamError.Create(sxeCodecStreamNotAssigned);
  2032.     // Determine encoding
  2033.     FEncoding := se8Bit;
  2034.     BytesRead := FStream.Read(BOM, 4);
  2035.     for i := 0 to cBomInfoCount - 1 do begin
  2036.       Found := True;
  2037.       for j := 0 to Min(BytesRead, cBomInfo[i].Len) - 1 do begin
  2038.         if BOM[j] <> cBomInfo[i].BOM[j] then begin
  2039.           Found := False;
  2040.           break;
  2041.         end;
  2042.       end;
  2043.       if Found then break;
  2044.     end;
  2045.     if Found then begin
  2046.       FEncoding := cBomInfo[i].Enc;
  2047.       FWriteBom := cBomInfo[i].HasBOM;
  2048.     end else begin
  2049.       // Unknown.. default to this
  2050.       FEncoding := se8Bit;
  2051.       FWriteBom := False;
  2052.     end;
  2053.     // Some encodings are not supported (yet)
  2054.     if FEncoding in [seUCS4BE, seUCS4_2143, seUCS4_3412, seEBCDIC] then
  2055.       raise EStreamError.Create(sxeUnsupportedEncoding);
  2056.     // Correct stream to start position
  2057.     if FWriteBom then
  2058.       FStream.Seek(cBomInfo[i].Len - BytesRead, soCurrent)
  2059.     else
  2060.       FStream.Seek(-BytesRead, soCurrent);
  2061.     // Check if we must swap byte order
  2062.     if FEncoding in [se16BitBE, seUTF16BE] then
  2063.       FSwapByteOrder := True;
  2064.   end;
  2065.   // Check mode
  2066.   if FMode <> umRead then
  2067.     raise EStreamError.Create(sxeCannotReadCodecForWriting);
  2068.   // Check count
  2069.   if Count <> 1 then
  2070.     raise EStreamError.Create(sxeCannotReadMultipeChar);
  2071.   // Now finally read
  2072.   TBytes(Buffer)[Offset] := ReadByte;
  2073.   if TBytes(Buffer)[Offset] <> 0 then Result := 1;
  2074. end;
  2075. {$IFDEF CLR}
  2076. function TsdCodecStream.Read(var Buffer: array of Byte; Offset, Count: Longint): Longint;
  2077. begin
  2078.   Result := InternalRead(Buffer, Offset, Count);
  2079. end;
  2080. {$ELSE}
  2081. function TsdCodecStream.Read(var Buffer; Count: Longint): Longint;
  2082. begin
  2083.   Result := InternalRead(Buffer, 0, Count);
  2084. end;
  2085. {$ENDIF}
  2086. function TsdCodecStream.ReadByte: byte;
  2087. begin
  2088.   // default does nothing
  2089.   Result := 0;
  2090. end;
  2091. function TsdCodecStream.InternalSeek(Offset: Longint; Origin: TSeekOrigin): Longint;
  2092. begin
  2093.   Result := 0;
  2094.   if FMode = umUnknown then
  2095.     raise EStreamError.Create(sxeCannotSeekBeforeReadWrite);
  2096.   if Origin = soCurrent then begin
  2097.     if Offset = 0 then begin
  2098.       // Position
  2099.       Result := FStream.Position;
  2100.       exit;
  2101.     end;
  2102.     if (FMode = umRead) and ((Offset = -1) or (Offset = -2)) then begin
  2103.       FBuffer := '';
  2104.       case Offset of
  2105.       -1: FStream.Seek(FPosMin1, soBeginning);
  2106.       -2: FStream.Seek(FPosMin2, soBeginning);
  2107.       end;
  2108.       exit;
  2109.     end;
  2110.   end;
  2111.   if (Origin = soEnd) and (Offset = 0) then begin
  2112.     // Size
  2113.     Result := FStream.Size;
  2114.     exit;
  2115.   end;
  2116.   // Ignore set position from beginning (used in Size command)
  2117.   if Origin = soBeginning then exit;
  2118.   // Arriving here means we cannot do it
  2119.   raise EStreamError.Create(sxeCannotPerformSeek);
  2120. end;
  2121. {$IFDEF CLR}
  2122. function TsdCodecStream.Seek(const Offset: Int64; Origin: TSeekOrigin): Int64;
  2123. begin
  2124.   Result := InternalSeek(Offset, Origin);
  2125. end;
  2126. {$ELSE}
  2127. function TsdCodecStream.Seek(Offset: Longint; Origin: Word): Longint;
  2128. begin
  2129.   Result := InternalSeek(Offset, TSeekOrigin(Origin));
  2130. end;
  2131. {$ENDIF}
  2132. procedure TsdCodecStream.StorePrevPositions;
  2133. begin
  2134.   FPosMin2 := FPosMin1;
  2135.   FPosMin1 := FStream.Position;
  2136. end;
  2137. function TsdCodecStream.InternalWrite(const Buffer{$IFDEF CLR}: array of Byte{$ENDIF}; Offset, Count: Longint): Longint;
  2138. var
  2139.   i: integer;
  2140. begin
  2141.   if FMode = umUnknown then begin
  2142.     FMode := umWrite;
  2143.     // Some encodings are not supported (yet)
  2144.     if FEncoding in [seUCS4BE, seUCS4_2143, seUCS4_3412, seEBCDIC] then
  2145.       raise EStreamError.Create(sxeUnsupportedEncoding);
  2146.     // Find correct encoding info
  2147.     for i := 0 to cBomInfoCount - 1 do
  2148.       if cBomInfo[i].Enc = FEncoding then begin
  2149.         FWriteBom := cBomInfo[i].HasBOM;
  2150.         break;
  2151.       end;
  2152.     // Write BOM
  2153.     if FWriteBom then
  2154.       FStream.WriteBuffer(cBomInfo[i].BOM, cBomInfo[i].Len);
  2155.     // Check if we must swap byte order
  2156.     if FEncoding in [se16BitBE, seUTF16BE] then
  2157.       FSwapByteOrder := True;
  2158.   end;
  2159.   if FMode <> umWrite then
  2160.     raise EStreamError.Create(sxeCannotWriteCodecForReading);
  2161.   WriteBuf(Buffer, Offset, Count);
  2162.   Result := Count;
  2163. end;
  2164. {$IFDEF CLR}
  2165. function TsdCodecStream.Write(const Buffer: array of Byte; Offset, Count: Longint): Longint;
  2166. begin
  2167.   Result := InternalWrite(Buffer, Offset, Count);
  2168. end;
  2169. {$ELSE}
  2170. function TsdCodecStream.Write(const Buffer; Count: Longint): Longint;
  2171. begin
  2172.   Result := InternalWrite(Byte(Buffer), 0, Count);
  2173. end;
  2174. {$ENDIF}
  2175. procedure TsdCodecStream.WriteBuf(const Buffer{$IFDEF CLR}: TBytes{$ENDIF}; Offset, Count: longint);
  2176. var
  2177.   i: integer;
  2178. begin
  2179.   // Default just writes out bytes one by one. We override this in descendants
  2180.   // to provide faster writes for some modes
  2181.   for i := 0 to Count - 1 do
  2182.   {$IFDEF CLR}
  2183.     WriteByte(Buffer[Offset + i]);
  2184.   {$ELSE}
  2185.     WriteByte(TBytes(Buffer)[Offset + i]);
  2186.   {$ENDIF}
  2187. end;
  2188. procedure TsdCodecStream.WriteByte(const B: byte);
  2189. begin
  2190. // default does nothing
  2191. end;
  2192. {$IFDEF CLR}
  2193. procedure TsdCodecStream.SetSize(NewSize: Int64);
  2194. begin
  2195. // default does nothing
  2196. end;
  2197. {$ENDIF}
  2198. { TsdAnsiStream }
  2199. function TsdAnsiStream.ReadByte: byte;
  2200. var
  2201.   B: byte;
  2202.   W: word;
  2203. begin
  2204.   StorePrevPositions;
  2205.   case FEncoding of
  2206.   se8Bit, seUTF8:
  2207.     begin
  2208.       // Just a flat read of one byte. UTF8 is not converted back, when UTF8
  2209.       // encoding is detected, the document will set Utf8Encoded to True.
  2210.       B := 0;
  2211.       FStream.Read(B, 1);
  2212.       Result := B;
  2213.     end;
  2214.   se16BitBE,se16BitLE,seUTF16BE,seUTF16LE:
  2215.     begin
  2216.       // Read two bytes
  2217.       W := 0;
  2218.       FStream.Read(W, 2);
  2219.       // Swap byte order
  2220.       if FSwapByteOrder then
  2221.         W := swap(W);
  2222.       // Unicode warning loss
  2223.       if ((W and $FF00) > 0) and not FWarningUnicodeLoss then begin
  2224.         FWarningUnicodeLoss := True;
  2225.         if assigned(FOnUnicodeLoss) then
  2226.           FOnUnicodeLoss(Self);
  2227.         // We cannot display unicode range characters
  2228.         Result := ord('?');
  2229.       end else
  2230.         Result := W and $FF;
  2231.     end;
  2232.   else
  2233.     raise EStreamError.Create(sxeUnsupportedEncoding);
  2234.   end;
  2235. end;
  2236. procedure TsdAnsiStream.WriteBuf(const Buffer{$IFDEF CLR}: TBytes{$ENDIF}; Offset, Count: longint);
  2237. begin
  2238.   case FEncoding of
  2239.   se8Bit:
  2240.     begin
  2241.       // one on one
  2242.       if StreamWrite(FStream, Buffer, Offset, Count) <> Count then
  2243.         raise EStreamError.Create(sxeCannotWriteToOutputStream);
  2244.     end;
  2245.   else
  2246.     inherited;
  2247.   end;
  2248. end;
  2249. procedure TsdAnsiStream.WriteByte(const B: byte);
  2250. var
  2251.   SA, SU: string;
  2252.   W: word;
  2253. begin
  2254.   case FEncoding of
  2255.   se8Bit:
  2256.     begin
  2257.       // Just a flat write of one byte
  2258.       FStream.Write(B, 1);
  2259.     end;
  2260.   seUTF8:
  2261.     begin
  2262.       // Convert Ansi to UTF8
  2263.       SA := char(B);
  2264.       SU := sdAnsiToUTF8(SA);
  2265.       // write out
  2266.       if FStream.Write(SU[1], length(SU)) = 0 then
  2267.         raise EStreamError.Create(sxeCannotWriteToOutputStream);
  2268.     end;
  2269.   se16BitBE,se16BitLE,seUTF16BE,seUTF16LE:
  2270.     begin
  2271.       // Convert Ansi to Unicode
  2272.       W := B;
  2273.       // Swap byte order
  2274.       if FSwapByteOrder then
  2275.         W := swap(W);
  2276.       // write out
  2277.       if FStream.Write(W, 2) = 0 then
  2278.         raise EStreamError.Create(sxeCannotWriteToOutputStream);
  2279.     end;
  2280.   else
  2281.     raise EStreamError.Create(sxeUnsupportedEncoding);
  2282.   end;
  2283. end;
  2284. { TsdUtf8Stream }
  2285. function TsdUtf8Stream.ReadByte: byte;
  2286. var
  2287.   B, B1, B2, B3: byte;
  2288.   W: word;
  2289.   SA: string;
  2290. begin
  2291.   Result := 0;
  2292.   // New character?
  2293.   if (Length(FBuffer) = 0) or (FBufferPos > length(FBuffer)) then begin
  2294.     StorePrevPositions;
  2295.     FBufferPos := 1;
  2296.     // Read another char and put in buffer
  2297.     case FEncoding of
  2298.     se8Bit:
  2299.       begin
  2300.         // read one byte
  2301.         B := 0;
  2302.         FStream.Read(B, 1);
  2303.         SA := char(B);
  2304.         // Convert to UTF8
  2305.         FBuffer := sdAnsiToUtf8(SA);
  2306.       end;
  2307.     seUTF8:
  2308.       begin
  2309.         // Read one, two or three bytes in the buffer
  2310.         B1 := 0;
  2311.         FStream.Read(B1, 1);
  2312.         FBuffer := char(B1);
  2313.         if (B1 and $80) > 0 then begin
  2314.           if (B1 and $20) <> 0 then begin
  2315.             B2 := 0;
  2316.             FStream.Read(B2, 1);
  2317.             FBuffer := FBuffer + char(B2);
  2318.           end;
  2319.           B3 := 0;
  2320.           FStream.Read(B3, 1);
  2321.           FBuffer := FBuffer + char(B3);
  2322.         end;
  2323.       end;
  2324.     se16BitBE,se16BitLE,seUTF16BE,seUTF16LE:
  2325.       begin
  2326.         // Read two bytes
  2327.         W := 0;
  2328.         FStream.Read(W, 2);
  2329.         // Swap byte order
  2330.         if FSwapByteOrder then
  2331.           W := swap(W);
  2332.         // Convert to UTF8 in buffer
  2333.         {$IFDEF D5UP}
  2334.         FBuffer := sdUnicodeToUtf8(widechar(W));
  2335.         {$ELSE}
  2336.         FBuffer := sdUnicodeToUtf8(char(W and $FF));
  2337.         {$ENDIF}
  2338.       end;
  2339.     else
  2340.       raise EStreamError.Create(sxeUnsupportedEncoding);
  2341.     end;
  2342.   end;
  2343.   // Now we have the buffer, so read
  2344.   if (FBufferPos > 0) and (FBufferPos <= length(FBuffer)) then
  2345.     Result := byte(FBuffer[FBufferPos]);
  2346.   inc(FBufferPos);
  2347. end;
  2348. procedure TsdUtf8Stream.WriteBuf(const Buffer{$IFDEF CLR}: TBytes{$ENDIF}; Offset, Count: longint);
  2349. begin
  2350.   case FEncoding of
  2351.   seUtf8:
  2352.     begin
  2353.       // one on one
  2354.       if StreamWrite(FStream, Buffer, Offset, Count) <> Count then
  2355.         raise EStreamError.Create(sxeCannotWriteToOutputStream);
  2356.     end
  2357.   else
  2358.     inherited;
  2359.   end;
  2360. end;
  2361. procedure TsdUtf8Stream.WriteByte(const B: byte);
  2362. var
  2363.   SA: string;
  2364.   SW: widestring;
  2365.   MustWrite: boolean;
  2366. begin
  2367.   case FEncoding of
  2368.   se8Bit,se16BitBE,se16BitLE,seUTF16BE,seUTF16LE:
  2369.     begin
  2370.       MustWrite := True;
  2371.       case Length(FBuffer) of
  2372.       0:
  2373.         begin
  2374.           FBuffer := char(B);
  2375.           if (B and $80) <> 0 then MustWrite := False;
  2376.         end;
  2377.       1:
  2378.         begin
  2379.           FBuffer := FBuffer + char(B);
  2380.           if (byte(FBuffer[1]) and $20) <> 0 then MustWrite := False;
  2381.         end;
  2382.       2: FBuffer := FBuffer + char(B);
  2383.       end;
  2384.       if MustWrite then begin
  2385.         if FEncoding = se8Bit then begin
  2386.           // Convert to ansi
  2387.           SA := sdUtf8ToAnsi(FBuffer);
  2388.           // write out
  2389.           if length(SA) = 1 then
  2390.             if FStream.Write(SA[1], 1) <> 1 then
  2391.               raise EStreamError.Create(sxeCannotWriteToOutputStream);
  2392.         end else begin
  2393.           // Convert to unicode
  2394.           SW := sdUtf8ToUnicode(FBuffer);
  2395.           // write out
  2396.           if length(SW) = 1 then
  2397.             if FStream.Write(SW[1], 2) <> 2 then
  2398.               raise EStreamError.Create(sxeCannotWriteToOutputStream);
  2399.         end;
  2400.         FBuffer := '';
  2401.       end;
  2402.     end;
  2403.   seUTF8:
  2404.     begin
  2405.       // Just a flat write of one byte
  2406.       if FStream.Write(B, 1) <> 1 then
  2407.         raise EStreamError.Create(sxeCannotWriteToOutputStream);
  2408.     end;
  2409.   else
  2410.     raise EStreamError.Create(sxeUnsupportedEncoding);
  2411.   end;
  2412. end;
  2413. {$IFDEF CLR}
  2414. { TsdBufferedStream }
  2415. constructor TsdBufferedStream.Create(AStream: TStream; Owned: Boolean = False);
  2416. begin
  2417.   inherited Create;
  2418.   FStream := AStream;
  2419.   FOwned := Owned;
  2420. end;
  2421. destructor TsdBufferedStream.Destroy;
  2422. begin
  2423.   if FOwned then FreeAndNil(FStream);
  2424.   inherited Destroy;
  2425. end;
  2426. function TsdBufferedStream.Read(var Buffer: array of Byte; Offset, Count: Longint): Longint;
  2427. begin
  2428.   Result := FStream.Read(Buffer, Offset, Count);
  2429. end;
  2430. function TsdBufferedStream.Write(const Buffer: array of Byte; Offset, Count: Longint): Longint;
  2431. begin
  2432.   Result := FStream.Write(Buffer, Offset, Count);
  2433. end;
  2434. function TsdBufferedStream.Seek(const Offset: Int64; Origin: TSeekOrigin): Int64;
  2435. begin
  2436.   Result := FStream.Seek(Offset, Origin);
  2437. end;
  2438. procedure TsdBufferedStream.SetSize(NewSize: Int64);
  2439. begin
  2440.   FStream.Size := NewSize;
  2441. end;
  2442. {$ELSE}
  2443. { TsdBufferedReadStream }
  2444. const
  2445.   cMaxBufferSize = $10000; // 65536 bytes in the buffer
  2446. procedure TsdBufferedReadStream.CheckPosition;
  2447. var
  2448.   NewPage: integer;
  2449.   FStartPos: longint;
  2450. begin
  2451.   // Page and buffer position
  2452.   NewPage := FPosition div cMaxBufferSize;
  2453.   FBufPos := FPosition mod cMaxBufferSize;
  2454.   // Read new page if required
  2455.   if (NewPage <> FPage) then begin
  2456.     // New page and buffer
  2457.     FPage := NewPage;
  2458.     // Start position in stream
  2459.     FStartPos := FPage * cMaxBufferSize;
  2460.     FBufSize  := Min(cMaxBufferSize, FStream.Size - FStartPos);
  2461.     FStream.Seek(FStartPos, soBeginning);
  2462.     if FBufSize > 0 then
  2463.       FStream.Read(FBuffer^, FBufSize);
  2464.   end;
  2465.   FMustCheck := False;
  2466. end;
  2467. constructor TsdBufferedReadStream.Create(AStream: TStream; Owned: boolean);
  2468. begin
  2469.   inherited Create;
  2470.   FStream := AStream;
  2471.   FOwned := Owned;
  2472.   FMustCheck := True;
  2473.   FPage := -1; // Set to invalid number to force an update on first read
  2474.   ReallocMem(FBuffer, cMaxBufferSize);
  2475. end;
  2476. destructor TsdBufferedReadStream.Destroy;
  2477. begin
  2478.   if FOwned then FreeAndNil(FStream);
  2479.   ReallocMem(FBuffer, 0);
  2480.   inherited;
  2481. end;
  2482. function TsdBufferedReadStream.Read(var Buffer; Count: longint): Longint;
  2483. var
  2484.   Packet: PByte;
  2485.   PacketCount: integer;
  2486. begin
  2487.   // Set the right page
  2488.   if FMustCheck then CheckPosition;
  2489.   // Special case - read one byte, most often
  2490.   if (Count = 1) and (FBufPos < FBufSize - 1) then begin
  2491.     byte(Buffer) := FBuffer^[FBufPos];
  2492.     inc(FBufPos);
  2493.     inc(FPosition);
  2494.     Result := 1;
  2495.     exit;
  2496.   end;
  2497.   // general case
  2498.   Packet := @Buffer;
  2499.   Result := 0;
  2500.   while Count > 0 do begin
  2501.     PacketCount := min(FBufSize - FBufPos, Count);
  2502.     if PacketCount <= 0 then exit;
  2503.     Move(FBuffer^[FBufPos], Packet^, PacketCount);
  2504.     dec(Count, PacketCount);
  2505.     inc(Packet, PacketCount);
  2506.     inc(Result, PacketCount);
  2507.     inc(FPosition, PacketCount);
  2508.     inc(FBufPos, PacketCount);
  2509.     if FBufPos >= FBufSize then CheckPosition;
  2510.   end;
  2511. end;
  2512. function TsdBufferedReadStream.Seek(Offset: longint; Origin: Word): Longint;
  2513. begin
  2514.   case Origin of
  2515.   soFromBeginning:
  2516.     FPosition := Offset;
  2517.   soFromCurrent:
  2518.     begin
  2519.       // no need to check in this case - it is the GetPosition command
  2520.       if Offset = 0 then begin
  2521.         Result := FPosition;
  2522.         exit;
  2523.       end;
  2524.       FPosition := FPosition + Offset;
  2525.     end;
  2526.   soFromEnd:
  2527.     FPosition := FStream.Size + Offset;
  2528.   end;//case
  2529.   Result := FPosition;
  2530.   FMustCheck := True;
  2531. end;
  2532. function TsdBufferedReadStream.Write(const Buffer; Count: longint): Longint;
  2533. begin
  2534.   raise EStreamError.Create(sxeCannotWriteCodecForReading);
  2535. end;
  2536. { TsdBufferedWriteStream }
  2537. constructor TsdBufferedWriteStream.Create(AStream: TStream;
  2538.   Owned: boolean);
  2539. begin
  2540.   inherited Create;
  2541.   FStream := AStream;
  2542.   FOwned := Owned;
  2543.   ReallocMem(FBuffer, cMaxBufferSize);
  2544. end;
  2545. destructor TsdBufferedWriteStream.Destroy;
  2546. begin
  2547.   Flush;
  2548.   if FOwned then FreeAndNil(FStream);
  2549.   ReallocMem(FBuffer, 0);
  2550.   inherited;
  2551. end;
  2552. procedure TsdBufferedWriteStream.Flush;
  2553. begin
  2554.   // Write the buffer to the stream
  2555.   if FBufPos > 0 then begin
  2556.     FStream.Write(FBuffer^, FBufPos);
  2557.     FBufPos := 0;
  2558.   end;
  2559. end;
  2560. function TsdBufferedWriteStream.Read(var Buffer; Count: longint): Longint;
  2561. begin
  2562.   raise EStreamError.Create(sxeCannotReadCodecForWriting);
  2563. end;
  2564. function TsdBufferedWriteStream.Seek(Offset: longint; Origin: Word): Longint;
  2565. begin
  2566.   case Origin of
  2567.   soFromBeginning:
  2568.     if Offset = FPosition then begin
  2569.       Result := FPosition;
  2570.       exit;
  2571.     end;
  2572.   soFromCurrent:
  2573.     begin
  2574.       // GetPosition command
  2575.       if Offset = 0 then begin
  2576.         Result := FPosition;
  2577.         exit;
  2578.       end;
  2579.     end;
  2580.   soFromEnd:
  2581.     if Offset = 0 then begin
  2582.       Result := FPosition;
  2583.       exit;
  2584.     end;
  2585.   end;//case
  2586.   raise EStreamError.Create(sxeCannotPerformSeek);
  2587. end;
  2588. function TsdBufferedWriteStream.Write(const Buffer; Count: longint): Longint;
  2589. var
  2590.   Packet: PByte;
  2591.   PacketCount: integer;
  2592. begin
  2593.   // Special case - read less bytes than would fill buffersize
  2594.   if (FBufPos + Count < cMaxBufferSize) then begin
  2595.     Move(Buffer, FBuffer^[FBufPos], Count);
  2596.     inc(FBufPos, Count);
  2597.     inc(FPosition, Count);
  2598.     Result := Count;
  2599.     exit;
  2600.   end;
  2601.   // general case that wraps buffer
  2602.   Packet := @Buffer;
  2603.   Result := 0;
  2604.   while Count > 0 do begin
  2605.     PacketCount := min(cMaxBufferSize - FBufPos, Count);
  2606.     if PacketCount <= 0 then exit;
  2607.     Move(Packet^, FBuffer^[FBufPos], PacketCount);
  2608.     dec(Count,     PacketCount);
  2609.     inc(Result,    PacketCount);
  2610.     inc(FPosition, PacketCount);
  2611.     inc(Packet,    PacketCount);
  2612.     inc(FBufPos,   PacketCount);
  2613.     if FBufPos = cMaxBufferSize then Flush;
  2614.   end;
  2615. end;
  2616. {$ENDIF}
  2617. { TsdSurplusReader }
  2618. constructor TsdSurplusReader.Create(AStream: TStream);
  2619. begin
  2620.   inherited Create;
  2621.   FStream := AStream;
  2622. end;
  2623. function TsdSurplusReader.ReadChar(var Ch: char): integer;
  2624. begin
  2625.   if length(FSurplus) > 0 then begin
  2626.     Ch := FSurplus[1];
  2627.     FSurplus := copy(FSurplus, 2, length(FSurplus) - 1);
  2628.     Result := 1;
  2629.   end else
  2630.     Result := FStream.Read(Ch, 1);
  2631. end;
  2632. function TsdSurplusReader.ReadCharSkipBlanks(var Ch: char): boolean;
  2633. begin
  2634.   Result := False;
  2635.   repeat
  2636.     // Read character, exit if none available
  2637.     if ReadChar(Ch) = 0 then exit;
  2638.     // Skip if in controlchars
  2639.     if not (Ch in cControlchars) then break;
  2640.   until False;
  2641.   Result := True;
  2642. end;
  2643. { TsdStringBuilder }
  2644. procedure TsdStringBuilder.AddChar(Ch: Char);
  2645. begin
  2646.   inc(FCurrentIdx);
  2647.   Reallocate(FCurrentIdx);
  2648.   FData[FCurrentIdx] := Ch;
  2649. end;
  2650. procedure TsdStringBuilder.AddString(var S: string);
  2651. var
  2652.   {$IFDEF CLR}
  2653.   i: integer;
  2654.   {$ENDIF}
  2655.   Count: integer;
  2656. begin
  2657.   {$IFDEF CLR}
  2658.   Count := S.Length;
  2659.   {$ELSE}
  2660.   Count := System.length(S);
  2661.   {$ENDIF}
  2662.   if Count = 0 then exit;
  2663.   Reallocate(FCurrentIdx + Count);
  2664.   {$IFDEF CLR}
  2665.   for i := 1 to S.Length do
  2666.     FData[FCurrentIdx + i] := S[i];
  2667.   {$ELSE}
  2668.   Move(S[1], FData[FCurrentIdx + 1], Count);
  2669.   {$ENDIF}
  2670.   inc(FCurrentIdx, Count);
  2671. end;
  2672. procedure TsdStringBuilder.Clear;
  2673. begin
  2674.   FCurrentIdx := 0;
  2675. end;
  2676. function TsdStringBuilder.StringCopy(AFirst, ALength: integer): string;
  2677. begin
  2678.   if ALength > FCurrentIdx - AFirst + 1 then
  2679.     ALength := FCurrentIdx - AFirst + 1;
  2680.   Result := Copy(FData, AFirst, ALength);
  2681. end;
  2682. constructor TsdStringBuilder.Create;
  2683. begin
  2684.   inherited Create;
  2685.   SetLength(FData, 64);
  2686. end;
  2687. function TsdStringBuilder.GetData(Index: integer): Char;
  2688. begin
  2689.   Result := FData[Index];
  2690. end;
  2691. procedure TsdStringBuilder.Reallocate(RequiredLength: integer);
  2692. begin
  2693.   {$IFDEF CLR}
  2694.   while FData.Length < RequiredLength do
  2695.     SetLength(FData, FData.Length * 2);
  2696.   {$ELSE}
  2697.   while System.Length(FData) < RequiredLength do
  2698.     SetLength(FData, System.Length(FData) * 2);
  2699.   {$ENDIF}
  2700. end;
  2701. function TsdStringBuilder.Value: string;
  2702. begin
  2703.   Result := Copy(FData, 1, FCurrentIdx);
  2704. end;
  2705. initialization
  2706.   {$IFDEF TRIALXML}
  2707.   ShowMessage(
  2708.     'This is the unregistered version of NativeXml.pas'#13#13 +
  2709.     'Please visit http://www.simdesign.nl/xml.html to buy the'#13 +
  2710.     'registered version for Eur 29.95 (source included).');
  2711.   {$ENDIF}
  2712. end.