* xmlread.pp, modified attribute parsing code to use DOM-independent data structures (third part)

* Namespace handling rewritten to fit into XMLReader's own data structures.
  * Remaining TDOMElementDef's replaced by TElementDecl.
  * Removed DoAttrText(), it has become obsolete.
  * Create objects that are needed for namespace processing only if actually doing namespace processing, reduces memory requirements.
  * Improved TAttributeDef construction.

git-svn-id: trunk@16230 -
This commit is contained in:
sergei 2010-10-27 11:57:03 +00:00
parent 19baf7d3e0
commit 068d2fba37
4 changed files with 177 additions and 149 deletions

View File

@ -2140,7 +2140,6 @@ begin
FNamespaces[1] := stduri_xml; FNamespaces[1] := stduri_xml;
FNamespaces[2] := stduri_xmlns; FNamespaces[2] := stduri_xmlns;
FEmptyNode := TDOMElement.Create(Self); FEmptyNode := TDOMElement.Create(Self);
FNodeLists := THashTable.Create(32, True);
end; end;
destructor TDOMDocument.Destroy; destructor TDOMDocument.Destroy;
@ -2400,6 +2399,8 @@ var
Key, P: DOMPChar; Key, P: DOMPChar;
Item: PHashItem; Item: PHashItem;
begin begin
if FNodeLists = nil then
FNodeLists := THashTable.Create(32, True);
L := (sizeof(Pointer) div sizeof(WideChar)) + Length(aLocalName); L := (sizeof(Pointer) div sizeof(WideChar)) + Length(aLocalName);
if UseNS then if UseNS then
Inc(L, Length(nsURI)+1); Inc(L, Length(nsURI)+1);
@ -2827,7 +2828,6 @@ begin
result := GetAncestorElement(Self).InternalLookupPrefix(nsURI, Original); result := GetAncestorElement(Self).InternalLookupPrefix(nsURI, Original);
end; end;
// Copypasted from the same procedure in xmlread
function LoadAttribute(doc: TDOMDocument; src: PNodeData): TDOMAttr; function LoadAttribute(doc: TDOMDocument; src: PNodeData): TDOMAttr;
var var
curr: PNodeData; curr: PNodeData;
@ -2837,6 +2837,10 @@ begin
result.FNSI.QName := src^.FQName; result.FNSI.QName := src^.FQName;
if not src^.FIsDefault then if not src^.FIsDefault then
Include(result.FFlags, nfSpecified); Include(result.FFlags, nfSpecified);
if Assigned(src^.FTypeInfo) then
result.FDataType := TAttributeDef(src^.FTypeInfo).DataType;
if Assigned(src^.FNsUri) then
result.SetNSI(src^.FNsUri^.Key, src^.FColonPos+1);
if Assigned(src^.FNext) then if Assigned(src^.FNext) then
begin begin
curr := src^.FNext; curr := src^.FNext;

View File

@ -72,9 +72,10 @@ type
FDataType: TAttrDataType; FDataType: TAttrDataType;
FDefault: TAttrDefault; FDefault: TAttrDefault;
FTag: Cardinal; FTag: Cardinal;
FIsNamespaceDecl: Boolean;
FEnumeration: array of WideString; FEnumeration: array of WideString;
public public
constructor Create; constructor Create(aName: PHashItem; aColonPos: Integer);
destructor Destroy; override; destructor Destroy; override;
function AddEnumToken(Buf: PWideChar; Len: Integer): Boolean; function AddEnumToken(Buf: PWideChar; Len: Integer): Boolean;
function HasEnumToken(const aValue: WideString): Boolean; function HasEnumToken(const aValue: WideString): Boolean;
@ -82,6 +83,7 @@ type
property Default: TAttrDefault read FDefault write FDefault; property Default: TAttrDefault read FDefault write FDefault;
property DataType: TAttrDataType read FDataType write FDataType; property DataType: TAttrDataType read FDataType write FDataType;
property Tag: Cardinal read FTag write FTag; property Tag: Cardinal read FTag write FTag;
property IsNamespaceDecl: Boolean read FIsNamespaceDecl;
end; end;
TElementContentType = ( TElementContentType = (
@ -288,11 +290,16 @@ end;
{ TAttributeDef } { TAttributeDef }
constructor TAttributeDef.Create; constructor TAttributeDef.Create(aName: PHashItem; aColonPos: Integer);
begin begin
New(FData); New(FData);
FillChar(FData^, sizeof(TNodeData), 0); FillChar(FData^, sizeof(TNodeData), 0);
FData^.FIsDefault := True; FData^.FIsDefault := True;
FData^.FQName := aName;
FData^.FColonPos := aColonPos;
FData^.FTypeInfo := Self;
FIsNamespaceDecl := ((Length(aName^.Key) = 5) or (aColonPos = 6)) and
(Pos(WideString('xmlns'), aName^.Key) = 1);
end; end;
destructor TAttributeDef.Destroy; destructor TAttributeDef.Destroy;

View File

@ -154,7 +154,6 @@ type
TDOMNotationEx = class(TDOMNotation); TDOMNotationEx = class(TDOMNotation);
TDOMDocumentTypeEx = class(TDOMDocumentType); TDOMDocumentTypeEx = class(TDOMDocumentType);
TDOMTopNodeEx = class(TDOMNode_TopLevel); TDOMTopNodeEx = class(TDOMNode_TopLevel);
TDOMElementDef = dtdmodel.TElementDecl;
TDTDSubsetType = (dsNone, dsInternal, dsExternal); TDTDSubsetType = (dsNone, dsInternal, dsExternal);
@ -261,10 +260,10 @@ type
end; end;
TElementValidator = object TElementValidator = object
FElementDef: TDOMElementDef; FElementDef: TElementDecl;
FCurCP: TContentParticle; FCurCP: TContentParticle;
FFailed: Boolean; FFailed: Boolean;
function IsElementAllowed(Def: TDOMElementDef): Boolean; function IsElementAllowed(Def: TElementDecl): Boolean;
function Incomplete: Boolean; function Incomplete: Boolean;
end; end;
@ -280,11 +279,6 @@ type
xtCDSect, xtComment, xtPI, xtDoctype, xtEntity, xtEntityEnd, xtPopElement, xtCDSect, xtComment, xtPI, xtDoctype, xtEntity, xtEntityEnd, xtPopElement,
xtPopEmptyElement, xtPushElement); xtPopEmptyElement, xtPushElement);
TPrefixedAttr = record
Attr: TDOMAttr;
PrefixLen: Integer; // to avoid recalculation
end;
TLiteralType = (ltPlain, ltPubid, ltEntity); TLiteralType = (ltPlain, ltPubid, ltEntity);
TXMLReader = class TXMLReader = class
@ -318,10 +312,11 @@ type
FCurrEntity: TDOMEntityEx; FCurrEntity: TDOMEntityEx;
FNSHelper: TNSSupport; FNSHelper: TNSSupport;
FWorkAtts: array of TPrefixedAttr;
FNsAttHash: TDblHashArray; FNsAttHash: TDblHashArray;
FStdPrefix_xml: PHashItem; FStdPrefix_xml: PHashItem;
FStdPrefix_xmlns: PHashItem; FStdPrefix_xmlns: PHashItem;
FStdUri_xml: PHashItem;
FStdUri_xmlns: PHashItem;
FColonPos: Integer; FColonPos: Integer;
FValidate: Boolean; // parsing options, copy of FCtrl.Options FValidate: Boolean; // parsing options, copy of FCtrl.Options
@ -337,6 +332,7 @@ type
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: TDOMEntityEx; out Src: TXMLCharSource); procedure EntityToSource(AEntity: TDOMEntityEx; out Src: TXMLCharSource);
function ContextPush(AEntity: TDOMEntityEx): Boolean; function ContextPush(AEntity: TDOMEntityEx): Boolean;
function ContextPop(Forced: Boolean = False): Boolean; function ContextPop(Forced: Boolean = False): Boolean;
@ -350,7 +346,7 @@ type
procedure ValidateIdRefs; procedure ValidateIdRefs;
procedure StandaloneError(LineOffs: Integer = 0); procedure StandaloneError(LineOffs: Integer = 0);
procedure CallErrorHandler(E: EXMLReadError); procedure CallErrorHandler(E: EXMLReadError);
function FindOrCreateElDef: TDOMElementDef; function FindOrCreateElDef: TElementDecl;
function SkipUntilSeq(const Delim: TSetOfChar; c1: WideChar; c2: WideChar = #0): Boolean; function SkipUntilSeq(const Delim: TSetOfChar; c1: WideChar; c2: WideChar = #0): Boolean;
procedure CheckMaxChars; procedure CheckMaxChars;
function AllocNodeData(AIndex: Integer): PNodeData; function AllocNodeData(AIndex: Integer): PNodeData;
@ -362,6 +358,7 @@ type
FNesting: Integer; FNesting: Integer;
FCurrNode: PNodeData; FCurrNode: PNodeData;
FAttrCount: Integer; FAttrCount: Integer;
FPrefixedAttrs: Integer;
FNodeStack: TNodeDataDynArray; FNodeStack: TNodeDataDynArray;
FCursorStack: TDOMNodeDynArray; FCursorStack: TDOMNodeDynArray;
FValidators: TValidatorDynArray; FValidators: TValidatorDynArray;
@ -399,7 +396,7 @@ type
procedure ParseStartTag; // [39] procedure ParseStartTag; // [39]
procedure ParseEndTag; // [42] procedure ParseEndTag; // [42]
procedure DoEndElement; procedure DoEndElement;
procedure ParseAttribute(Elem: TDOMElement; ElDef: TDOMElementDef); procedure ParseAttribute(ElDef: TElementDecl);
procedure ParseContent; // [43] procedure ParseContent; // [43]
function Read: Boolean; function Read: Boolean;
function ResolvePredefined: Boolean; function ResolvePredefined: Boolean;
@ -418,18 +415,17 @@ type
procedure ParseElementDecl; procedure ParseElementDecl;
procedure ParseNotationDecl; procedure ParseNotationDecl;
function ResolveEntity(const ASystemID, APublicID, ABaseURI: WideString; out Source: TXMLCharSource): Boolean; function ResolveEntity(const ASystemID, APublicID, ABaseURI: WideString; out Source: TXMLCharSource): Boolean;
procedure ProcessDefaultAttributes(Element: TDOMElement; ElDef: TElementDecl); procedure ProcessDefaultAttributes(ElDef: TElementDecl);
procedure ProcessNamespaceAtts(Element: TDOMElement); procedure ProcessNamespaceAtts;
procedure AddBinding(Attr: TDOMAttr; PrefixPtr: PWideChar; PrefixLen: Integer); procedure AddBinding(attrData: PNodeData);
procedure PushVC(aElDef: TDOMElementDef); procedure PushVC(aElDef: TElementDecl);
procedure PopVC; procedure PopVC;
procedure UpdateConstraints; procedure UpdateConstraints;
procedure ValidateDTD; procedure ValidateDTD;
procedure ValidateRoot; procedure ValidateRoot;
procedure ValidationError(const Msg: string; const args: array of const; LineOffs: Integer = -1); procedure ValidationError(const Msg: string; const args: array of const; LineOffs: Integer = -1);
procedure ValidationErrorWithName(const Msg: string; LineOffs: Integer = -1); procedure ValidationErrorWithName(const Msg: string; LineOffs: Integer = -1);
procedure DoAttrText(node: TDOMAttr; ch: PWideChar; Count: Integer);
procedure DTDReloadHook; procedure DTDReloadHook;
procedure ConvertSource(SrcIn: TXMLInputSource; out SrcOut: TXMLCharSource); procedure ConvertSource(SrcIn: TXMLInputSource; out SrcOut: TXMLCharSource);
// Some SAX-alike stuff (at a very early stage) // Some SAX-alike stuff (at a very early stage)
@ -1248,14 +1244,8 @@ begin
BufAllocate(FValue, 512); BufAllocate(FValue, 512);
FIDRefs := TFPList.Create; FIDRefs := TFPList.Create;
FNotationRefs := TFPList.Create; FNotationRefs := TFPList.Create;
FNSHelper := TNSSupport.Create;
FAttrChunks := TFPList.Create; FAttrChunks := TFPList.Create;
FNsAttHash := TDblHashArray.Create;
SetLength(FWorkAtts, 16);
FStdPrefix_xml := FNSHelper.GetPrefix(@PrefixDefault, 3);
FStdPrefix_xmlns := FNSHelper.GetPrefix(@PrefixDefault, 5);
// Set char rules to XML 1.0 // Set char rules to XML 1.0
FNamePages := @NamePages; FNamePages := @NamePages;
SetLength(FNodeStack, 16); SetLength(FNodeStack, 16);
@ -1316,6 +1306,22 @@ begin
FSource.FXml11Rules := True; FSource.FXml11Rules := True;
end; end;
{ Must be executed after doc has been set.
After introducing own NameTable, merge this into constructor }
procedure TXMLReader.NSPrepare;
begin
if FNamespaces then
begin
FNSHelper := TNSSupport.Create;
FNsAttHash := TDblHashArray.Create;
FStdPrefix_xml := FNSHelper.GetPrefix(@PrefixDefault, 3);
FStdPrefix_xmlns := FNSHelper.GetPrefix(@PrefixDefault, 5);
FStdUri_xmlns := doc.Names.FindOrAdd(PWideChar(stduri_xmlns), Length(stduri_xmlns));
FStdUri_xml := doc.Names.FindOrAdd(PWideChar(stduri_xml), Length(stduri_xml));
end;
end;
procedure TXMLReader.ProcessXML(ASource: TXMLCharSource); procedure TXMLReader.ProcessXML(ASource: TXMLCharSource);
begin begin
doc := TXMLDocument.Create; doc := TXMLDocument.Create;
@ -1324,6 +1330,7 @@ begin
FNesting := 0; FNesting := 0;
FCurrNode := @FNodeStack[0]; FCurrNode := @FNodeStack[0];
FCursorStack[0] := doc; FCursorStack[0] := doc;
NSPrepare;
Initialize(ASource); Initialize(ASource);
ParseContent; ParseContent;
@ -1342,6 +1349,7 @@ begin
FCurrNode := @FNodeStack[0]; FCurrNode := @FNodeStack[0];
FCursorStack[0] := AOwner as TDOMNode_WithChildren; FCursorStack[0] := AOwner as TDOMNode_WithChildren;
FXML11 := doc.InheritsFrom(TXMLDocument) and (TXMLDocument(doc).XMLVersion = '1.1'); FXML11 := doc.InheritsFrom(TXMLDocument) and (TXMLDocument(doc).XMLVersion = '1.1');
NSPrepare;
Initialize(ASource); Initialize(ASource);
FDocType := TDOMDocumentTypeEx(doc.DocType); FDocType := TDOMDocumentTypeEx(doc.DocType);
ParseContent; ParseContent;
@ -1530,11 +1538,6 @@ begin
ExpectChar(';'); ExpectChar(';');
end; end;
procedure TXMLReader.DoAttrText(node: TDOMAttr; ch: PWideChar; Count: Integer);
begin
node.InternalAppend(Doc.CreateTextNodeBuf(ch, Count, False));
end;
const const
AttrDelims: TSetOfChar = [#0, '<', '&', '''', '"', #9, #10, #13]; AttrDelims: TSetOfChar = [#0, '<', '&', '''', '"', #9, #10, #13];
GT_Delim: TSetOfChar = [#0, '>']; GT_Delim: TSetOfChar = [#0, '>'];
@ -1745,7 +1748,6 @@ end;
procedure TXMLReader.StartPE; procedure TXMLReader.StartPE;
var var
PEName: WideString;
PEnt: TDOMEntityEx; PEnt: TDOMEntityEx;
begin begin
PEnt := nil; PEnt := nil;
@ -1753,8 +1755,7 @@ begin
PEnt := FPEMap.Get(FName.Buffer, FName.Length) as TDOMEntityEx; PEnt := FPEMap.Get(FName.Buffer, FName.Length) as TDOMEntityEx;
if PEnt = nil then if PEnt = nil then
begin begin
SetString(PEName, FName.Buffer, FName.Length); ValidationErrorWithName('Undefined parameter entity ''%s'' referenced', FName.Length+2);
ValidationError('Undefined parameter entity ''%s'' referenced', [PEName], FName.Length+2);
// cease processing declarations, unless document is standalone. // cease processing declarations, unless document is standalone.
FDTDProcessed := FStandalone; FDTDProcessed := FStandalone;
Exit; Exit;
@ -2154,16 +2155,16 @@ begin
FSource.NextChar; FSource.NextChar;
end; end;
function TXMLReader.FindOrCreateElDef: TDOMElementDef; function TXMLReader.FindOrCreateElDef: TElementDecl;
var var
p: PHashItem; p: PHashItem;
begin begin
CheckName; CheckName;
p := doc.Names.FindOrAdd(FName.Buffer, FName.Length); p := doc.Names.FindOrAdd(FName.Buffer, FName.Length);
Result := TDOMElementDef(p^.Data); Result := TElementDecl(p^.Data);
if Result = nil then if Result = nil then
begin begin
Result := TDOMElementDef.Create; Result := TElementDecl.Create;
p^.Data := Result; p^.Data := Result;
end; end;
end; end;
@ -2213,7 +2214,7 @@ end;
procedure TXMLReader.ParseElementDecl; // [45] procedure TXMLReader.ParseElementDecl; // [45]
var var
ElDef: TDOMElementDef; ElDef: TElementDecl;
CurrentEntity: TObject; CurrentEntity: TObject;
I: Integer; I: Integer;
CP: TContentParticle; CP: TContentParticle;
@ -2323,7 +2324,7 @@ const
procedure TXMLReader.ParseAttlistDecl; // [52] procedure TXMLReader.ParseAttlistDecl; // [52]
var var
ElDef: TDOMElementDef; ElDef: TElementDecl;
AttDef: TAttributeDef; AttDef: TAttributeDef;
dt: TAttrDataType; dt: TAttrDataType;
Found, DiscardIt: Boolean; Found, DiscardIt: Boolean;
@ -2338,9 +2339,8 @@ begin
CheckName; CheckName;
ExpectWhitespace; ExpectWhitespace;
attrName := doc.Names.FindOrAdd(FName.Buffer, FName.Length); attrName := doc.Names.FindOrAdd(FName.Buffer, FName.Length);
AttDef := TAttributeDef.Create; AttDef := TAttributeDef.Create(attrName, FColonPos);
try try
AttDef.Data^.FQName := attrName;
AttDef.ExternallyDeclared := FSource.DTDSubsetType <> dsInternal; AttDef.ExternallyDeclared := FSource.DTDSubsetType <> dsInternal;
// In case of duplicate declaration of the same attribute, we must discard it, // In case of duplicate declaration of the same attribute, we must discard it,
// not modifying ElDef, and suppressing certain validation errors. // not modifying ElDef, and suppressing certain validation errors.
@ -2654,6 +2654,7 @@ begin
FDocType := TDOMDocumentTypeEx.Create(doc); FDocType := TDOMDocumentTypeEx.Create(doc);
// 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(FDocType); doc.AppendChild(FDocType);
NSPrepare;
Initialize(ASource); Initialize(ASource);
ParseMarkupDecl; ParseMarkupDecl;
end; end;
@ -2937,9 +2938,12 @@ end;
procedure TXMLReader.ParseStartTag; // [39] [40] [44] procedure TXMLReader.ParseStartTag; // [39] [40] [44]
var var
NewElem: TDOMElement; NewElem: TDOMElement;
ElDef: TDOMElementDef; Attr: TDOMAttr;
ElDef: TElementDecl;
IsEmpty: Boolean; IsEmpty: Boolean;
ElName: PHashItem; ElName: PHashItem;
b: TBinding;
i: Integer;
begin begin
if FState > rsRoot then if FState > rsRoot then
FatalError('Only one top-level element allowed', FName.Length) FatalError('Only one top-level element allowed', FName.Length)
@ -2960,7 +2964,7 @@ begin
ElName := NewElem.NSI.QName; ElName := NewElem.NSI.QName;
// Find declaration for this element // Find declaration for this element
ElDef := TDOMElementDef(ElName^.Data); ElDef := TElementDecl(ElName^.Data);
if (ElDef = nil) or (ElDef.ContentType = ctUndeclared) then if (ElDef = nil) or (ElDef.ContentType = ctUndeclared) then
ValidationError('Using undeclared element ''%s''',[ElName^.Key], FName.Length); ValidationError('Using undeclared element ''%s''',[ElName^.Key], FName.Length);
@ -2970,16 +2974,18 @@ begin
IsEmpty := False; IsEmpty := False;
FAttrCount := 0; FAttrCount := 0;
FPrefixedAttrs := 0;
PushVC(ElDef); // this increases FNesting PushVC(ElDef); // this increases FNesting
FCursorStack[FNesting] := NewElem; FCursorStack[FNesting] := NewElem;
FCurrNode^.FQName := ElName; FCurrNode^.FQName := ElName;
FCurrNode^.FNodeType := ntElement; FCurrNode^.FNodeType := ntElement;
FCurrNode^.FColonPos := FColonPos;
if FNamespaces then if FNamespaces then
begin begin
FNSHelper.StartElement; FNSHelper.StartElement;
if FColonPos > 0 then if FColonPos > 0 then
FCurrNode^.FPrefix := FNSHelper.GetPrefix(FName.Buffer, FColonPos-1); FCurrNode^.FPrefix := FNSHelper.GetPrefix(FName.Buffer, FColonPos);
end; end;
while (FSource.FBuf^ <> '>') and (FSource.FBuf^ <> '/') do while (FSource.FBuf^ <> '>') and (FSource.FBuf^ <> '/') do
@ -2987,10 +2993,8 @@ begin
SkipS(True); SkipS(True);
if (FSource.FBuf^ = '>') or (FSource.FBuf^ = '/') then if (FSource.FBuf^ = '>') or (FSource.FBuf^ = '/') then
Break; Break;
ParseAttribute(NewElem, ElDef); ParseAttribute(ElDef);
end; end;
// ParseAttribute might have reallocated FNodeStack, so restore FCurrNode once again
FCurrNode := @FNodeStack[FNesting];
if FSource.FBuf^ = '/' then if FSource.FBuf^ = '/' then
begin begin
@ -3000,10 +3004,41 @@ begin
ExpectChar('>'); ExpectChar('>');
if Assigned(ElDef) and ElDef.NeedsDefaultPass then if Assigned(ElDef) and ElDef.NeedsDefaultPass then
ProcessDefaultAttributes(NewElem, ElDef); ProcessDefaultAttributes(ElDef);
// Adding attributes might have reallocated FNodeStack, so restore FCurrNode once again
FCurrNode := @FNodeStack[FNesting];
if FNamespaces then if FNamespaces then
ProcessNamespaceAtts(NewElem); begin
{ Assign namespace URIs to prefixed attrs }
ProcessNamespaceAtts;
{ Expand the element name }
if Assigned(FCurrNode^.FPrefix) then
begin
b := TBinding(FCurrNode^.FPrefix^.Data);
if not (Assigned(b) and (b.uri <> '')) then
FatalError('Unbound prefix "%s"', [FCurrNode^.FPrefix^.Key]);
FCurrNode^.FNsUri := doc.Names.FindOrAdd(PWideChar(b.uri), Length(b.uri));
NewElem.SetNSI(b.uri, FCurrNode^.FColonPos+1);
end
else
begin
b := FNSHelper.DefaultNSBinding;
if Assigned(b) then
begin
FCurrNode^.FNsUri := doc.Names.FindOrAdd(PWideChar(b.uri), Length(b.uri));
NewElem.SetNSI(b.uri, FCurrNode^.FColonPos+1);
end;
end;
end;
for i := 1 to FAttrCount do
begin
Attr := LoadAttribute(doc, @FNodeStack[FNesting+i]);
NewElem.SetAttributeNode(Attr);
ValidateAttrValue(Attr, FNodeStack[FNesting+i].FValueStr);
end;
if not IsEmpty then if not IsEmpty then
begin begin
@ -3050,9 +3085,8 @@ begin
FNext := xtPopElement; FNext := xtPopElement;
end; end;
procedure TXMLReader.ParseAttribute(Elem: TDOMElement; ElDef: TDOMElementDef); procedure TXMLReader.ParseAttribute(ElDef: TElementDecl);
var var
attr: TDOMAttr;
attrName: PHashItem; attrName: PHashItem;
attrData: PNodeData; attrData: PNodeData;
AttDef: TAttributeDef; AttDef: TAttributeDef;
@ -3066,13 +3100,14 @@ begin
ValidationError('Value of attribute ''%s'' does not match its #FIXED default',[attrData^.FQName^.Key], -1); ValidationError('Value of attribute ''%s'' does not match its #FIXED default',[attrData^.FQName^.Key], -1);
if not ValidateAttrSyntax(AttDef, attrData^.FValueStr) then if not ValidateAttrSyntax(AttDef, attrData^.FValueStr) then
ValidationError('Attribute ''%s'' type mismatch', [attrData^.FQName^.Key], -1); ValidationError('Attribute ''%s'' type mismatch', [attrData^.FQName^.Key], -1);
ValidateAttrValue(Attr, attrData^.FValueStr); // ValidateAttrValue(Attr, attrData^.FValueStr);
end; end;
begin begin
CheckName; CheckName;
attrName := doc.Names.FindOrAdd(FName.Buffer, FName.Length); attrName := doc.Names.FindOrAdd(FName.Buffer, FName.Length);
attrData := AllocAttributeData(attrName); attrData := AllocAttributeData(attrName);
attrData^.FColonPos := FColonPos;
if Assigned(ElDef) then if Assigned(ElDef) then
begin begin
@ -3086,25 +3121,42 @@ begin
else else
AttDef := nil; AttDef := nil;
attrData^.FTypeInfo := AttDef;
// check for duplicates // check for duplicates
for i := 1 to FAttrCount-1 do for i := 1 to FAttrCount-1 do
if FNodeStack[FNesting+i].FQName = attrName then if FNodeStack[FNesting+i].FQName = attrName then
FatalError('Duplicate attribute', FName.Length); FatalError('Duplicate attribute', FName.Length);
if FNamespaces then
begin
if ((FName.Length = 5) or (FColonPos = 5)) and
(FName.Buffer[0] = 'x') and (FName.Buffer[1] = 'm') and
(FName.Buffer[2] = 'l') and (FName.Buffer[3] = 'n') and
(FName.Buffer[4] = 's') then
begin
if FColonPos > 0 then
attrData^.FPrefix := FStdPrefix_xmlns;
attrData^.FNsUri := FStdUri_xmlns;
end
else if FColonPos > 0 then
begin
attrData^.FPrefix := FNSHelper.GetPrefix(FName.Buffer, FColonPos);
Inc(FPrefixedAttrs);
end;
end;
ExpectEq; ExpectEq;
normalized := ExpectAttValue(attrData, Assigned(AttDef) and (AttDef.DataType <> dtCDATA)); normalized := ExpectAttValue(attrData, Assigned(AttDef) and (AttDef.DataType <> dtCDATA));
attr := LoadAttribute(doc, attrData);
elem.Attributes.SetNamedItem(attr);
if Assigned(AttDef) and ((AttDef.DataType <> dtCdata) or (AttDef.Default = adFixed)) then if Assigned(AttDef) and ((AttDef.DataType <> dtCdata) or (AttDef.Default = adFixed)) then
begin begin
Attr.DataType := AttDef.DataType;
if normalized and FStandalone and AttDef.ExternallyDeclared then if normalized and FStandalone and AttDef.ExternallyDeclared then
StandaloneError(-1); StandaloneError(-1);
CheckValue; CheckValue;
end; end;
if Assigned(attrData^.FNsUri) then
AddBinding(attrData);
end; end;
procedure TXMLReader.AddForwardRef(aList: TFPList; Buf: PWideChar; Length: Integer); procedure TXMLReader.AddForwardRef(aList: TFPList; Buf: PWideChar; Length: Integer);
@ -3137,11 +3189,10 @@ begin
ClearRefs(FIDRefs); ClearRefs(FIDRefs);
end; end;
procedure TXMLReader.ProcessDefaultAttributes(Element: TDOMElement; ElDef: TElementDecl); procedure TXMLReader.ProcessDefaultAttributes(ElDef: TElementDecl);
var var
I: Integer; I: Integer;
AttDef: TAttributeDef; AttDef: TAttributeDef;
Attr: TDOMAttr;
attrData: PNodeData; attrData: PNodeData;
begin begin
for I := 0 to ElDef.AttrDefCount-1 do for I := 0 to ElDef.AttrDefCount-1 do
@ -3157,125 +3208,87 @@ begin
attrData := AllocAttributeData(nil); attrData := AllocAttributeData(nil);
attrData^ := AttDef.Data^; attrData^ := AttDef.Data^;
Attr := LoadAttribute(doc, AttDef.Data); if FNamespaces then
Element.SetAttributeNode(Attr); begin
if AttDef.IsNamespaceDecl then
ValidateAttrValue(Attr, Attr.Value); begin
if attrData^.FColonPos > 0 then
attrData^.FPrefix := FStdPrefix_xmlns;
attrData^.FNsUri := FStdUri_xmlns;
AddBinding(attrData);
end
else if attrData^.FColonPos > 0 then
begin
attrData^.FPrefix := FNSHelper.GetPrefix(PWideChar(attrData^.FQName^.Key), attrData^.FColonPos);
Inc(FPrefixedAttrs);
end;
end;
end; end;
adRequired: adRequired:
ValidationError('Required attribute ''%s'' of element ''%s'' is missing',[AttDef.Data^.FQName^.Key, Element.TagName], 0) ValidationError('Required attribute ''%s'' of element ''%s'' is missing',
[AttDef.Data^.FQName^.Key, FNodeStack[FNesting].FQName^.Key], 0)
end; end;
end; end;
end; end;
end; end;
procedure TXMLReader.AddBinding(Attr: TDOMAttr; PrefixPtr: PWideChar; PrefixLen: Integer); procedure TXMLReader.AddBinding(attrData: PNodeData);
var var
nsUri: DOMString; nsUri, Pfx: PHashItem;
Pfx: PHashItem;
begin begin
nsUri := Attr.NodeValue; nsUri := doc.Names.FindOrAdd(PWideChar(attrData^.FValueStr), Length(attrData^.FValueStr));
Pfx := FNSHelper.GetPrefix(PrefixPtr, PrefixLen); if attrData^.FColonPos > 0 then
Pfx := FNSHelper.GetPrefix(@attrData^.FQName^.key[7], Length(attrData^.FQName^.key)-6)
else
Pfx := FNSHelper.GetPrefix(nil, 0); { will return the default prefix }
{ 'xml' is allowed to be bound to the correct namespace } { 'xml' is allowed to be bound to the correct namespace }
if ((nsUri = stduri_xml) <> (Pfx = FStdPrefix_xml)) or if ((nsUri = FStduri_xml) <> (Pfx = FStdPrefix_xml)) or
(Pfx = FStdPrefix_xmlns) or (Pfx = FStdPrefix_xmlns) or
(nsUri = stduri_xmlns) then (nsUri = FStduri_xmlns) then
begin begin
if (Pfx = FStdPrefix_xml) or (Pfx = FStdPrefix_xmlns) then if (Pfx = FStdPrefix_xml) or (Pfx = FStdPrefix_xmlns) then
FatalError('Illegal usage of reserved prefix ''%s''', [Pfx^.Key]) FatalError('Illegal usage of reserved prefix ''%s''', [Pfx^.Key])
else else
FatalError('Illegal usage of reserved namespace URI ''%s''', [nsUri]); FatalError('Illegal usage of reserved namespace URI ''%s''', [attrData^.FValueStr]);
end; end;
if (nsUri = '') and not (FXML11 or (Pfx^.Key = '')) then if (attrData^.FValueStr = '') and not (FXML11 or (Pfx^.Key = '')) then
FatalError('Illegal undefining of namespace'); { position - ? } FatalError('Illegal undefining of namespace'); { position - ? }
FNSHelper.BindPrefix(nsURI, Pfx); FNSHelper.BindPrefix(attrData^.FValueStr, Pfx);
end; end;
procedure TXMLReader.ProcessNamespaceAtts(Element: TDOMElement); procedure TXMLReader.ProcessNamespaceAtts;
var var
I, J: Integer; I, J: Integer;
Map: TDOMNamedNodeMap;
Pfx, AttrName: PHashItem; Pfx, AttrName: PHashItem;
Attr: TDOMAttr; attrData: PNodeData;
PrefixCount: Integer;
b: TBinding; b: TBinding;
begin begin
PrefixCount := 0; if FPrefixedAttrs = 0 then
if Element.HasAttributes then Exit;
FNsAttHash.Init(FPrefixedAttrs);
for I := 1 to FAttrCount do
begin begin
Map := Element.Attributes; attrData := @FNodeStack[FNesting+i];
if Map.Length > LongWord(Length(FWorkAtts)) then if (attrData^.FColonPos < 1) or Assigned(attrData^.FNsUri) then
SetLength(FWorkAtts, Map.Length+10); Continue;
{ Pass 1, identify prefixed attrs and assign prefixes }
for I := 0 to Map.Length-1 do Pfx := attrData^.FPrefix;
begin b := TBinding(Pfx^.Data);
Attr := TDOMAttr(Map[I]); if not (Assigned(b) and (b.uri <> '')) then
AttrName := Attr.NSI.QName;
if Pos(WideString('xmlns'), AttrName^.Key) = 1 then
begin
{ this is a namespace declaration }
if Length(AttrName^.Key) = 5 then
begin
// TODO: check all consequences of having zero PrefixLength
Attr.SetNSI(stduri_xmlns, 0);
AddBinding(Attr, nil, 0);
end
else if AttrName^.Key[6] = ':' then
begin
Attr.SetNSI(stduri_xmlns, 6);
AddBinding(Attr, @AttrName^.Key[7], Length(AttrName^.Key)-6);
end;
end
else
begin
J := Pos(WideChar(':'), AttrName^.Key);
if J > 1 then
begin
FWorkAtts[PrefixCount].Attr := Attr;
FWorkAtts[PrefixCount].PrefixLen := J;
Inc(PrefixCount);
end;
end;
end;
end;
{ Pass 2, now all bindings are known, handle remaining prefixed attributes }
if PrefixCount > 0 then
begin
FNsAttHash.Init(PrefixCount);
for I := 0 to PrefixCount-1 do
begin
AttrName := FWorkAtts[I].Attr.NSI.QName;
if not FNSHelper.IsPrefixBound(PWideChar(AttrName^.Key), FWorkAtts[I].PrefixLen-1, Pfx) then
FatalError('Unbound prefix "%s"', [Pfx^.Key]); FatalError('Unbound prefix "%s"', [Pfx^.Key]);
b := TBinding(Pfx^.Data);
{ detect duplicates } { detect duplicates }
J := FWorkAtts[I].PrefixLen+1; J := attrData^.FColonPos+1;
AttrName := attrData^.FQName;
if FNsAttHash.Locate(@b.uri, @AttrName^.Key[J], Length(AttrName^.Key) - J+1) then if FNsAttHash.Locate(@b.uri, @AttrName^.Key[J], Length(AttrName^.Key) - J+1) then
FatalError('Duplicate prefixed attribute'); FatalError('Duplicate prefixed attribute');
// convert Attr into namespaced one (by hack for the time being) attrData^.FNsUri := doc.Names.FindOrAdd(PWideChar(b.uri), Length(b.uri));
FWorkAtts[I].Attr.SetNSI(b.uri, J-1);
end;
end;
{ Finally, expand the element name }
J := Pos(WideChar(':'), Element.NSI.QName^.Key);
if J > 1 then
begin
if not FNSHelper.IsPrefixBound(PWideChar(Element.NSI.QName^.Key), J-1, Pfx) then
FatalError('Unbound prefix "%s"', [Pfx^.Key]);
b := TBinding(Pfx^.Data);
Element.SetNSI(b.uri, J);
end
else
begin
b := FNSHelper.DefaultNSBinding;
if Assigned(b) then
Element.SetNSI(b.uri, 0);
end; end;
end; end;
@ -3474,6 +3487,7 @@ begin
Result^.FQName := AName; Result^.FQName := AName;
Result^.FPrefix := nil; Result^.FPrefix := nil;
Result^.FNsUri := nil; Result^.FNsUri := nil;
Result^.FIsDefault := False;
Inc(FAttrCount); Inc(FAttrCount);
end; end;
@ -3544,11 +3558,12 @@ begin
FCurrNode^.FValueLength := FValue.Length; FCurrNode^.FValueLength := FValue.Length;
end; end;
procedure TXMLReader.PushVC(aElDef: TDOMElementDef); procedure TXMLReader.PushVC(aElDef: TElementDecl);
begin begin
Inc(FNesting); Inc(FNesting);
FCurrNode := AllocNodeData(FNesting); FCurrNode := AllocNodeData(FNesting);
FCurrNode^.FPrefix := nil; FCurrNode^.FPrefix := nil;
FCurrNode^.FNsUri := nil;
if FNesting >= Length(FCursorStack) then if FNesting >= Length(FCursorStack) then
begin begin
@ -3585,7 +3600,7 @@ end;
{ TElementValidator } { TElementValidator }
function TElementValidator.IsElementAllowed(Def: TDOMElementDef): Boolean; function TElementValidator.IsElementAllowed(Def: TElementDecl): Boolean;
var var
Next: TContentParticle; Next: TContentParticle;
begin begin

View File

@ -141,6 +141,8 @@ type
FQName: PHashItem; FQName: PHashItem;
FPrefix: PHashItem; FPrefix: PHashItem;
FNsUri: PHashItem; FNsUri: PHashItem;
FColonPos: Integer;
FTypeInfo: TObject;
FNodeType: TXMLNodeType; FNodeType: TXMLNodeType;
FValueStr: WideString; FValueStr: WideString;