浏览代码

* fcl-xml, making progress with streaming API, moved DOM-dependent stuff from TXMLTextReader to the TLoader helper object. Now TXMLTextReader class is almost free from direct DOM dependencies (indirect ones like TDOMParser are still present, also entity handling is still intertwined too closely).

git-svn-id: trunk@20467 -
sergei 13 年之前
父节点
当前提交
a417e9d0b6
共有 1 个文件被更改,包括 279 次插入231 次删除
  1. 279 231
      packages/fcl-xml/src/xmlread.pp

+ 279 - 231
packages/fcl-xml/src/xmlread.pp

@@ -276,6 +276,7 @@ type
     FNameTable: THashTable;
     FNameTable: THashTable;
     FCtrl: TDOMParser;
     FCtrl: TDOMParser;
     FXML11: Boolean;
     FXML11: Boolean;
+    FNameTableOwned: Boolean;
     FState: TXMLReadState;
     FState: TXMLReadState;
     FHavePERefs: Boolean;
     FHavePERefs: Boolean;
     FInsideDecl: Boolean;
     FInsideDecl: Boolean;
@@ -321,7 +322,6 @@ type
     procedure SetEOFState;
     procedure SetEOFState;
     procedure SkipQuote(out Delim: WideChar; required: Boolean = True);
     procedure SkipQuote(out Delim: WideChar; required: Boolean = True);
     procedure Initialize(ASource: TXMLCharSource);
     procedure Initialize(ASource: TXMLCharSource);
-    procedure NSPrepare;
     procedure EntityToSource(AEntity: TEntityDecl; out Src: TXMLCharSource);
     procedure EntityToSource(AEntity: TEntityDecl; out Src: TXMLCharSource);
     function ContextPush(AEntity: TEntityDecl): Boolean;
     function ContextPush(AEntity: TEntityDecl): Boolean;
     function ContextPop(Forced: Boolean = False): Boolean;
     function ContextPop(Forced: Boolean = False): Boolean;
@@ -383,7 +383,6 @@ type
     procedure ExpectAttValue(attrData: PNodeData; NonCDATA: Boolean);   // [10]
     procedure ExpectAttValue(attrData: PNodeData; NonCDATA: Boolean);   // [10]
     procedure ParseComment(discard: Boolean);                           // [15]
     procedure ParseComment(discard: Boolean);                           // [15]
     procedure ParsePI;                                                  // [16]
     procedure ParsePI;                                                  // [16]
-    function CreatePINode: TDOMNode;
     procedure ParseXmlOrTextDecl(TextDecl: Boolean);
     procedure ParseXmlOrTextDecl(TextDecl: Boolean);
     procedure ExpectEq;
     procedure ExpectEq;
     procedure ParseDoctypeDecl;                                         // [28]
     procedure ParseDoctypeDecl;                                         // [28]
@@ -391,12 +390,10 @@ type
     procedure ParseIgnoreSection;
     procedure ParseIgnoreSection;
     procedure ParseStartTag;                                            // [39]
     procedure ParseStartTag;                                            // [39]
     procedure ParseEndTag;                                              // [42]
     procedure ParseEndTag;                                              // [42]
-    function DoStartElement: TDOMElement;
     procedure HandleEntityStart;
     procedure HandleEntityStart;
     procedure HandleEntityEnd;
     procedure HandleEntityEnd;
     procedure DoStartEntity;
     procedure DoStartEntity;
     procedure ParseAttribute(ElDef: TElementDecl);
     procedure ParseAttribute(ElDef: TElementDecl);
-    procedure ParseContent(cursor: TDOMNode_WithChildren);              // [43]
     function  ReadTopLevel: Boolean;
     function  ReadTopLevel: Boolean;
     procedure NextAttrValueChunk;
     procedure NextAttrValueChunk;
   public
   public
@@ -452,16 +449,32 @@ type
     procedure ValidationErrorWithName(const Msg: string; LineOffs: Integer = -1);
     procedure ValidationErrorWithName(const Msg: string; LineOffs: Integer = -1);
     procedure DTDReloadHook;
     procedure DTDReloadHook;
     procedure ConvertSource(SrcIn: TXMLInputSource; out SrcOut: TXMLCharSource);
     procedure ConvertSource(SrcIn: TXMLInputSource; out SrcOut: TXMLCharSource);
-    function DoCDSect(ch: PWideChar; Count: Integer): TDOMNode;
     procedure DoNotationDecl(const aName, aPubID, aSysID: XMLString);
     procedure DoNotationDecl(const aName, aPubID, aSysID: XMLString);
+    procedure SetOptions(AParser: TDOMParser);
   public
   public
-    doc: TDOMDocument;
+    { Entity loading still needs to reference the document, at least as an opaque pointer }
+    FDoc: TObject;
     constructor Create; overload;
     constructor Create; overload;
-    constructor Create(AParser: TDOMParser); overload;
+    constructor Create(ASrc: TXMLCharSource; ANameTable: THashTable); overload;
+    constructor Create(ASrc: TXMLCharSource; AParent: TXMLTextReader); overload;
+    constructor Create(const uri: XMLString; ANameTable: THashTable; AParser: TDOMParser); overload;
+    constructor Create(ASrc: TXMLInputSource; ANameTable: THashTable; AParser: TDOMParser); overload;
     destructor Destroy; override;
     destructor Destroy; override;
-    procedure ProcessXML(ASource: TXMLCharSource);                // [1]
-    procedure ProcessFragment(ASource: TXMLCharSource; AOwner: TDOMNode);
-    procedure ProcessDTD(ASource: TXMLCharSource);               // ([29])
+    procedure AfterConstruction; override;
+  end;
+
+  TLoader = object
+    doc: TDOMDocument;
+    reader: TXMLTextReader;
+    function DoStartElement: TDOMElement;
+    function DoCDSect(ch: PWideChar; Count: Integer): TDOMNode;
+    function CreatePINode: TDOMNode;
+    procedure ParseContent(cursor: TDOMNode_WithChildren);
+
+    procedure ProcessXML(ADoc: TDOMDocument; AReader: TXMLTextReader);
+    procedure ProcessFragment(AOwner: TDOMNode; AReader: TXMLTextReader);
+    procedure ProcessDTD(ADoc: TDOMDocument; AReader: TXMLTextReader);
+    procedure ProcessEntity(ADoc: TObject; AReader: TXMLTextReader; AEntity: TEntityDecl);
   end;
   end;
 
 
 const
 const
@@ -564,41 +577,39 @@ end;
 
 
 procedure TDOMParser.Parse(Src: TXMLInputSource; out ADoc: TXMLDocument);
 procedure TDOMParser.Parse(Src: TXMLInputSource; out ADoc: TXMLDocument);
 var
 var
-  InputSrc: TXMLCharSource;
+  Reader: TXMLTextReader;
+  ldr: TLoader;
 begin
 begin
-  with TXMLTextReader.Create(Self) do
+  ADoc := TXMLDocument.Create;
+  Reader := TXMLTextReader.Create(Src, ADoc.Names, Self);
   try
   try
-    ConvertSource(Src, InputSrc);  // handles 'no-input-specified' case
-    ProcessXML(InputSrc)
+    ldr.ProcessXML(ADoc, Reader);
   finally
   finally
-    ADoc := TXMLDocument(doc);
-    Free;
+    Reader.Free;
   end;
   end;
 end;
 end;
 
 
 procedure TDOMParser.ParseUri(const URI: XMLString; out ADoc: TXMLDocument);
 procedure TDOMParser.ParseUri(const URI: XMLString; out ADoc: TXMLDocument);
 var
 var
-  Src: TXMLCharSource;
+  Reader: TXMLTextReader;
+  ldr: TLoader;
 begin
 begin
-  ADoc := nil;
-  with TXMLTextReader.Create(Self) do
+  ADoc := TXMLDocument.Create;
+  Reader := TXMLTextReader.Create(URI, ADoc.Names, Self);
   try
   try
-    if ResolveResource(URI, '', '', Src) then
-      ProcessXML(Src)
-    else
-      DoErrorPos(esFatal, 'The specified URI could not be resolved', NullLocation);
+    ldr.ProcessXML(ADoc, Reader)
   finally
   finally
-    ADoc := TXMLDocument(doc);
-    Free;
+    Reader.Free;
   end;
   end;
 end;
 end;
 
 
 function TDOMParser.ParseWithContext(Src: TXMLInputSource;
 function TDOMParser.ParseWithContext(Src: TXMLInputSource;
   Context: TDOMNode; Action: TXMLContextAction): TDOMNode;
   Context: TDOMNode; Action: TXMLContextAction): TDOMNode;
 var
 var
-  InputSrc: TXMLCharSource;
   Frag: TDOMDocumentFragment;
   Frag: TDOMDocumentFragment;
   node: TDOMNode;
   node: TDOMNode;
+  reader: TXMLTextReader;
+  ldr: TLoader;
 begin
 begin
   if Action in [xaInsertBefore, xaInsertAfter, xaReplace] then
   if Action in [xaInsertBefore, xaInsertAfter, xaReplace] then
     node := Context.ParentNode
     node := Context.ParentNode
@@ -611,12 +622,11 @@ begin
   if not (node.NodeType in [ELEMENT_NODE, DOCUMENT_FRAGMENT_NODE]) then
   if not (node.NodeType in [ELEMENT_NODE, DOCUMENT_FRAGMENT_NODE]) then
     raise EDOMHierarchyRequest.Create('DOMParser.ParseWithContext');
     raise EDOMHierarchyRequest.Create('DOMParser.ParseWithContext');
 
 
-  with TXMLTextReader.Create(Self) do
+  reader := TXMLTextReader.Create(Src, Context.OwnerDocument.Names, Self);
   try
   try
-    ConvertSource(Src, InputSrc);    // handles 'no-input-specified' case
     Frag := Context.OwnerDocument.CreateDocumentFragment;
     Frag := Context.OwnerDocument.CreateDocumentFragment;
     try
     try
-      ProcessFragment(InputSrc, Frag);
+      ldr.ProcessFragment(Frag, reader);
       Result := Frag.FirstChild;
       Result := Frag.FirstChild;
       case Action of
       case Action of
         xaAppendAsChildren: Context.AppendChild(Frag);
         xaAppendAsChildren: Context.AppendChild(Frag);
@@ -633,7 +643,7 @@ begin
       Frag.Free;
       Frag.Free;
     end;
     end;
   finally
   finally
-    Free;
+    reader.Free;
   end;
   end;
 end;
 end;
 
 
@@ -1275,9 +1285,8 @@ begin
   SetLength(FValidators, 16);
   SetLength(FValidators, 16);
 end;
 end;
 
 
-constructor TXMLTextReader.Create(AParser: TDOMParser);
+procedure TXMLTextReader.SetOptions(AParser: TDOMParser);
 begin
 begin
-  Create;
   FCtrl := AParser;
   FCtrl := AParser;
   if FCtrl = nil then
   if FCtrl = nil then
     Exit;
     Exit;
@@ -1293,6 +1302,51 @@ begin
   FMaxChars := FCtrl.Options.MaxChars;
   FMaxChars := FCtrl.Options.MaxChars;
 end;
 end;
 
 
+constructor TXMLTextReader.Create(ASrc: TXMLInputSource; ANameTable: THashTable; AParser: TDOMParser);
+var
+  InputSrc: TXMLCharSource;
+begin
+  Create;
+  SetOptions(AParser);
+  FNameTable := ANameTable;
+  ConvertSource(ASrc, InputSrc);
+  FSource := InputSrc;
+  FSource.FReader := Self;
+end;
+
+constructor TXMLTextReader.Create(const uri: XMLString; ANameTable: THashTable; AParser: TDOMParser);
+begin
+  Create;
+  SetOptions(AParser);
+  FNameTable := ANameTable;
+  { TODO: should not open file in ResolveResource, but do it when Read() is called
+    for the first time }
+  if ResolveResource(uri, '', '', FSource) then
+    FSource.FReader := Self
+  else
+    DoErrorPos(esFatal, 'The specified URI could not be resolved', NullLocation);
+end;
+
+
+constructor TXMLTextReader.Create(ASrc: TXMLCharSource; ANameTable: THashTable);
+begin
+  if ANameTable = nil then
+  begin
+    ANameTable := THashTable.Create(256, True);
+    FNameTableOwned := True;
+  end;
+  FNameTable := ANameTable;
+  Create;
+  FSource := ASrc;
+  FSource.FReader := Self;
+end;
+
+constructor TXMLTextReader.Create(ASrc: TXMLCharSource; AParent: TXMLTextReader);
+begin
+  Create(ASrc, AParent.FNameTable);
+  SetOptions(AParent.FCtrl);
+end;
+
 destructor TXMLTextReader.Destroy;
 destructor TXMLTextReader.Destroy;
 var
 var
   i: Integer;
   i: Integer;
@@ -1314,16 +1368,17 @@ begin
   FIDMap.Free;
   FIDMap.Free;
   FForwardRefs.Free;
   FForwardRefs.Free;
   FAttrChunks.Free;
   FAttrChunks.Free;
-  if doc = nil then
+  if FNameTableOwned then
     FNameTable.Free;
     FNameTable.Free;
   inherited Destroy;
   inherited Destroy;
 end;
 end;
 
 
 
 
-{ Must be executed after doc has been set.
-  After introducing own NameTable, merge this into constructor }
-procedure TXMLTextReader.NSPrepare;
+procedure TXMLTextReader.AfterConstruction;
 begin
 begin
+  FNesting := 0;
+  FValidatorNesting := 0;
+  FCurrNode := @FNodeStack[0];
   if FNamespaces then
   if FNamespaces then
   begin
   begin
     FNSHelper := TNSSupport.Create;
     FNSHelper := TNSSupport.Create;
@@ -1336,63 +1391,165 @@ begin
   end;
   end;
 end;
 end;
 
 
-procedure TXMLTextReader.ProcessXML(ASource: TXMLCharSource);
+procedure TLoader.ProcessXML(ADoc: TDOMDocument; AReader: TXMLTextReader);
 begin
 begin
-  doc := TXMLDocument.Create;
-  doc.documentURI := ASource.SystemID;  // TODO: to be changed to URI or BaseURI
-  FNameTable := doc.Names;
-  FState := rsProlog;
-  FNesting := 0;
-  FValidatorNesting := 0;
-  FCurrNode := @FNodeStack[0];
-  FFragmentMode := False;
-  NSPrepare;
-  Initialize(ASource);
-  if FSource.FXMLVersion <> xmlVersionUnknown then
-    TDOMTopNodeEx(TDOMNode(doc)).FXMLVersion := FSource.FXMLVersion;
-  TDOMTopNodeEx(TDOMNode(doc)).FXMLEncoding := FSource.FXMLEncoding;
-  doc.XMLStandalone := FStandalone;
-  FNext := xtText;
+  doc := ADoc;
+  reader := AReader;
+  reader.FDoc := ADoc;
+  doc.documentURI := reader.BaseURI;
+  reader.FState := rsProlog;
+  reader.FFragmentMode := False;
   ParseContent(doc);
   ParseContent(doc);
+  doc.XMLStandalone := reader.FStandalone;
 
 
-  if FValidate then
-    ValidateIdRefs;
+  if reader.FValidate then
+    reader.ValidateIdRefs;
 
 
-  doc.IDs := FIDMap;
-  FIDMap := nil;
+  doc.IDs := reader.FIDMap;
+  reader.FIDMap := nil;
 end;
 end;
 
 
-procedure TXMLTextReader.ProcessFragment(ASource: TXMLCharSource; AOwner: TDOMNode);
+procedure TLoader.ProcessFragment(AOwner: TDOMNode; AReader: TXMLTextReader);
 var
 var
   DoctypeNode: TDOMDocumentTypeEx;
   DoctypeNode: TDOMDocumentTypeEx;
 begin
 begin
   doc := AOwner.OwnerDocument;
   doc := AOwner.OwnerDocument;
-  FNameTable := doc.Names;
-  FState := rsRoot;
-  FNesting := 0;
-  FValidatorNesting := 0;
-  FCurrNode := @FNodeStack[0];
-  FFragmentMode := True;
-  FXML11 := doc.XMLVersion = '1.1';
-  NSPrepare;
-  Initialize(ASource);
-  { Get doctype from the owner's document, but only if it is not already assigned
-   (It is set directly when parsing children of an Entity, see LoadEntity procedure) }
-  if FDocType = nil then
+  reader := AReader;
+  reader.FDoc := doc;
+  reader.FState := rsRoot;
+  reader.FFragmentMode := True;
+  reader.FXML11 := doc.XMLVersion = '1.1';
+  DoctypeNode := TDOMDocumentTypeEx(doc.DocType);
+  if Assigned(DoctypeNode) then
+    reader.FDocType := DocTypeNode.FModel.Reference;
+  ParseContent(aOwner as TDOMNode_WithChildren);
+end;
+
+procedure TLoader.ProcessEntity(ADoc: TObject; AReader: TXMLTextReader; AEntity: TEntityDecl);
+var
+  DoctypeNode: TDOMDocumentType;
+  Ent: TDOMEntityEx;
+  src: TXMLCharSource;
+begin
+  DoctypeNode := TDOMDocument(ADoc).DocType;
+  if DoctypeNode = nil then
+    Exit;
+  Ent := TDOMEntityEx(DocTypeNode.Entities.GetNamedItem(AEntity.FName));
+  if Ent = nil then
+    Exit;
+  AReader.EntityToSource(AEntity, Src);
+  if Src = nil then
+    Exit;
+  reader := TXMLTextReader.Create(Src, AReader);
+  try
+    Ent.SetReadOnly(False);
+    ProcessFragment(Ent, reader);
+    AEntity.FResolved := True;
+  finally
+    reader.Free;
+    AEntity.FOnStack := False;
+    Ent.SetReadOnly(True);
+  end;
+end;
+
+procedure TLoader.ParseContent(cursor: TDOMNode_WithChildren);
+var
+  element: TDOMElement;
+begin
+  if reader.ReadState = rsInitial then
   begin
   begin
-    DoctypeNode := TDOMDocumentTypeEx(doc.DocType);
-    if Assigned(DoctypeNode) then
-      FDocType := DocTypeNode.FModel.Reference;
+    reader.Read;
+    if cursor is TDOMNode_TopLevel then
+    begin
+      if reader.FSource.FXMLVersion <> xmlVersionUnknown then
+        TDOMTopNodeEx(cursor).FXMLVersion := reader.FSource.FXMLVersion;
+      TDOMTopNodeEx(cursor).FXMLEncoding := reader.FSource.FXMLEncoding;
+    end;
   end;
   end;
-  if AOwner is TDOMEntity then
+
+  with reader do
+  repeat
+    if FValidate then
+      ValidateCurrentNode;
+
+    case FCurrNode^.FNodeType of
+      ntText:
+        cursor.InternalAppend(doc.CreateTextNodeBuf(FValue.Buffer, FValue.Length, False));
+
+      ntWhitespace, ntSignificantWhitespace:
+        if FPreserveWhitespace then
+          cursor.InternalAppend(doc.CreateTextNodeBuf(FValue.Buffer, FValue.Length, FCurrNode^.FNodeType = ntWhitespace));
+
+      ntCDATA:
+        cursor.InternalAppend(DoCDSect(FValue.Buffer, FValue.Length));
+
+      ntProcessingInstruction:
+        cursor.InternalAppend(CreatePINode);
+
+      ntComment:
+        if not FIgnoreComments then
+          cursor.InternalAppend(doc.CreateCommentBuf(FCurrNode^.FValueStart, FCurrNode^.FValueLength));
+
+      ntElement:
+        begin
+          element := DoStartElement;
+          cursor.InternalAppend(element);
+          cursor := element;
+        end;
+
+      ntEndElement:
+          cursor := TDOMNode_WithChildren(cursor.ParentNode);
+
+      ntDocumentType:
+        if not FCanonical then
+          cursor.InternalAppend(TDOMDocumentType.Create(doc, FDocType));
+
+      ntEntityReference:
+        cursor.InternalAppend(doc.CreateEntityReference(FCurrNode^.FQName^.Key));
+    end;
+  until not Read;
+end;
+
+function TLoader.DoStartElement: TDOMElement;
+var
+  Attr: TDOMAttr;
+  i: Integer;
+begin
+  with reader.FCurrNode^ do
   begin
   begin
-    TDOMTopNodeEx(AOwner).FXMLVersion := FSource.FXMLVersion;
-    TDOMTopNodeEx(AOwner).FXMLEncoding := FSource.FXMLEncoding;
+    Result := doc.CreateElementBuf(PWideChar(FQName^.Key), Length(FQName^.Key));
+    if Assigned(FNsUri) then
+      Result.SetNSI(FNsUri^.Key, FColonPos+1);
   end;
   end;
-  FNext := xtText;
-  ParseContent(aOwner as TDOMNode_WithChildren);
+
+  for i := 1 to reader.FAttrCount do
+  begin
+    Attr := LoadAttribute(doc, @reader.FNodeStack[reader.FNesting+i]);
+    Result.SetAttributeNode(Attr);
+    // Attach element to ID map entry if necessary
+    if Assigned(reader.FNodeStack[reader.FNesting+i].FIDEntry) then
+      reader.FNodeStack[reader.FNesting+i].FIDEntry^.Data := Result;
+  end;
+end;
+
+function TLoader.CreatePINode: TDOMNode;
+var
+  NameStr, ValueStr: DOMString;
+begin
+  SetString(NameStr, reader.FName.Buffer, reader.FName.Length);
+  SetString(ValueStr, reader.FValue.Buffer, reader.FValue.Length);
+  result := Doc.CreateProcessingInstruction(NameStr, ValueStr);
+end;
+
+function TLoader.DoCDSect(ch: PWideChar; Count: Integer): TDOMNode;
+var
+  s: XMLString;
+begin
+  SetString(s, ch, Count);
+  result := doc.CreateCDATASection(s);
 end;
 end;
 
 
+
 function TXMLTextReader.CheckName(aFlags: TCheckNameFlags): Boolean;
 function TXMLTextReader.CheckName(aFlags: TCheckNameFlags): Boolean;
 var
 var
   p: PWideChar;
   p: PWideChar;
@@ -1922,15 +2079,6 @@ begin
     FNameTable.FindOrAdd(FName.Buffer, FName.Length));
     FNameTable.FindOrAdd(FName.Buffer, FName.Length));
 end;
 end;
 
 
-function TXMLTextReader.CreatePINode: TDOMNode;
-var
-  NameStr, ValueStr: DOMString;
-begin
-  SetString(NameStr, FName.Buffer, FName.Length);
-  SetString(ValueStr, FValue.Buffer, FValue.Length);
-  result := Doc.CreateProcessingInstruction(NameStr, ValueStr);
-end;
-
 const
 const
   vers: array[Boolean] of TXMLVersion = (xmlVersion10, xmlVersion11);
   vers: array[Boolean] of TXMLVersion = (xmlVersion10, xmlVersion11);
 
 
@@ -2550,8 +2698,6 @@ begin
     if FSource.FBuf^ = '?' then
     if FSource.FBuf^ = '?' then
     begin
     begin
       ParsePI;
       ParsePI;
-      if Assigned(doc) then
-        doc.AppendChild(CreatePINode);
     end
     end
     else
     else
     begin
     begin
@@ -2617,48 +2763,22 @@ begin
     FatalError('Illegal character in DTD');
     FatalError('Illegal character in DTD');
 end;
 end;
 
 
-procedure TXMLTextReader.ProcessDTD(ASource: TXMLCharSource);
+procedure TLoader.ProcessDTD(ADoc: TDOMDocument; AReader: TXMLTextReader);
 begin
 begin
-  doc := TXMLDocument.Create;
-  FNameTable := doc.Names;
-  FDocType := TDTDModel.Create(FNameTable);
+  AReader.FDocType := TDTDModel.Create(AReader.FNameTable);
   // TODO: DTD labeled version 1.1 will be rejected - must set FXML11 flag
   // TODO: DTD labeled version 1.1 will be rejected - must set FXML11 flag
-  doc.AppendChild(TDOMDocumentType.Create(doc, FDocType));
-  NSPrepare;
-  Initialize(ASource);
-  ParseMarkupDecl;
+  doc.AppendChild(TDOMDocumentType.Create(doc, AReader.FDocType));
+  AReader.FSource.Initialize;
+  AReader.ParseMarkupDecl;
 end;
 end;
 
 
 
 
 procedure TXMLTextReader.LoadEntity(AEntity: TEntityDecl);
 procedure TXMLTextReader.LoadEntity(AEntity: TEntityDecl);
 var
 var
-  InnerReader: TXMLTextReader;
-  Src: TXMLCharSource;
-  Ent: TDOMEntityEx;
-  DoctypeNode: TDOMDocumentType;
+  ldr: TLoader;
 begin
 begin
-  if Assigned(doc) then
-    DoctypeNode := doc.DocType
-  else
-    Exit;
-  if DoctypeNode = nil then
-    Exit;
-  Ent := TDOMEntityEx(DocTypeNode.Entities.GetNamedItem(AEntity.FName));
-  if Ent = nil then
-    Exit;
-  InnerReader := TXMLTextReader.Create(FCtrl);
-  try
-    InnerReader.FDocType := FDocType.Reference;
-    EntityToSource(AEntity, Src);
-    Ent.SetReadOnly(False);
-    if Assigned(Src) then
-      InnerReader.ProcessFragment(Src, Ent);
-    AEntity.FResolved := True;
-  finally
-    InnerReader.Free;
-    AEntity.FOnStack := False;
-    Ent.SetReadOnly(True);
-  end;
+  if Assigned(FDoc) then
+    ldr.ProcessEntity(FDoc, Self, AEntity);
 end;
 end;
 
 
 
 
@@ -2750,8 +2870,7 @@ end;
 
 
 function TXMLTextReader.GetBaseUri: XMLString;
 function TXMLTextReader.GetBaseUri: XMLString;
 begin
 begin
-  { TODO: implement }
-  result := '';
+  result := FSource.SystemID;
 end;
 end;
 
 
 function TXMLTextReader.MoveToFirstAttribute: Boolean;
 function TXMLTextReader.MoveToFirstAttribute: Boolean;
@@ -3133,26 +3252,6 @@ begin
   FNext := xtText;
   FNext := xtText;
 end;
 end;
 
 
-function TXMLTextReader.DoStartElement: TDOMElement;
-var
-  Attr: TDOMAttr;
-  i: Integer;
-begin
-  with FCurrNode^.FQName^ do
-    Result := doc.CreateElementBuf(PWideChar(Key), Length(Key));
-  if Assigned(FCurrNode^.FNsUri) then
-    Result.SetNSI(FCurrNode^.FNsUri^.Key, FCurrNode^.FColonPos+1);
-
-  for i := 1 to FAttrCount do
-  begin
-    Attr := LoadAttribute(doc, @FNodeStack[FNesting+i]);
-    Result.SetAttributeNode(Attr);
-    // Attach element to ID map entry if necessary
-    if Assigned(FNodeStack[FNesting+i].FIDEntry) then
-      FNodeStack[FNesting+i].FIDEntry^.Data := Result;
-  end;
-end;
-
 // The code below does the bulk of the parsing, and must be as fast as possible.
 // The code below does the bulk of the parsing, and must be as fast as possible.
 // To minimize CPU cache effects, methods from different classes are kept together
 // To minimize CPU cache effects, methods from different classes are kept together
 
 
@@ -3206,53 +3305,6 @@ const
     ntText
     ntText
   );
   );
 
 
-procedure TXMLTextReader.ParseContent(cursor: TDOMNode_WithChildren);
-var
-  element: TDOMElement;
-begin
-  while Read do
-  begin
-    if FValidate then
-      ValidateCurrentNode;
-
-    case FCurrNode^.FNodeType of
-      ntText:
-        cursor.InternalAppend(doc.CreateTextNodeBuf(FValue.Buffer, FValue.Length, False));
-
-      ntWhitespace, ntSignificantWhitespace:
-        if FPreserveWhitespace then
-          cursor.InternalAppend(doc.CreateTextNodeBuf(FValue.Buffer, FValue.Length, FCurrNode^.FNodeType = ntWhitespace));
-
-      ntCDATA:
-        cursor.InternalAppend(DoCDSect(FValue.Buffer, FValue.Length));
-
-      ntProcessingInstruction:
-        cursor.InternalAppend(CreatePINode);
-
-      ntComment:
-        if not FIgnoreComments then
-          cursor.InternalAppend(doc.CreateCommentBuf(FCurrNode^.FValueStart, FCurrNode^.FValueLength));
-
-      ntElement:
-        begin
-          element := DoStartElement;
-          cursor.InternalAppend(element);
-          cursor := element;
-        end;
-
-      ntEndElement:
-          cursor := TDOMNode_WithChildren(cursor.ParentNode);
-
-      ntDocumentType:
-        if not FCanonical then
-          cursor.InternalAppend(TDOMDocumentType.Create(doc, FDocType));
-
-      ntEntityReference:
-        cursor.InternalAppend(doc.CreateEntityReference(FCurrNode^.FQName^.Key));
-    end;
-  end;
-end;
-
 function TXMLTextReader.ReadTopLevel: Boolean;
 function TXMLTextReader.ReadTopLevel: Boolean;
 var
 var
   nonWs: Boolean;
   nonWs: Boolean;
@@ -3366,7 +3418,12 @@ begin
     Result := False;
     Result := False;
     Exit;
     Exit;
   end;
   end;
-  FReadState := rsInteractive;
+  if FReadState = rsInitial then
+  begin
+    FReadState := rsInteractive;
+    FSource.Initialize;
+    FNext := xtText;
+  end;
   if FAttrReadState <> arsNone then
   if FAttrReadState <> arsNone then
     CleanAttrReadState;
     CleanAttrReadState;
   if FNext = xtPopEmptyElement then
   if FNext = xtPopEmptyElement then
@@ -3973,17 +4030,6 @@ begin
         DoErrorPos(esError, 'Notation ''%s'' is not declared', [Value], Loc);
         DoErrorPos(esError, 'Notation ''%s'' is not declared', [Value], Loc);
 end;
 end;
 
 
-
-function TXMLTextReader.DoCDSect(ch: PWideChar; Count: Integer): TDOMNode;
-var
-  s: XMLString;
-begin
-  Assert(not FCDSectionsAsText, 'Should not be called when CDSectionsAsText=True');
-
-  SetString(s, ch, Count);
-  result := doc.CreateCDATASection(s);
-end;
-
 procedure TXMLTextReader.DoNotationDecl(const aName, aPubID, aSysID: XMLString);
 procedure TXMLTextReader.DoNotationDecl(const aName, aPubID, aSysID: XMLString);
 var
 var
   Notation: TNotationDecl;
   Notation: TNotationDecl;
@@ -4190,14 +4236,14 @@ procedure ReadXMLFile(out ADoc: TXMLDocument; var f: Text);
 var
 var
   Reader: TXMLTextReader;
   Reader: TXMLTextReader;
   Src: TXMLCharSource;
   Src: TXMLCharSource;
+  ldr: TLoader;
 begin
 begin
-  ADoc := nil;
+  ADoc := TXMLDocument.Create;
   Src := TXMLFileInputSource.Create(f);
   Src := TXMLFileInputSource.Create(f);
-  Reader := TXMLTextReader.Create;
+  Reader := TXMLTextReader.Create(Src, ADoc.Names);
   try
   try
-    Reader.ProcessXML(Src);
+    ldr.ProcessXML(ADoc,Reader);
   finally
   finally
-    ADoc := TXMLDocument(Reader.Doc);
     Reader.Free;
     Reader.Free;
   end;
   end;
 end;
 end;
@@ -4206,15 +4252,15 @@ procedure ReadXMLFile(out ADoc: TXMLDocument; f: TStream; const ABaseURI: String
 var
 var
   Reader: TXMLTextReader;
   Reader: TXMLTextReader;
   Src: TXMLCharSource;
   Src: TXMLCharSource;
+  ldr: TLoader;
 begin
 begin
-  ADoc := nil;
-  Reader := TXMLTextReader.Create;
+  ADoc := TXMLDocument.Create;
+  Src := TXMLStreamInputSource.Create(f, False);
+  Src.SystemID := ABaseURI;
+  Reader := TXMLTextReader.Create(Src, ADoc.Names);
   try
   try
-    Src := TXMLStreamInputSource.Create(f, False);
-    Src.SystemID := ABaseURI;
-    Reader.ProcessXML(Src);
+    ldr.ProcessXML(ADoc, Reader);
   finally
   finally
-    ADoc := TXMLDocument(Reader.doc);
     Reader.Free;
     Reader.Free;
   end;
   end;
 end;
 end;
@@ -4241,11 +4287,12 @@ procedure ReadXMLFragment(AParentNode: TDOMNode; var f: Text);
 var
 var
   Reader: TXMLTextReader;
   Reader: TXMLTextReader;
   Src: TXMLCharSource;
   Src: TXMLCharSource;
+  ldr: TLoader;
 begin
 begin
-  Reader := TXMLTextReader.Create;
+  Src := TXMLFileInputSource.Create(f);
+  Reader := TXMLTextReader.Create(Src, AParentNode.OwnerDocument.Names);
   try
   try
-    Src := TXMLFileInputSource.Create(f);
-    Reader.ProcessFragment(Src, AParentNode);
+    ldr.ProcessFragment(AParentNode, Reader);
   finally
   finally
     Reader.Free;
     Reader.Free;
   end;
   end;
@@ -4255,12 +4302,13 @@ procedure ReadXMLFragment(AParentNode: TDOMNode; f: TStream; const ABaseURI: Str
 var
 var
   Reader: TXMLTextReader;
   Reader: TXMLTextReader;
   Src: TXMLCharSource;
   Src: TXMLCharSource;
+  ldr: TLoader;
 begin
 begin
-  Reader := TXMLTextReader.Create;
+  Src := TXMLStreamInputSource.Create(f, False);
+  Src.SystemID := ABaseURI;
+  Reader := TXMLTextReader.Create(Src, AParentNode.OwnerDocument.Names);
   try
   try
-    Src := TXMLStreamInputSource.Create(f, False);
-    Src.SystemID := ABaseURI;
-    Reader.ProcessFragment(Src, AParentNode);
+    ldr.ProcessFragment(AParentNode, Reader);
   finally
   finally
     Reader.Free;
     Reader.Free;
   end;
   end;
@@ -4288,14 +4336,14 @@ procedure ReadDTDFile(out ADoc: TXMLDocument; var f: Text);
 var
 var
   Reader: TXMLTextReader;
   Reader: TXMLTextReader;
   Src: TXMLCharSource;
   Src: TXMLCharSource;
+  ldr: TLoader;
 begin
 begin
-  ADoc := nil;
-  Reader := TXMLTextReader.Create;
+  ADoc := TXMLDocument.Create;
+  Src := TXMLFileInputSource.Create(f);
+  Reader := TXMLTextReader.Create(Src, ADoc.Names);
   try
   try
-    Src := TXMLFileInputSource.Create(f);
-    Reader.ProcessDTD(Src);
+    ldr.ProcessDTD(ADoc,Reader);
   finally
   finally
-    ADoc := TXMLDocument(Reader.doc);
     Reader.Free;
     Reader.Free;
   end;
   end;
 end;
 end;
@@ -4304,15 +4352,15 @@ procedure ReadDTDFile(out ADoc: TXMLDocument; f: TStream; const ABaseURI: String
 var
 var
   Reader: TXMLTextReader;
   Reader: TXMLTextReader;
   Src: TXMLCharSource;
   Src: TXMLCharSource;
+  ldr: TLoader;
 begin
 begin
-  ADoc := nil;
-  Reader := TXMLTextReader.Create;
+  ADoc := TXMLDocument.Create;
+  Src := TXMLStreamInputSource.Create(f, False);
+  Src.SystemID := ABaseURI;
+  Reader := TXMLTextReader.Create(Src, ADoc.Names);
   try
   try
-    Src := TXMLStreamInputSource.Create(f, False);
-    Src.SystemID := ABaseURI;
-    Reader.ProcessDTD(Src);
+    ldr.ProcessDTD(ADoc,Reader);
   finally
   finally
-    ADoc := TXMLDocument(Reader.doc);
     Reader.Free;
     Reader.Free;
   end;
   end;
 end;
 end;