Browse Source

* fcl-xml, making progress with streaming API, implemented IXmlLineInfo interface and fixed reported locations for attributes and their child nodes.
+ Set Name and Value properties for DocumentType nodes.
* Unified HandleEntityStart and HandleEntityEnd code for entity references in content and in attributes.

git-svn-id: trunk@20524 -

sergei 13 years ago
parent
commit
c2a2f88bd4
2 changed files with 81 additions and 19 deletions
  1. 72 19
      packages/fcl-xml/src/xmlread.pp
  2. 9 0
      packages/fcl-xml/src/xmlutils.pp

+ 72 - 19
packages/fcl-xml/src/xmlread.pp

@@ -268,7 +268,7 @@ type
 
 
   TLiteralType = (ltPlain, ltPubid, ltEntity);
   TLiteralType = (ltPlain, ltPubid, ltEntity);
 
 
-  TXMLTextReader = class(TXMLReader)
+  TXMLTextReader = class(TXMLReader, IXmlLineInfo)
   private
   private
     FSource: TXMLCharSource;
     FSource: TXMLCharSource;
     FNameTable: THashTable;
     FNameTable: THashTable;
@@ -341,6 +341,9 @@ type
     procedure SetNodeInfoWithValue(typ: TXMLNodeType; AName: PHashItem = nil);
     procedure SetNodeInfoWithValue(typ: TXMLNodeType; AName: PHashItem = nil);
     function SetupFakeLF(nextstate: TXMLToken): Boolean;
     function SetupFakeLF(nextstate: TXMLToken): Boolean;
     function AddId(aNodeData: PNodeData): Boolean;
     function AddId(aNodeData: PNodeData): Boolean;
+    function QueryInterface(constref iid: TGUID; out obj): HRESULT; {$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF};
+    function _AddRef: Longint; {$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF};
+    function _Release: Longint; {$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF};
   protected
   protected
     FNesting: Integer;
     FNesting: Integer;
     FCurrNode: PNodeData;
     FCurrNode: PNodeData;
@@ -392,6 +395,7 @@ type
     procedure ParseAttribute(ElDef: TElementDecl);
     procedure ParseAttribute(ElDef: TElementDecl);
     function  ReadTopLevel: Boolean;
     function  ReadTopLevel: Boolean;
     procedure NextAttrValueChunk;
     procedure NextAttrValueChunk;
+    function  GetHasLineInfo: Boolean;
     function  GetLineNumber: Integer;
     function  GetLineNumber: Integer;
     function  GetLinePosition: Integer;
     function  GetLinePosition: Integer;
   public
   public
@@ -1000,6 +1004,24 @@ end;
 
 
 { TXMLTextReader }
 { TXMLTextReader }
 
 
+function TXMLTextReader.QueryInterface(constref iid: TGUID; out obj): HRESULT; {$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF};
+begin
+  if GetInterface(iid,obj) then
+    result := S_OK
+  else
+    result:= E_NOINTERFACE;
+end;
+
+function TXMLTextReader._AddRef: Longint; {$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF};
+begin
+  result := -1;
+end;
+
+function TXMLTextReader._Release: Longint; {$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF};
+begin
+  result := -1;
+end;
+
 procedure TXMLTextReader.ConvertSource(SrcIn: TXMLInputSource; out SrcOut: TXMLCharSource);
 procedure TXMLTextReader.ConvertSource(SrcIn: TXMLInputSource; out SrcOut: TXMLCharSource);
 begin
 begin
   SrcOut := nil;
   SrcOut := nil;
@@ -1746,9 +1768,12 @@ var
   start: TObject;
   start: TObject;
   curr: PNodeData;
   curr: PNodeData;
   StartPos: Integer;
   StartPos: Integer;
+  StartLoc: TLocation;
   entName: PHashItem;
   entName: PHashItem;
 begin
 begin
   SkipQuote(Delim);
   SkipQuote(Delim);
+  AttrData^.FLoc2 := FTokenStart;
+  StartLoc := FTokenStart;
   curr := AttrData;
   curr := AttrData;
   FValue.Length := 0;
   FValue.Length := 0;
   StartPos := 0;
   StartPos := 0;
@@ -1767,10 +1792,16 @@ begin
       if ((ent = nil) or (not FExpandEntities)) and (FSource.FEntity = start) then
       if ((ent = nil) or (not FExpandEntities)) and (FSource.FEntity = start) then
       begin
       begin
         if FValue.Length > StartPos then
         if FValue.Length > StartPos then
+        begin
           AllocAttributeValueChunk(curr, StartPos);
           AllocAttributeValueChunk(curr, StartPos);
+          curr^.FLoc := StartLoc;
+        end;
         AllocAttributeValueChunk(curr, FValue.Length);
         AllocAttributeValueChunk(curr, FValue.Length);
         curr^.FNodeType := ntEntityReference;
         curr^.FNodeType := ntEntityReference;
         curr^.FQName := entName;
         curr^.FQName := entName;
+        StoreLocation(StartLoc);
+        curr^.FLoc := StartLoc;
+        Dec(curr^.FLoc.LinePos, FName.Length+1);
       end;
       end;
       StartPos := FValue.Length;
       StartPos := FValue.Length;
       if Assigned(ent) then
       if Assigned(ent) then
@@ -1796,7 +1827,10 @@ begin
   begin
   begin
     FAttrCleanupFlag := True;
     FAttrCleanupFlag := True;
     if FValue.Length > StartPos then
     if FValue.Length > StartPos then
+    begin
       AllocAttributeValueChunk(curr, StartPos);
       AllocAttributeValueChunk(curr, StartPos);
+      curr^.FLoc := StartLoc;
+    end;
   end;
   end;
   if nonCDATA then
   if nonCDATA then
     BufNormalize(FValue, attrData^.FDenormalized)
     BufNormalize(FValue, attrData^.FDenormalized)
@@ -2193,6 +2227,7 @@ end;
 procedure TXMLTextReader.ParseDoctypeDecl;    // [28]
 procedure TXMLTextReader.ParseDoctypeDecl;    // [28]
 var
 var
   Src: TXMLCharSource;
   Src: TXMLCharSource;
+  DTDName: PHashItem;
 begin
 begin
   if FState >= rsDTD then
   if FState >= rsDTD then
     FatalError('Markup declaration is not allowed here');
     FatalError('Markup declaration is not allowed here');
@@ -2208,6 +2243,7 @@ begin
 
 
   CheckName;
   CheckName;
   SetString(FDocType.FName, FName.Buffer, FName.Length);
   SetString(FDocType.FName, FName.Buffer, FName.Length);
+  DTDName := FNameTable.FindOrAdd(FName.Buffer, FName.Length);
   SkipS(True);
   SkipS(True);
   ParseExternalID(FDocType.FSystemID, FDocType.FPublicID, False);
   ParseExternalID(FDocType.FSystemID, FDocType.FPublicID, False);
   SkipS;
   SkipS;
@@ -2249,7 +2285,9 @@ begin
     end;
     end;
   end;
   end;
   FState := rsAfterDTD;
   FState := rsAfterDTD;
-  FCurrNode^.FNodeType := ntDocumentType;
+  FValue.Length := 0;
+  BufAppendString(FValue, FDocType.FInternalSubset);
+  SetNodeInfoWithValue(ntDocumentType, DTDName);
 end;
 end;
 
 
 procedure TXMLTextReader.ExpectEq;   // [25]
 procedure TXMLTextReader.ExpectEq;   // [25]
@@ -2792,6 +2830,8 @@ end;
 procedure TXMLTextReader.Close;
 procedure TXMLTextReader.Close;
 begin
 begin
   FReadState := rsClosed;
   FReadState := rsClosed;
+  FTokenStart.Line := 0;
+  FTokenStart.LinePos := 0;
 end;
 end;
 
 
 function TXMLTextReader.GetAttributeCount: Integer;
 function TXMLTextReader.GetAttributeCount: Integer;
@@ -2882,14 +2922,27 @@ begin
   result := FSource.SystemID;
   result := FSource.SystemID;
 end;
 end;
 
 
+{ IXmlLineInfo methods }
+
+function TXMLTextReader.GetHasLineInfo: Boolean;
+begin
+  result := True;
+end;
+
 function TXMLTextReader.GetLineNumber: Integer;
 function TXMLTextReader.GetLineNumber: Integer;
 begin
 begin
-  result := FCurrNode^.FLoc.Line;
+  if (FCurrNode^.FNodeType in [ntElement,ntAttribute]) or (FAttrReadState <> arsNone) then
+    result := FCurrNode^.FLoc.Line
+  else
+    result := FTokenStart.Line;
 end;
 end;
 
 
 function TXMLTextReader.GetLinePosition: Integer;
 function TXMLTextReader.GetLinePosition: Integer;
 begin
 begin
-  result := FCurrNode^.FLoc.LinePos;
+  if (FCurrNode^.FNodeType in [ntElement,ntAttribute]) or (FAttrReadState <> arsNone) then
+    result := FCurrNode^.FLoc.LinePos
+  else
+    result := FTokenStart.LinePos;
 end;
 end;
 
 
 function TXMLTextReader.LookupNamespace(const APrefix: XMLString): XMLString;
 function TXMLTextReader.LookupNamespace(const APrefix: XMLString): XMLString;
@@ -2999,6 +3052,7 @@ begin
   end;
   end;
 
 
   FCurrNode := @FNodeStack[FNesting+FAttrCount+1];
   FCurrNode := @FNodeStack[FNesting+FAttrCount+1];
+  StoreLocation(FCurrNode^.FLoc);
   FValue.Length := 0;
   FValue.Length := 0;
   if FAttrReadState = arsText then
   if FAttrReadState = arsText then
   repeat
   repeat
@@ -3033,19 +3087,12 @@ begin
 
 
   if tok = arsEntity then
   if tok = arsEntity then
   begin
   begin
-    FCurrNode^.FNodeType := ntEntityReference;
-    FCurrNode^.FQName := FNameTable.FindOrAdd(FName.Buffer, FName.Length);
-    FCurrNode^.FValueStart := nil;
-    FCurrNode^.FValueLength := 0;
-    FCurrNode^.FValueStr := '';
+    HandleEntityStart;
     FAttrReadState := arsText;
     FAttrReadState := arsText;
   end
   end
   else if tok = arsEntityEnd then
   else if tok = arsEntityEnd then
   begin
   begin
-    ContextPop(True);
-    Dec(FNesting);
-    FCurrNode := @FNodeStack[FNesting+FAttrCount+1];
-    FCurrNode^.FNodeType := ntEndEntity;
+    HandleEntityEnd;
     FAttrReadState := arsText;
     FAttrReadState := arsText;
   end;
   end;
 end;
 end;
@@ -3219,21 +3266,26 @@ end;
 
 
 procedure TXMLTextReader.HandleEntityStart;
 procedure TXMLTextReader.HandleEntityStart;
 begin
 begin
-  FCurrNode := @FNodeStack[FNesting];
+  FCurrNode := @FNodeStack[FNesting+(FAttrCount+1)*ord(FAttrReadState<>arsNone)];
   FCurrNode^.FNodeType := ntEntityReference;
   FCurrNode^.FNodeType := ntEntityReference;
   FCurrNode^.FQName := FNameTable.FindOrAdd(FName.Buffer, FName.Length);
   FCurrNode^.FQName := FNameTable.FindOrAdd(FName.Buffer, FName.Length);
+  FCurrNode^.FColonPos := -1;
   FCurrNode^.FValueStart := nil;
   FCurrNode^.FValueStart := nil;
   FCurrNode^.FValueLength := 0;
   FCurrNode^.FValueLength := 0;
+  FCurrNode^.FValueStr := '';
+  StoreLocation(FCurrNode^.FLoc);
+  { point past '&' to first char of entity name }
+  Dec(FCurrNode^.FLoc.LinePos, FName.Length+1);
 end;
 end;
 
 
 procedure TXMLTextReader.HandleEntityEnd;
 procedure TXMLTextReader.HandleEntityEnd;
 begin
 begin
   ContextPop(True);
   ContextPop(True);
   if FNesting > 0 then Dec(FNesting);
   if FNesting > 0 then Dec(FNesting);
-  FCurrNode := @FNodeStack[FNesting];
+  FCurrNode := @FNodeStack[FNesting+(FAttrCount+1)*ord(FAttrReadState<>arsNone)];
   FCurrNode^.FNodeType := ntEndEntity;
   FCurrNode^.FNodeType := ntEndEntity;
-  // TODO: other properties of FCurrNode
-  FNext := xtText;
+  { point to trailing ';' }
+  Inc(FCurrNode^.FLoc.LinePos, Length(FCurrNode^.FQName^.Key));
 end;
 end;
 
 
 procedure TXMLTextReader.ResolveEntity;
 procedure TXMLTextReader.ResolveEntity;
@@ -3252,6 +3304,7 @@ begin
     if n <> FCurrNode then
     if n <> FCurrNode then
       n^ := FCurrNode^;
       n^ := FCurrNode^;
 
 
+    ent := nil;
     if Assigned(FDocType) then
     if Assigned(FDocType) then
       ent := FDocType.Entities.Get(PWideChar(n^.FQName^.Key),Length(n^.FQName^.Key)) as TEntityDecl;
       ent := FDocType.Entities.Get(PWideChar(n^.FQName^.Key),Length(n^.FQName^.Key)) as TEntityDecl;
     if ent = nil then
     if ent = nil then
@@ -3754,6 +3807,8 @@ begin
 
 
   FCurrNode := @FNodeStack[FNesting];  // move off the possible child
   FCurrNode := @FNodeStack[FNesting];  // move off the possible child
   FCurrNode^.FNodeType := ntEndElement;
   FCurrNode^.FNodeType := ntEndElement;
+  Inc(FTokenStart.LinePos, 2);         // move over '</' chars
+  FCurrNode^.FLoc := FTokenStart;
   ElName := FCurrNode^.FQName;
   ElName := FCurrNode^.FQName;
 
 
   CheckName;
   CheckName;
@@ -3766,7 +3821,6 @@ begin
     SkipS;
     SkipS;
     ExpectChar('>');
     ExpectChar('>');
   end;
   end;
-  Inc(FTokenStart.LinePos, 2);   // move over '</' chars
   FNext := xtPopElement;
   FNext := xtPopElement;
 end;
 end;
 
 
@@ -3822,7 +3876,6 @@ begin
 
 
   ExpectEq;
   ExpectEq;
   ExpectAttValue(attrData, Assigned(AttDef) and (AttDef.DataType <> dtCDATA));
   ExpectAttValue(attrData, Assigned(AttDef) and (AttDef.DataType <> dtCDATA));
-  attrData^.FLoc2 := FTokenStart;
 
 
   if Assigned(attrData^.FNsUri) then
   if Assigned(attrData^.FNsUri) then
   begin
   begin

+ 9 - 0
packages/fcl-xml/src/xmlutils.pp

@@ -147,6 +147,15 @@ type
     LinePos: Integer;
     LinePos: Integer;
   end;
   end;
 
 
+  IXmlLineInfo = interface(IInterface)['{FD0A892B-B26C-4954-9995-103B2A9D178A}']
+    function GetHasLineInfo: Boolean;
+    function GetLineNumber: Integer;
+    function GetLinePosition: Integer;
+    property HasLineInfo: Boolean read GetHasLineInfo;
+    property LineNumber: Integer read GetLineNumber;
+    property LinePosition: Integer read GetLinePosition;
+  end;
+
 { generic node info record, shared between DOM and reader }
 { generic node info record, shared between DOM and reader }
 
 
   PNodeData = ^TNodeData;
   PNodeData = ^TNodeData;