Преглед изворни кода

* xmlwrite.pp, continuing with streaming API:
* Factored WriteStartElement, WriteEndElement and WriteXmlDecl into separate procedures
* Handle element names independent from DOM.

git-svn-id: trunk@20949 -

sergei пре 13 година
родитељ
комит
12a4e1a37d
1 измењених фајлова са 78 додато и 34 уклоњено
  1. 78 34
      packages/fcl-xml/src/xmlwrite.pp

+ 78 - 34
packages/fcl-xml/src/xmlwrite.pp

@@ -50,13 +50,19 @@ type
     Prefix: PHashItem;
   end;
 
+  TNodeInfo = record
+    Name: XMLString;
+  end;
+
+  TNodeInfoArray = array of TNodeInfo;
+
   TXMLWriter = class(TObject)
   private
     FStream: TStream;
     FInsideTextNode: Boolean;
     FCanonical: Boolean;
     FIndent: XMLString;
-    FIndentCount: Integer;
+    FNesting: Integer;
     FBuffer: PChar;
     FBufPos: PChar;
     FCapacity: Integer;
@@ -65,12 +71,15 @@ type
     FAttrFixups: TFPList;
     FScratch: TFPList;
     FNSDefs: TFPList;
+    FNodes: TNodeInfoArray;
+    procedure WriteXMLDecl(const aVersion, aEncoding: XMLString;
+      aStandalone: Integer);
     procedure wrtChars(Src: PWideChar; Length: Integer);
-    procedure IncIndent;
-    procedure DecIndent; {$IFDEF HAS_INLINE} inline; {$ENDIF}
+    procedure IncNesting;
+    procedure DecNesting; {$IFDEF HAS_INLINE} inline; {$ENDIF}
     procedure wrtStr(const ws: XMLString); {$IFDEF HAS_INLINE} inline; {$ENDIF}
     procedure wrtChr(c: WideChar); {$IFDEF HAS_INLINE} inline; {$ENDIF}
-    procedure wrtIndent; {$IFDEF HAS_INLINE} inline; {$ENDIF}
+    procedure wrtIndent(EndElement: Boolean = False);
     procedure wrtQuotedLiteral(const ws: XMLString);
     procedure ConvWrite(const s: XMLString; const SpecialChars: TSetOfChar;
       const SpecialCharCallback: TSpecialCharCallback);
@@ -90,6 +99,8 @@ type
     procedure VisitDocumentType(Node: TDOMNode);
     procedure VisitPI(Node: TDOMNode);
 
+    procedure WriteStartElement(const Name: XMLString);
+    procedure WriteEndElement(shortForm: Boolean);
     procedure WriteProcessingInstruction(const Target, Data: XMLString);
     procedure WriteEntityRef(const Name: XMLString);
     procedure WriteAttributeString(const Name, Value: XMLString);
@@ -169,7 +180,8 @@ begin
   else
     FIndent[2] := ' ';
   for I := 3 to 100 do FIndent[I] := ' ';
-  FIndentCount := 0;
+  FNesting := 0;
+  SetLength(FNodes, 16);
   FNSHelper := TNSSupport.Create(ANameTable);
   FScratch := TFPList.Create;
   FNSDefs := TFPList.Create;
@@ -264,29 +276,31 @@ begin
   Inc(FBufPos);
 end;
 
-procedure TXMLWriter.wrtIndent; { inline }
+procedure TXMLWriter.wrtIndent(EndElement: Boolean);
 begin
-  wrtChars(PWideChar(FIndent), FIndentCount*2+Length(FLineBreak));
+  wrtChars(PWideChar(FIndent), (FNesting-ord(EndElement))*2+Length(FLineBreak));
 end;
 
-procedure TXMLWriter.IncIndent;
+procedure TXMLWriter.IncNesting;
 var
   I, NewLen, OldLen: Integer;
 begin
-  Inc(FIndentCount);
-  if Length(FIndent) < 2 * FIndentCount then
+  Inc(FNesting);
+  if FNesting >= Length(FNodes) then
+    SetLength(FNodes, FNesting+8);
+  if Length(FIndent) < 2 * FNesting then
   begin
     OldLen := Length(FIndent);
-    NewLen := 4 * FIndentCount;
+    NewLen := 4 * FNesting;
     SetLength(FIndent, NewLen);
     for I := OldLen to NewLen do
       FIndent[I] := ' ';
   end;
 end;
 
-procedure TXMLWriter.DecIndent; { inline }
+procedure TXMLWriter.DecNesting; { inline }
 begin
-  if FIndentCount>0 then dec(FIndentCount);
+  if FNesting>0 then dec(FNesting);
 end;
 
 procedure TXMLWriter.ConvWrite(const s: XMLString; const SpecialChars: TSetOfChar;
@@ -579,11 +593,7 @@ var
   child: TDOMNode;
   SavedInsideTextNode: Boolean;
 begin
-  if not FInsideTextNode then
-    wrtIndent;
-  FNSHelper.PushScope;
-  wrtChr('<');
-  wrtStr(TDOMElement(node).TagName);
+  WriteStartElement(TDOMElement(node).TagName);
 
   if nfLevel2 in node.Flags then
     NamespaceFixup(TDOMElement(node))
@@ -596,7 +606,7 @@ begin
     end;
   Child := node.FirstChild;
   if Child = nil then
-    wrtChars('/>', 2)
+    WriteEndElement(True)
   else
   begin
     // TODO: presence of zero-length textnodes triggers the indenting logic,
@@ -604,19 +614,40 @@ begin
     SavedInsideTextNode := FInsideTextNode;
     wrtChr('>');
     FInsideTextNode := FCanonical or (Child.NodeType in [TEXT_NODE, CDATA_SECTION_NODE]);
-    IncIndent;
     repeat
       WriteNode(Child);
       Child := Child.NextSibling;
     until Child = nil;
-    DecIndent;
     if not (node.LastChild.NodeType in [TEXT_NODE, CDATA_SECTION_NODE]) then
-      wrtIndent;
+      wrtIndent(True);
     FInsideTextNode := SavedInsideTextNode;
+    writeEndElement(False);
+  end;
+end;
+
+procedure TXMLWriter.WriteStartElement(const Name: XMLString);
+begin
+  if not FInsideTextNode then
+    wrtIndent;
+
+  FNSHelper.PushScope;
+  IncNesting;
+  wrtChr('<');
+  wrtStr(Name);
+  FNodes[FNesting].Name := Name;
+end;
+
+procedure TXMLWriter.WriteEndElement(shortForm: Boolean);
+begin
+  if shortForm then
+    wrtChars('/>', 2)
+  else
+  begin
     wrtChars('</', 2);
-    wrtStr(TDOMElement(Node).TagName);
+    wrtStr(FNodes[FNesting].Name);
     wrtChr('>');
   end;
+  DecNesting;
   FNSHelper.PopScope;
 end;
 
@@ -679,26 +710,39 @@ begin
   wrtChars('-->', 3);
 end;
 
-procedure TXMLWriter.VisitDocument(node: TDOMNode);
-var
-  child: TDOMNode;
+procedure TXMLWriter.WriteXMLDecl(const aVersion, aEncoding: XMLString; aStandalone: Integer);
 begin
   wrtStr('<?xml version="');
-  // Definitely should not escape anything here
-  if Length(TXMLDocument(node).XMLVersion) > 0 then
-    wrtStr(TXMLDocument(node).XMLVersion)
+  if aVersion <> '' then
+    wrtStr(aVersion)
   else
     wrtStr('1.0');
   wrtChr('"');
 
-  // Here we ignore doc.xmlEncoding and write a fixed utf-8 label,
-  // because it is the only output encoding currently supported.
-  wrtStr(' encoding="utf-8"');
+  wrtStr(' encoding="');
+  wrtStr(aEncoding);
+  wrtChr('"');
 
-  if TXMLDocument(node).xmlStandalone then
-    wrtStr(' standalone="yes"');
+  if aStandalone >= 0 then
+  begin
+    wrtStr(' standalone="');
+    if aStandalone > 0 then
+      wrtStr('yes')
+    else
+      wrtStr('no');
+    wrtChr('"');
+  end;
 
   wrtStr('?>');
+end;
+
+procedure TXMLWriter.VisitDocument(node: TDOMNode);
+var
+  child: TDOMNode;
+begin
+  // Here we ignore doc.xmlEncoding and write a fixed utf-8 label,
+  // because it is the only output encoding currently supported.
+  WriteXMLDecl(TXMLDocument(node).XMLVersion, 'utf-8', (ord(TXMLDocument(node).XMLStandalone)-1) or 1);
 
   // TODO: now handled as a regular PI, remove this?
   if node is TXMLDocument then