summaryrefslogtreecommitdiff
path: root/packages/fcl-xml
diff options
context:
space:
mode:
authorsergei <sergei@3ad0048d-3df7-0310-abae-a5850022a9f2>2011-02-14 17:53:09 +0000
committersergei <sergei@3ad0048d-3df7-0310-abae-a5850022a9f2>2011-02-14 17:53:09 +0000
commitce59631c7413c0c556e74ee34dd18368ce0bea7a (patch)
tree1bc0b9e9c6163eaa2e3fb0a09c7ef90f75eed327 /packages/fcl-xml
parent2f1d9f18e03e57ff506b589f46a956f26a07fe39 (diff)
downloadfpc-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.pp173
-rw-r--r--packages/fcl-xml/src/dtdmodel.pp92
-rw-r--r--packages/fcl-xml/src/xmlread.pp200
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]);