diff options
| author | sergei <sergei@3ad0048d-3df7-0310-abae-a5850022a9f2> | 2011-02-14 17:53:09 +0000 |
|---|---|---|
| committer | sergei <sergei@3ad0048d-3df7-0310-abae-a5850022a9f2> | 2011-02-14 17:53:09 +0000 |
| commit | ce59631c7413c0c556e74ee34dd18368ce0bea7a (patch) | |
| tree | 1bc0b9e9c6163eaa2e3fb0a09c7ef90f75eed327 /packages/fcl-xml | |
| parent | 2f1d9f18e03e57ff506b589f46a956f26a07fe39 (diff) | |
| download | fpc-ce59631c7413c0c556e74ee34dd18368ce0bea7a.tar.gz | |
+ fcl-xml, initial implementation of 'shared' DTD: it now consists of the model (classes TDTDModel, TEntityDecl, TNotationDecl), which is referenced by one or more TDOMDocumentType nodes.
git-svn-id: http://svn.freepascal.org/svn/fpc/trunk@16914 3ad0048d-3df7-0310-abae-a5850022a9f2
Diffstat (limited to 'packages/fcl-xml')
| -rw-r--r-- | packages/fcl-xml/src/dom.pp | 173 | ||||
| -rw-r--r-- | packages/fcl-xml/src/dtdmodel.pp | 92 | ||||
| -rw-r--r-- | packages/fcl-xml/src/xmlread.pp | 200 |
3 files changed, 329 insertions, 136 deletions
diff --git a/packages/fcl-xml/src/dom.pp b/packages/fcl-xml/src/dom.pp index 1ec1b09e33..30370455e7 100644 --- a/packages/fcl-xml/src/dom.pp +++ b/packages/fcl-xml/src/dom.pp @@ -245,7 +245,7 @@ type function RemoveChild(OldChild: TDOMNode): TDOMNode; function AppendChild(NewChild: TDOMNode): TDOMNode; function HasChildNodes: Boolean; virtual; - function CloneNode(deep: Boolean): TDOMNode; overload; + function CloneNode(deep: Boolean): TDOMNode; overload; virtual; // DOM level 2 function IsSupported(const Feature, Version: DOMString): Boolean; @@ -498,9 +498,10 @@ type property documentURI: DOMString read FURI write FURI; property XMLVersion: DOMString read GetXMLVersion write SetXMLVersion; // Extensions to DOM interface: - constructor Create; + constructor Create; virtual; destructor Destroy; override; function AddID(Attr: TDOMAttr): Boolean; deprecated; + function CloneNode(deep: Boolean): TDOMNode; overload; override; property Names: THashTable read FNames; property IDs: THashTable read FIDList write FIDList; end; @@ -512,7 +513,7 @@ type // These fields are extensions to the DOM interface: StylesheetType, StylesheetHRef: DOMString; - constructor Create; + constructor Create; override; function CreateCDATASection(const data: DOMString): TDOMCDATASection; override; function CreateProcessingInstruction(const target, data: DOMString): TDOMProcessingInstruction; override; @@ -664,24 +665,25 @@ type TDOMDocumentType = class(TDOMNode) protected - FName: DOMString; - FPublicID: DOMString; - FSystemID: DOMString; - FInternalSubset: DOMString; + FModel: TDTDModel; FEntities, FNotations: TDOMNamedNodeMap; function GetEntities: TDOMNamedNodeMap; function GetNotations: TDOMNamedNodeMap; function GetNodeType: Integer; override; function GetNodeName: DOMString; override; + function GetPublicID: DOMString; + function GetSystemID: DOMString; + function GetInternalSubset: DOMString; public + constructor Create(aOwner: TDOMDocument; aModel: TDTDModel); destructor Destroy; override; - property Name: DOMString read FName; + property Name: DOMString read GetNodeName; property Entities: TDOMNamedNodeMap read GetEntities; property Notations: TDOMNamedNodeMap read GetNotations; // Introduced in DOM Level 2: - property PublicID: DOMString read FPublicID; - property SystemID: DOMString read FSystemID; - property InternalSubset: DOMString read FInternalSubset; + property PublicID: DOMString read GetPublicID; + property SystemID: DOMString read GetSystemID; + property InternalSubset: DOMString read GetInternalSubset; end; @@ -691,14 +693,15 @@ type TDOMNotation = class(TDOMNode) protected - FName: DOMString; - FPublicID, FSystemID: DOMString; + FDecl: TNotationDecl; function GetNodeType: Integer; override; function GetNodeName: DOMString; override; + function GetPublicID: DOMString; + function GetSystemID: DOMString; public function CloneNode(deep: Boolean; ACloneOwner: TDOMDocument): TDOMNode; overload; override; - property PublicID: DOMString read FPublicID; - property SystemID: DOMString read FSystemID; + property PublicID: DOMString read GetPublicID; + property SystemID: DOMString read GetSystemID; end; @@ -708,15 +711,17 @@ type TDOMEntity = class(TDOMNode_TopLevel) protected - FName: DOMString; - FPublicID, FSystemID, FNotationName: DOMString; + FDecl: TEntityDecl; function GetNodeType: Integer; override; function GetNodeName: DOMString; override; + function GetPublicID: DOMString; + function GetSystemID: DOMString; + function GetNotationName: DOMString; public function CloneNode(deep: Boolean; aCloneOwner: TDOMDocument): TDOMNode; override; - property PublicID: DOMString read FPublicID; - property SystemID: DOMString read FSystemID; - property NotationName: DOMString read FNotationName; + property PublicID: DOMString read GetPublicID; + property SystemID: DOMString read GetSystemID; + property NotationName: DOMString read GetNotationName; property XMLVersion: DOMString read GetXMLVersion; end; @@ -2088,16 +2093,18 @@ function TDOMImplementation.CreateDocumentType(const QualifiedName, PublicID, SystemID: DOMString): TDOMDocumentType; var res: Integer; + model: TDTDModel; begin res := CheckQName(QualifiedName, -1, False); if res < 0 then raise EDOMError.Create(-res, 'Implementation.CreateDocumentType'); - Result := TDOMDocumentType.Create(nil); - Result.FName := QualifiedName; - + model := TDTDModel.Create(nil); // !!nowhere to get nametable from at this time + model.FName := QualifiedName; // DOM does not restrict PublicID without SystemID (unlike XML spec) - Result.FPublicID := PublicID; - Result.FSystemID := SystemID; + model.FPublicID := PublicID; + model.FSystemID := SystemID; + Result := TDOMDocumentType.Create(nil, model); + model.Release; // now Result remains a sole owner of model end; function TDOMImplementation.CreateDocument(const NamespaceURI, @@ -2158,6 +2165,33 @@ begin // (because children reference the nametable) end; +function TDOMDocument.CloneNode(deep: Boolean): TDOMNode; +type + TDOMDocumentClass = class of TDOMDocument; +var + Clone: TDOMDocument; + node, doctypenode: TDOMNode; +begin + Clone := TDOMDocumentClass(ClassType).Create; + Clone.FInputEncoding := FInputEncoding; + Clone.FXMLEncoding := FXMLEncoding; + Clone.FXMLVersion := FXMLVersion; + Clone.FURI := FURI; + if deep then + begin + node := FirstChild; + doctypenode := DocType; + while Assigned(node) do + begin + {TODO: now just skip doctype, a better solution is to be found.} + if node <> doctypenode then + Clone.InternalAppend(node.CloneNode(True, Clone)); + node := node.NextSibling; + end; + end; + Result := Clone; +end; + function TDOMDocument.Alloc(AClass: TDOMNodeClass): TDOMNode; var pp: TNodePool; @@ -3176,11 +3210,60 @@ end; function TDOMDocumentType.GetNodeName: DOMString; begin - Result := FName; + Result := FModel.FName; +end; + +function TDOMDocumentType.GetPublicID: DOMString; +begin + Result := FModel.FPublicID; +end; + +function TDOMDocumentType.GetSystemID: DOMString; +begin + Result := FModel.FSystemID; +end; + +function TDOMDocumentType.GetInternalSubset: DOMString; +begin + Result := FModel.FInternalSubset; +end; + +function ConvertEntity(Entry: PHashItem; arg: Pointer): Boolean; +var + this: TDOMDocumentType absolute arg; + node: TDOMEntity; +begin + node := TDOMEntity.Create(this.ownerDocument); + node.FDecl := TEntityDecl(Entry^.Data); + node.SetReadOnly(True); + this.Entities.SetNamedItem(node); + Result := True; +end; + +function ConvertNotation(Entry: PHashItem; arg: Pointer): Boolean; +var + this: TDOMDocumentType absolute arg; + node: TDOMNotation; +begin + node := TDOMNotation.Create(this.ownerDocument); + node.FDecl := TNotationDecl(Entry^.Data); + node.SetReadOnly(True); + this.Notations.SetNamedItem(node); + Result := True; +end; + +constructor TDOMDocumentType.Create(aOwner: TDOMDocument; aModel: TDTDModel); +begin + inherited Create(aOwner); + FModel := aModel.Reference; + FModel.Entities.ForEach(@ConvertEntity, Self); + FModel.Notations.ForEach(@ConvertNotation, Self); + SetReadOnly(True); end; destructor TDOMDocumentType.Destroy; begin + FModel.Release; FEntities.Free; FNotations.Free; inherited Destroy; @@ -3211,16 +3294,24 @@ end; function TDOMNotation.GetNodeName: DOMString; begin - Result := FName; + Result := FDecl.FName; +end; + +function TDOMNotation.GetPublicID: DOMString; +begin + Result := FDecl.FPublicID; +end; + +function TDOMNotation.GetSystemID: DOMString; +begin + Result := FDecl.FSystemID; end; function TDOMNotation.CloneNode(deep: Boolean; ACloneOwner: TDOMDocument): TDOMNode; begin Result := ACloneOwner.Alloc(TDOMNotation); TDOMNotation(Result).Create(ACloneOwner); - TDOMNotation(Result).FName := FName; - TDOMNotation(Result).FPublicID := PublicID; - TDOMNotation(Result).FSystemID := SystemID; + TDOMNotation(Result).FDecl := FDecl; // notation cannot have children, ignore Deep end; @@ -3236,17 +3327,29 @@ end; function TDOMEntity.GetNodeName: DOMString; begin - Result := FName; + Result := FDecl.FName; +end; + +function TDOMEntity.GetPublicID: DOMString; +begin + Result := FDecl.FPublicID; +end; + +function TDOMEntity.GetSystemID: DOMString; +begin + Result := FDecl.FSystemID; +end; + +function TDOMEntity.GetNotationName: DOMString; +begin + Result := FDecl.FNotationName; end; function TDOMEntity.CloneNode(deep: Boolean; aCloneOwner: TDOMDocument): TDOMNode; begin Result := aCloneOwner.Alloc(TDOMEntity); TDOMEntity(Result).Create(aCloneOwner); - TDOMEntity(Result).FName := FName; - TDOMEntity(Result).FSystemID := FSystemID; - TDOMEntity(Result).FPublicID := FPublicID; - TDOMEntity(Result).FNotationName := FNotationName; + TDOMEntity(Result).FDecl := FDecl; if deep then CloneChildren(Result, aCloneOwner); Result.SetReadOnly(True); diff --git a/packages/fcl-xml/src/dtdmodel.pp b/packages/fcl-xml/src/dtdmodel.pp index 4e8896d09c..07b66682c7 100644 --- a/packages/fcl-xml/src/dtdmodel.pp +++ b/packages/fcl-xml/src/dtdmodel.pp @@ -113,9 +113,101 @@ type property NeedsDefaultPass: Boolean read FNeedsDefaultPass; end; + TEntityDecl = class(TDTDObject) + public + FName: WideString; // TODO: change to PHashItem + FInputEncoding: WideString; + FXMLEncoding: WideString; + FPublicID: WideString; + FSystemID: WideString; + FNotationName: WideString; + FURI: WideString; + FReplacementText: WideString; + FXMLVersion: TXMLVersion; + FPrefetched: Boolean; + FResolved: Boolean; + FOnStack: Boolean; + FBetweenDecls: Boolean; + FIsPE: Boolean; + FStartLocation: TLocation; + FCharCount: Cardinal; + end; + + TNotationDecl = class(TDTDObject) + public + FName: WideString; + FPublicID: WideString; + FSystemID: WideString; + end; + + TDTDModel = class + private + FRefCount: Integer; + FNameTable: THashTable; + FEntities: THashTable; + FNotations: THashTable; + function GetEntities: THashTable; + function GetNotations: THashTable; + public + FName: WideString; + FSystemID: WideString; + FPublicID: WideString; + FInternalSubset: WideString; + constructor Create(aNameTable: THashTable); + destructor Destroy; override; + function Reference: TDTDModel; + procedure Release; + property Entities: THashTable read GetEntities; + property Notations: THashTable read GetNotations; + end; implementation +{ TDTDModel } + +function TDTDModel.GetEntities: THashTable; +begin + if FEntities = nil then + FEntities := THashTable.Create(256, True); + Result := FEntities; +end; + +function TDTDModel.GetNotations: THashTable; +begin + if FNotations = nil then + FNotations := THashTable.Create(256, True); + Result := FNotations; +end; + +constructor TDTDModel.Create(aNameTable: THashTable); +begin + FNameTable := aNameTable; + FRefCount := 1; +end; + +destructor TDTDModel.Destroy; +begin + FEntities.Free; + FNotations.Free; + inherited Destroy; +end; + +function TDTDModel.Reference: TDTDModel; +begin + Inc(FRefCount); + Result := Self; +end; + +procedure TDTDModel.Release; +begin + if Assigned(Self) then + begin + Dec(FRefCount); + if FRefCount = 0 then + self.Destroy; + end; +end; + { TContentParticle } function TContentParticle.Add: TContentParticle; diff --git a/packages/fcl-xml/src/xmlread.pp b/packages/fcl-xml/src/xmlread.pp index 8665a3ddc1..56d75c372d 100644 --- a/packages/fcl-xml/src/xmlread.pp +++ b/packages/fcl-xml/src/xmlread.pp @@ -151,7 +151,6 @@ const '#', '@', '$', '_', '%']; type - TDOMNotationEx = class(TDOMNotation); TDOMDocumentTypeEx = class(TDOMDocumentType); TDOMTopNodeEx = class(TDOMNode_TopLevel); @@ -159,18 +158,7 @@ type TLocation = xmlutils.TLocation; - TDOMEntityEx = class(TDOMEntity) - protected - FExternallyDeclared: Boolean; - FPrefetched: Boolean; - FResolved: Boolean; - FOnStack: Boolean; - FBetweenDecls: Boolean; - FIsPE: Boolean; - FReplacementText: DOMString; - FStartLocation: TLocation; - FCharCount: Cardinal; - end; + TDOMEntityEx = class(TDOMEntity); TXMLReader = class; @@ -180,7 +168,7 @@ type FBufEnd: PWideChar; FReader: TXMLReader; FParent: TXMLCharSource; - FEntity: TObject; // weak reference + FEntity: TEntityDecl; FLineNo: Integer; LFPos: PWideChar; FXML11Rules: Boolean; @@ -294,20 +282,18 @@ type FTokenStart: TLocation; FStandalone: Boolean; // property of Doc ? FNamePages: PByteArray; - FDocType: TDOMDocumentTypeEx; // a shortcut + FDocType: TDTDModel; FPEMap: THashTable; - FGEMap: THashTable; FForwardRefs: TFPList; FCurrContentType: TElementContentType; FSaViolation: Boolean; FDTDStartPos: PWideChar; FIntSubset: TWideCharBuf; FAttrTag: Cardinal; - FOwnsDoctype: Boolean; FDTDProcessed: Boolean; FToken: TXMLToken; FNext: TXMLToken; - FCurrEntity: TDOMEntityEx; + FCurrEntity: TEntityDecl; FIDMap: THashTable; FNSHelper: TNSSupport; @@ -332,8 +318,8 @@ type procedure SkipQuote(out Delim: WideChar; required: Boolean = True); procedure Initialize(ASource: TXMLCharSource); procedure NSPrepare; - procedure EntityToSource(AEntity: TDOMEntityEx; out Src: TXMLCharSource); - function ContextPush(AEntity: TDOMEntityEx): Boolean; + procedure EntityToSource(AEntity: TEntityDecl; out Src: TXMLCharSource); + function ContextPush(AEntity: TEntityDecl): Boolean; function ContextPop(Forced: Boolean = False): Boolean; procedure XML11_BuildTables; function ParseQuantity: TCPQuant; @@ -403,9 +389,10 @@ type procedure ParseContent; // [43] function Read: Boolean; function ResolvePredefined: Boolean; - function EntityCheck(NoExternals: Boolean = False): TDOMEntityEx; - procedure AppendReference(cur: TDOMNode; AEntity: TDOMEntityEx); - function PrefetchEntity(AEntity: TDOMEntityEx): Boolean; + function EntityCheck(NoExternals: Boolean = False): TEntityDecl; + procedure LoadEntity(AEntity: TEntityDecl); + procedure AppendReference(cur: TDOMNode; AEntity: TEntityDecl); + function PrefetchEntity(AEntity: TEntityDecl): Boolean; procedure StartPE; function ParseRef(var ToFill: TWideCharBuf): Boolean; // [67] function ParseExternalID(out SysID, PubID: WideString; // [75] @@ -1086,7 +1073,7 @@ begin begin sysid := FSource.FSystemID; if (sysid = '') and Assigned(FSource.FEntity) then - sysid := TDOMEntityEx(FSource.FEntity).FURI; + sysid := FSource.FEntity.FURI; E := EXMLReadError.CreateFmt('In ''%s'' (line %d pos %d): %s', [sysid, ErrPos.Line, ErrPos.LinePos, descr]); end else @@ -1285,12 +1272,10 @@ begin while ContextPop(True) do; // clean input stack FSource.Free; FPEMap.Free; - FGEMap.Free; ClearForwardRefs; FNsAttHash.Free; FNSHelper.Free; - if FOwnsDoctype then - FDocType.Free; + FDocType.Release; FIDMap.Free; FForwardRefs.Free; FAttrChunks.Free; @@ -1347,6 +1332,8 @@ begin end; procedure TXMLReader.ProcessFragment(ASource: TXMLCharSource; AOwner: TDOMNode); +var + DoctypeNode: TDOMDocumentTypeEx; begin doc := AOwner.OwnerDocument; FNameTable := doc.Names; @@ -1359,7 +1346,11 @@ begin Initialize(ASource); // See comment in EntityCheck() if FDocType = nil then - FDocType := TDOMDocumentTypeEx(doc.DocType); + begin + DoctypeNode := TDOMDocumentTypeEx(doc.DocType); + if Assigned(DoctypeNode) then + FDocType := DocTypeNode.FModel.Reference; + end; if AOwner is TDOMEntity then begin TDOMTopNodeEx(AOwner).FXMLVersion := FSource.FXMLVersion; @@ -1564,7 +1555,7 @@ function TXMLReader.ExpectAttValue(AttrData: PNodeData; NonCDATA: Boolean): Bool var wc: WideChar; Delim: WideChar; - ent: TDOMEntityEx; + ent: TEntityDecl; start: TObject; curr: PNodeData; StartPos: Integer; @@ -1641,14 +1632,14 @@ end; const PrefixChar: array[Boolean] of string = ('', '%'); -procedure TXMLReader.EntityToSource(AEntity: TDOMEntityEx; out Src: TXMLCharSource); +procedure TXMLReader.EntityToSource(AEntity: TEntityDecl; out Src: TXMLCharSource); begin if AEntity.FOnStack then FatalError('Entity ''%s%s'' recursively references itself', [PrefixChar[AEntity.FIsPE], AEntity.FName]); - if (AEntity.SystemID <> '') and not AEntity.FPrefetched then + if (AEntity.FSystemID <> '') and not AEntity.FPrefetched then begin - if not ResolveEntity(AEntity.SystemID, AEntity.PublicID, AEntity.FURI, Src) then + if not ResolveEntity(AEntity.FSystemID, AEntity.FPublicID, AEntity.FURI, Src) then begin // TODO: a detailed message like SysErrorMessage(GetLastError) would be great here ValidationError('Unable to resolve external entity ''%s''', [AEntity.FName]); @@ -1662,7 +1653,7 @@ begin Src.FLineNo := AEntity.FStartLocation.Line; Src.LFPos := Src.FBuf - AEntity.FStartLocation.LinePos; // needed in case of prefetched external PE - if AEntity.SystemID <> '' then + if AEntity.FSystemID <> '' then Src.SystemID := AEntity.FURI; end; @@ -1670,7 +1661,7 @@ begin Src.FEntity := AEntity; end; -function TXMLReader.ContextPush(AEntity: TDOMEntityEx): Boolean; +function TXMLReader.ContextPush(AEntity: TEntityDecl): Boolean; var Src: TXMLCharSource; begin @@ -1692,10 +1683,10 @@ begin Error := False; if Assigned(FSource.FEntity) then begin - TDOMEntityEx(FSource.FEntity).FOnStack := False; - TDOMEntityEx(FSource.FEntity).FCharCount := FSource.FCharCount; + FSource.FEntity.FOnStack := False; + FSource.FEntity.FCharCount := FSource.FCharCount; // [28a] PE that was started between MarkupDecls may not end inside MarkupDecl - Error := TDOMEntityEx(FSource.FEntity).FBetweenDecls and FInsideDecl; + Error := FSource.FEntity.FBetweenDecls and FInsideDecl; end; FSource.Free; FSource := Src; @@ -1705,35 +1696,33 @@ begin end; end; -function TXMLReader.EntityCheck(NoExternals: Boolean): TDOMEntityEx; +function TXMLReader.EntityCheck(NoExternals: Boolean): TEntityDecl; var RefName: WideString; cnt: Integer; - InnerReader: TXMLReader; - Src: TXMLCharSource; begin Result := nil; SetString(RefName, FName.Buffer, FName.Length); cnt := FName.Length+2; if Assigned(FDocType) then - Result := FDocType.Entities.GetNamedItem(RefName) as TDOMEntityEx; + Result := FDocType.Entities.Get(FName.Buffer, FName.Length) as TEntityDecl; if Result = nil then begin - if FStandalone or (FDocType = nil) or not (FHavePERefs or (FDocType.SystemID <> '')) then + if FStandalone or (FDocType = nil) or not (FHavePERefs or (FDocType.FSystemID <> '')) then FatalError('Reference to undefined entity ''%s''', [RefName], cnt) else ValidationError('Undefined entity ''%s'' referenced', [RefName], cnt); Exit; end; - if FStandalone and Result.FExternallyDeclared then + if FStandalone and Result.ExternallyDeclared then FatalError('Standalone constraint violation', cnt); - if Result.NotationName <> '' then + if Result.FNotationName <> '' then FatalError('Reference to unparsed entity ''%s''', [RefName], cnt); - if NoExternals and (Result.SystemID <> '') then + if NoExternals and (Result.FSystemID <> '') then FatalError('External entity reference is not allowed in attribute value', cnt); if not Result.FResolved then @@ -1742,33 +1731,21 @@ begin // However, care must be taken to properly pass the DTD to InnerReader. // We now have doc.DocumentType=nil while DTD is being parsed, // which can break parsing 2+ level entities in default attribute values. - InnerReader := TXMLReader.Create(FCtrl); - try - InnerReader.FAttrTag := FAttrTag; - InnerReader.FDocType := FDocType; - EntityToSource(Result, Src); - Result.SetReadOnly(False); - if Assigned(Src) then - InnerReader.ProcessFragment(Src, Result); - Result.FResolved := True; - finally - FAttrTag := InnerReader.FAttrTag; - InnerReader.Free; - Result.FOnStack := False; - Result.SetReadOnly(True); - end; + + LoadEntity(Result); end; // at this point we know the charcount of the entity being included - CheckMaxChars(Result.FCharCount - cnt); + if Result.FCharCount >= cnt then + CheckMaxChars(Result.FCharCount - cnt); end; procedure TXMLReader.StartPE; var - PEnt: TDOMEntityEx; + PEnt: TEntityDecl; begin PEnt := nil; if Assigned(FPEMap) then - PEnt := FPEMap.Get(FName.Buffer, FName.Length) as TDOMEntityEx; + PEnt := FPEMap.Get(FName.Buffer, FName.Length) as TEntityDecl; if PEnt = nil then begin ValidationErrorWithName('Undefined parameter entity ''%s'' referenced', FName.Length+2); @@ -1778,7 +1755,7 @@ begin end; { cache an external PE so it's only fetched once } - if (PEnt.SystemID <> '') and (not PEnt.FPrefetched) and (not PrefetchEntity(PEnt)) then + if (PEnt.FSystemID <> '') and (not PEnt.FPrefetched) and (not PrefetchEntity(PEnt)) then begin FDTDProcessed := FStandalone; Exit; @@ -1790,7 +1767,7 @@ begin FHavePERefs := True; end; -function TXMLReader.PrefetchEntity(AEntity: TDOMEntityEx): Boolean; +function TXMLReader.PrefetchEntity(AEntity: TEntityDecl): Boolean; begin Result := ContextPush(AEntity); if Result then @@ -2069,9 +2046,8 @@ begin ExpectString('DOCTYPE'); SkipS(True); - FDocType := TDOMDocumentTypeEx(TDOMDocumentType.Create(doc)); + FDocType := TDTDModel.Create(FNameTable); FDTDProcessed := True; // assume success - FOwnsDoctype := True; FState := rsDTD; FDocType.FName := ExpectName; @@ -2097,9 +2073,9 @@ begin end; ExpectChar('>'); - if (FDocType.SystemID <> '') then + if (FDocType.FSystemID <> '') then begin - if ResolveEntity(FDocType.SystemID, FDocType.PublicID, FSource.SystemID, Src) then + if ResolveEntity(FDocType.FSystemID, FDocType.FPublicID, FSource.SystemID, Src) then begin Initialize(Src); try @@ -2116,7 +2092,6 @@ begin end; end; ValidateDTD; - FDocType.SetReadOnly(True); FState := rsAfterDTD; end; @@ -2460,7 +2435,7 @@ end; procedure TXMLReader.ParseEntityDecl; // [70] var IsPE, Exists: Boolean; - Entity: TDOMEntityEx; + Entity: TEntityDecl; Map: THashTable; Item: PHashItem; begin @@ -2475,18 +2450,13 @@ begin Map := FPEMap; end else - begin - if FGEMap = nil then - FGEMap := THashTable.Create(64, False); - Map := FGEMap; - end; + Map := FDocType.Entities; - Entity := TDOMEntityEx.Create(Doc); - Entity.SetReadOnly(True); + Entity := TEntityDecl.Create; try - Entity.FExternallyDeclared := FSource.DTDSubsetType <> dsInternal; + Entity.ExternallyDeclared := FSource.DTDSubsetType <> dsInternal; Entity.FIsPE := IsPE; - Entity.FName := ExpectName; + CheckName; CheckNCName; Item := Map.FindOrAdd(FName.Buffer, FName.Length, Exists); ExpectWhitespace; @@ -2531,8 +2501,7 @@ begin if FDTDProcessed and not Exists then begin Item^.Data := Entity; - if not IsPE then - FDocType.Entities.SetNamedItem(Entity); + Entity.FName := Item^.Key; end else Entity.Free; @@ -2654,22 +2623,53 @@ procedure TXMLReader.ProcessDTD(ASource: TXMLCharSource); begin doc := TXMLDocument.Create; FNameTable := doc.Names; - FDocType := TDOMDocumentTypeEx.Create(doc); + FDocType := TDTDModel.Create(FNameTable); // TODO: DTD labeled version 1.1 will be rejected - must set FXML11 flag - doc.AppendChild(FDocType); + doc.AppendChild(TDOMDocumentType.Create(doc, FDocType)); NSPrepare; Initialize(ASource); ParseMarkupDecl; end; -procedure TXMLReader.AppendReference(cur: TDOMNode; AEntity: TDOMEntityEx); + +procedure TXMLReader.LoadEntity(AEntity: TEntityDecl); +var + InnerReader: TXMLReader; + Src: TXMLCharSource; + Ent: TDOMEntityEx; + DoctypeNode: TDOMDocumentType; +begin + DoctypeNode := doc.DocType; + if DoctypeNode = nil then + Exit; + Ent := TDOMEntityEx(DocTypeNode.Entities.GetNamedItem(AEntity.FName)); + if Ent = nil then + Exit; + InnerReader := TXMLReader.Create(FCtrl); + try + InnerReader.FAttrTag := FAttrTag; + InnerReader.FDocType := FDocType.Reference; + EntityToSource(AEntity, Src); + Ent.SetReadOnly(False); + if Assigned(Src) then + InnerReader.ProcessFragment(Src, Ent); + AEntity.FResolved := True; + finally + FAttrTag := InnerReader.FAttrTag; + InnerReader.Free; + AEntity.FOnStack := False; + Ent.SetReadOnly(True); + end; +end; + +procedure TXMLReader.AppendReference(cur: TDOMNode; AEntity: TEntityDecl); var s: WideString; begin if AEntity = nil then SetString(s, FName.Buffer, FName.Length) else - s := AEntity.nodeName; + s := AEntity.FName; cur.AppendChild(doc.CreateEntityReference(s)); end; @@ -2768,10 +2768,9 @@ begin xtEndElement: DoEndElement; xtDoctype: - if not FCanonical then begin - doc.AppendChild(FDocType); - FOwnsDoctype := False; + if not FCanonical then + doc.AppendChild(TDOMDocumentType.Create(doc, FDocType)); end; end; end; @@ -3378,7 +3377,7 @@ end; procedure TXMLReader.ValidateAttrValue(AttrDef: TAttributeDef; attrData: PNodeData); var L, StartPos, EndPos: Integer; - Entity: TDOMEntity; + Entity: TEntityDecl; begin L := Length(attrData^.FValueStr); case AttrDef.DataType of @@ -3407,11 +3406,8 @@ begin EndPos := StartPos; while (EndPos <= L) and (attrData^.FValueStr[EndPos] <> #32) do Inc(EndPos); - if Assigned(FGEMap) then - Entity := TDOMEntity(FGEMap.Get(@attrData^.FValueStr[StartPos], EndPos-StartPos)) - else - Entity := nil; - if (Entity = nil) or (Entity.NotationName = '') then + Entity := TEntityDecl(FDocType.Entities.Get(@attrData^.FValueStr[StartPos], EndPos-StartPos)); + if (Entity = nil) or (Entity.FNotationName = '') then ValidationError('Attribute ''%s'' type mismatch', [attrData^.FQName^.Key], -1); StartPos := EndPos + 1; end; @@ -3423,7 +3419,7 @@ procedure TXMLReader.ValidateRoot; begin if Assigned(FDocType) then begin - if not BufEquals(FName, FDocType.Name) then + if not BufEquals(FName, FDocType.FName) then ValidationError('Root element name does not match DTD', [], FName.Length); end else @@ -3437,7 +3433,7 @@ begin if FValidate then for I := 0 to FForwardRefs.Count-1 do with PForwardRef(FForwardRefs[I])^ do - if FDocType.Notations.GetNamedItem(Value) = nil then + if FDocType.Notations.Get(PWideChar(Value), Length(Value)) = nil then DoErrorPos(esError, Format('Notation ''%s'' is not declared', [Value]), Loc); ClearForwardRefs; end; @@ -3503,15 +3499,17 @@ end; procedure TXMLReader.DoNotationDecl(const aName, aPubID, aSysID: WideString); var - Notation: TDOMNotationEx; + Notation: TNotationDecl; + Entry: PHashItem; begin - if FDocType.Notations.GetNamedItem(aName) = nil then + Entry := FDocType.Notations.FindOrAdd(PWideChar(aName), Length(aName)); + if Entry^.Data = nil then begin - Notation := TDOMNotationEx(TDOMNotation.Create(doc)); + Notation := TNotationDecl.Create; Notation.FName := aName; Notation.FPublicID := aPubID; Notation.FSystemID := aSysID; - FDocType.Notations.SetNamedItem(Notation); + Entry^.Data := Notation; end else ValidationError('Duplicate notation declaration: ''%s''', [aName]); |
