mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-08-18 22:09:28 +02:00
* xmlread.pp, started separating validation checks from the rest of code.
git-svn-id: trunk@16946 -
This commit is contained in:
parent
db6bc3bd00
commit
6a26b9cff3
@ -357,7 +357,9 @@ type
|
|||||||
|
|
||||||
procedure DoError(Severity: TErrorSeverity; const descr: string; LineOffs: Integer=0);
|
procedure DoError(Severity: TErrorSeverity; const descr: string; LineOffs: Integer=0);
|
||||||
procedure DoErrorPos(Severity: TErrorSeverity; const descr: string;
|
procedure DoErrorPos(Severity: TErrorSeverity; const descr: string;
|
||||||
const ErrPos: TLocation);
|
const ErrPos: TLocation); overload;
|
||||||
|
procedure DoErrorPos(Severity: TErrorSeverity; const descr: string;
|
||||||
|
const args: array of const; const ErrPos: TLocation); overload;
|
||||||
procedure FatalError(const descr: String; LineOffs: Integer=0); overload;
|
procedure FatalError(const descr: String; LineOffs: Integer=0); overload;
|
||||||
procedure FatalError(const descr: string; const args: array of const; LineOffs: Integer=0); overload;
|
procedure FatalError(const descr: string; const args: array of const; LineOffs: Integer=0); overload;
|
||||||
procedure FatalError(Expected: WideChar); overload;
|
procedure FatalError(Expected: WideChar); overload;
|
||||||
@ -385,7 +387,6 @@ type
|
|||||||
procedure ParseStartTag; // [39]
|
procedure ParseStartTag; // [39]
|
||||||
procedure ParseEndTag; // [42]
|
procedure ParseEndTag; // [42]
|
||||||
procedure DoStartElement;
|
procedure DoStartElement;
|
||||||
procedure DoEndElement;
|
|
||||||
procedure HandleEntityStart;
|
procedure HandleEntityStart;
|
||||||
procedure HandleEntityEnd;
|
procedure HandleEntityEnd;
|
||||||
procedure ResolveEntity;
|
procedure ResolveEntity;
|
||||||
@ -417,7 +418,7 @@ type
|
|||||||
procedure PopVC;
|
procedure PopVC;
|
||||||
procedure UpdateConstraints;
|
procedure UpdateConstraints;
|
||||||
procedure ValidateDTD;
|
procedure ValidateDTD;
|
||||||
procedure ValidateRoot;
|
procedure ValidateCurrentNode;
|
||||||
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 DTDReloadHook;
|
procedure DTDReloadHook;
|
||||||
@ -1069,6 +1070,12 @@ begin
|
|||||||
DoErrorPos(Severity, descr, FTokenStart);
|
DoErrorPos(Severity, descr, FTokenStart);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TXMLReader.DoErrorPos(Severity: TErrorSeverity; const descr: string;
|
||||||
|
const args: array of const; const ErrPos: TLocation);
|
||||||
|
begin
|
||||||
|
DoErrorPos(Severity, Format(descr, args), ErrPos);
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TXMLReader.DoErrorPos(Severity: TErrorSeverity; const descr: string; const ErrPos: TLocation);
|
procedure TXMLReader.DoErrorPos(Severity: TErrorSeverity; const descr: string; const ErrPos: TLocation);
|
||||||
var
|
var
|
||||||
E: EXMLReadError;
|
E: EXMLReadError;
|
||||||
@ -1931,8 +1938,6 @@ begin
|
|||||||
SetString(NameStr, FName.Buffer, FName.Length);
|
SetString(NameStr, FName.Buffer, FName.Length);
|
||||||
SetString(ValueStr, FValue.Buffer, FValue.Length);
|
SetString(ValueStr, FValue.Buffer, FValue.Length);
|
||||||
// SAX: ContentHandler.ProcessingInstruction(Name, Value);
|
// SAX: ContentHandler.ProcessingInstruction(Name, Value);
|
||||||
if FCurrContentType = ctEmpty then
|
|
||||||
ValidationError('Processing instructions are not allowed within EMPTY elements', []);
|
|
||||||
|
|
||||||
PINode := Doc.CreateProcessingInstruction(NameStr, ValueStr);
|
PINode := Doc.CreateProcessingInstruction(NameStr, ValueStr);
|
||||||
FCursorStack[FNesting].InternalAppend(PINode);
|
FCursorStack[FNesting].InternalAppend(PINode);
|
||||||
@ -2383,7 +2388,7 @@ begin
|
|||||||
if not AttDef.AddEnumToken(FName.Buffer, FName.Length) then
|
if not AttDef.AddEnumToken(FName.Buffer, FName.Length) then
|
||||||
ValidationError('Duplicate token in NOTATION attribute declaration',[], FName.Length);
|
ValidationError('Duplicate token in NOTATION attribute declaration',[], FName.Length);
|
||||||
|
|
||||||
if not DiscardIt then
|
if (not DiscardIt) and FValidate then
|
||||||
AddForwardRef(FName.Buffer, FName.Length);
|
AddForwardRef(FName.Buffer, FName.Length);
|
||||||
SkipWhitespace;
|
SkipWhitespace;
|
||||||
until not CheckForChar('|');
|
until not CheckForChar('|');
|
||||||
@ -2494,6 +2499,7 @@ begin
|
|||||||
ExpectWhitespace;
|
ExpectWhitespace;
|
||||||
StoreLocation(FTokenStart);
|
StoreLocation(FTokenStart);
|
||||||
Entity.FNotationName := ExpectName;
|
Entity.FNotationName := ExpectName;
|
||||||
|
if FValidate then
|
||||||
AddForwardRef(FName.Buffer, FName.Length);
|
AddForwardRef(FName.Buffer, FName.Length);
|
||||||
// SAX: DTDHandler.UnparsedEntityDecl(...);
|
// SAX: DTDHandler.UnparsedEntityDecl(...);
|
||||||
end;
|
end;
|
||||||
@ -2669,6 +2675,67 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TXMLReader.ValidateCurrentNode;
|
||||||
|
var
|
||||||
|
ElDef: TElementDecl;
|
||||||
|
begin
|
||||||
|
case FCurrNode^.FNodeType of
|
||||||
|
ntElement:
|
||||||
|
begin
|
||||||
|
{ TODO: pushing *validation* context must be moved here }
|
||||||
|
if (FNesting = 1) and (not FFragmentMode) then
|
||||||
|
begin
|
||||||
|
if Assigned(FDocType) then
|
||||||
|
begin
|
||||||
|
if FDocType.FName <> FCurrNode^.FQName^.Key then
|
||||||
|
DoErrorPos(esError, 'Root element name does not match DTD', FCurrNode^.FLoc);
|
||||||
|
end
|
||||||
|
else
|
||||||
|
DoErrorPos(esError, 'Missing DTD', FCurrNode^.FLoc);
|
||||||
|
end;
|
||||||
|
ElDef := TElementDecl(FCurrNode^.FQName^.Data);
|
||||||
|
if (ElDef = nil) or (ElDef.ContentType = ctUndeclared) then
|
||||||
|
DoErrorPos(esError, 'Using undeclared element ''%s''',[FCurrNode^.FQName^.Key], FCurrNode^.FLoc);
|
||||||
|
|
||||||
|
if not FValidators[FNesting-1].IsElementAllowed(ElDef) then
|
||||||
|
DoErrorPos(esError, 'Element ''%s'' is not allowed in this context',[FCurrNode^.FQName^.Key], FCurrNode^.FLoc);
|
||||||
|
end;
|
||||||
|
|
||||||
|
ntEndElement:
|
||||||
|
begin
|
||||||
|
if FValidators[FNesting].Incomplete then
|
||||||
|
ValidationError('Element ''%s'' is missing required sub-elements', [FCurrNode^.FQName^.Key], -1);
|
||||||
|
end;
|
||||||
|
|
||||||
|
ntText, ntWhitespace:
|
||||||
|
case FCurrContentType of
|
||||||
|
ctChildren:
|
||||||
|
if FCurrNode^.FNodeType = ntText then
|
||||||
|
ValidationError('Character data is not allowed in element-only content',[])
|
||||||
|
else
|
||||||
|
if FSaViolation then
|
||||||
|
StandaloneError(-1);
|
||||||
|
ctEmpty:
|
||||||
|
ValidationError('Character data is not allowed in EMPTY elements', []);
|
||||||
|
end;
|
||||||
|
|
||||||
|
ntCDATA:
|
||||||
|
if FCurrContentType = ctChildren then
|
||||||
|
ValidationError('CDATA sections are not allowed in element-only content',[]);
|
||||||
|
|
||||||
|
ntProcessingInstruction:
|
||||||
|
if FCurrContentType = ctEmpty then
|
||||||
|
ValidationError('Processing instructions are not allowed within EMPTY elements', []);
|
||||||
|
|
||||||
|
ntComment:
|
||||||
|
if FCurrContentType = ctEmpty then
|
||||||
|
ValidationError('Comments are not allowed within EMPTY elements', []);
|
||||||
|
|
||||||
|
ntDocumentType:
|
||||||
|
ValidateDTD;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TXMLReader.DoEntityReference;
|
procedure TXMLReader.DoEntityReference;
|
||||||
begin
|
begin
|
||||||
FCursorStack[FNesting].AppendChild(doc.CreateEntityReference(FCurrNode^.FQName^.Key));
|
FCursorStack[FNesting].AppendChild(doc.CreateEntityReference(FCurrNode^.FQName^.Key));
|
||||||
@ -2799,8 +2866,8 @@ const
|
|||||||
);
|
);
|
||||||
|
|
||||||
textNodeTypes: array[Boolean] of TXMLNodeType = (
|
textNodeTypes: array[Boolean] of TXMLNodeType = (
|
||||||
ntText,
|
ntWhitespace,
|
||||||
ntWhitespace
|
ntText
|
||||||
);
|
);
|
||||||
|
|
||||||
procedure TXMLReader.ParseContent;
|
procedure TXMLReader.ParseContent;
|
||||||
@ -2808,9 +2875,11 @@ begin
|
|||||||
FNext := xtText;
|
FNext := xtText;
|
||||||
while Read do
|
while Read do
|
||||||
begin
|
begin
|
||||||
|
if FValidate then
|
||||||
|
ValidateCurrentNode;
|
||||||
case FCurrNode^.FNodeType of
|
case FCurrNode^.FNodeType of
|
||||||
ntText, ntWhitespace:
|
ntText, ntWhitespace:
|
||||||
DoText(FValue.Buffer, FValue.Length, FToken = xtWhitespace);
|
DoText(FValue.Buffer, FValue.Length, FCurrNode^.FNodeType = ntWhitespace);
|
||||||
ntCDATA:
|
ntCDATA:
|
||||||
DoCDSect(FValue.Buffer, FValue.Length);
|
DoCDSect(FValue.Buffer, FValue.Length);
|
||||||
ntProcessingInstruction:
|
ntProcessingInstruction:
|
||||||
@ -2820,10 +2889,9 @@ begin
|
|||||||
ntElement:
|
ntElement:
|
||||||
DoStartElement;
|
DoStartElement;
|
||||||
ntEndElement:
|
ntEndElement:
|
||||||
DoEndElement;
|
;
|
||||||
ntDocumentType:
|
ntDocumentType:
|
||||||
begin
|
begin
|
||||||
ValidateDTD;
|
|
||||||
if not FCanonical then
|
if not FCanonical then
|
||||||
doc.AppendChild(TDOMDocumentType.Create(doc, FDocType));
|
doc.AppendChild(TDOMDocumentType.Create(doc, FDocType));
|
||||||
end;
|
end;
|
||||||
@ -3039,25 +3107,18 @@ begin
|
|||||||
FatalError('Only one top-level element allowed', FName.Length)
|
FatalError('Only one top-level element allowed', FName.Length)
|
||||||
else if FState < rsRoot then
|
else if FState < rsRoot then
|
||||||
begin
|
begin
|
||||||
if FValidate then
|
// dispose notation refs from DTD, if any
|
||||||
ValidateRoot;
|
ClearForwardRefs;
|
||||||
FState := rsRoot;
|
FState := rsRoot;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// we're about to process a new set of attributes
|
// we're about to process a new set of attributes
|
||||||
Inc(FAttrTag);
|
Inc(FAttrTag);
|
||||||
|
|
||||||
// Remember the hash entry, we'll need it often
|
// Get hash entry for element name
|
||||||
ElName := FNameTable.FindOrAdd(FName.Buffer, FName.Length);
|
ElName := FNameTable.FindOrAdd(FName.Buffer, FName.Length);
|
||||||
|
|
||||||
// Find declaration for this element
|
// Find declaration for this element
|
||||||
ElDef := TElementDecl(ElName^.Data);
|
ElDef := TElementDecl(ElName^.Data);
|
||||||
if (ElDef = nil) or (ElDef.ContentType = ctUndeclared) then
|
|
||||||
ValidationError('Using undeclared element ''%s''',[ElName^.Key], FName.Length);
|
|
||||||
|
|
||||||
// Check if new element is allowed in current context
|
|
||||||
if FValidate and not FValidators[FNesting].IsElementAllowed(ElDef) then
|
|
||||||
ValidationError('Element ''%s'' is not allowed in this context',[ElName^.Key], FName.Length);
|
|
||||||
|
|
||||||
IsEmpty := False;
|
IsEmpty := False;
|
||||||
FAttrCount := 0;
|
FAttrCount := 0;
|
||||||
@ -3068,7 +3129,7 @@ begin
|
|||||||
FCurrNode^.FQName := ElName;
|
FCurrNode^.FQName := ElName;
|
||||||
FCurrNode^.FNodeType := ntElement;
|
FCurrNode^.FNodeType := ntElement;
|
||||||
FCurrNode^.FColonPos := FColonPos;
|
FCurrNode^.FColonPos := FColonPos;
|
||||||
FCurrNode^.FLoc := FTokenStart;
|
StoreLocation(FCurrNode^.FLoc);
|
||||||
Dec(FCurrNode^.FLoc.LinePos, FName.Length);
|
Dec(FCurrNode^.FLoc.LinePos, FName.Length);
|
||||||
|
|
||||||
if FNamespaces then
|
if FNamespaces then
|
||||||
@ -3102,6 +3163,7 @@ begin
|
|||||||
if FNamespaces then
|
if FNamespaces then
|
||||||
begin
|
begin
|
||||||
{ Assign namespace URIs to prefixed attrs }
|
{ Assign namespace URIs to prefixed attrs }
|
||||||
|
if FPrefixedAttrs <> 0 then
|
||||||
ProcessNamespaceAtts;
|
ProcessNamespaceAtts;
|
||||||
{ Expand the element name }
|
{ Expand the element name }
|
||||||
if Assigned(FCurrNode^.FPrefix) then
|
if Assigned(FCurrNode^.FPrefix) then
|
||||||
@ -3132,12 +3194,6 @@ begin
|
|||||||
FNext := xtPopEmptyElement;
|
FNext := xtPopEmptyElement;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TXMLReader.DoEndElement;
|
|
||||||
begin
|
|
||||||
if FValidate and FValidators[FNesting].Incomplete then
|
|
||||||
ValidationError('Element ''%s'' is missing required sub-elements', [FNodeStack[FNesting].FQName^.Key], -1);
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TXMLReader.ParseEndTag; // [42]
|
procedure TXMLReader.ParseEndTag; // [42]
|
||||||
var
|
var
|
||||||
ElName: PHashItem;
|
ElName: PHashItem;
|
||||||
@ -3274,7 +3330,7 @@ begin
|
|||||||
for I := 0 to FForwardRefs.Count-1 do
|
for I := 0 to FForwardRefs.Count-1 do
|
||||||
with PForwardRef(FForwardRefs.List^[I])^ do
|
with PForwardRef(FForwardRefs.List^[I])^ do
|
||||||
if (FIDMap = nil) or (FIDMap.Find(PWideChar(Value), Length(Value)) = nil) then
|
if (FIDMap = nil) or (FIDMap.Find(PWideChar(Value), Length(Value)) = nil) then
|
||||||
DoErrorPos(esError, Format('The ID ''%s'' does not match any element', [Value]), Loc);
|
DoErrorPos(esError, 'The ID ''%s'' does not match any element', [Value], Loc);
|
||||||
ClearForwardRefs;
|
ClearForwardRefs;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -3360,9 +3416,6 @@ var
|
|||||||
attrData: PNodeData;
|
attrData: PNodeData;
|
||||||
b: TBinding;
|
b: TBinding;
|
||||||
begin
|
begin
|
||||||
if FPrefixedAttrs = 0 then
|
|
||||||
Exit;
|
|
||||||
|
|
||||||
FNsAttHash.Init(FPrefixedAttrs);
|
FNsAttHash.Init(FPrefixedAttrs);
|
||||||
for I := 1 to FAttrCount do
|
for I := 1 to FAttrCount do
|
||||||
begin
|
begin
|
||||||
@ -3479,27 +3532,14 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TXMLReader.ValidateRoot;
|
|
||||||
begin
|
|
||||||
if Assigned(FDocType) then
|
|
||||||
begin
|
|
||||||
if not BufEquals(FName, FDocType.FName) then
|
|
||||||
ValidationError('Root element name does not match DTD', [], FName.Length);
|
|
||||||
end
|
|
||||||
else
|
|
||||||
ValidationError('Missing DTD', [], FName.Length);
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TXMLReader.ValidateDTD;
|
procedure TXMLReader.ValidateDTD;
|
||||||
var
|
var
|
||||||
I: Integer;
|
I: Integer;
|
||||||
begin
|
begin
|
||||||
if FValidate then
|
|
||||||
for I := 0 to FForwardRefs.Count-1 do
|
for I := 0 to FForwardRefs.Count-1 do
|
||||||
with PForwardRef(FForwardRefs[I])^ do
|
with PForwardRef(FForwardRefs[I])^ do
|
||||||
if FDocType.Notations.Get(PWideChar(Value), Length(Value)) = nil then
|
if FDocType.Notations.Get(PWideChar(Value), Length(Value)) = nil then
|
||||||
DoErrorPos(esError, Format('Notation ''%s'' is not declared', [Value]), Loc);
|
DoErrorPos(esError, 'Notation ''%s'' is not declared', [Value], Loc);
|
||||||
ClearForwardRefs;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TXMLReader.DoText(ch: PWideChar; Count: Integer; Whitespace: Boolean);
|
procedure TXMLReader.DoText(ch: PWideChar; Count: Integer; Whitespace: Boolean);
|
||||||
@ -3515,19 +3555,6 @@ begin
|
|||||||
if (Whitespace and (not FPreserveWhitespace)) or (Count = 0) then
|
if (Whitespace and (not FPreserveWhitespace)) or (Count = 0) then
|
||||||
Exit;
|
Exit;
|
||||||
|
|
||||||
// Validating filter part
|
|
||||||
case FCurrContentType of
|
|
||||||
ctChildren:
|
|
||||||
if not Whitespace then
|
|
||||||
ValidationError('Character data is not allowed in element-only content',[])
|
|
||||||
else
|
|
||||||
if FSaViolation then
|
|
||||||
StandaloneError(-1);
|
|
||||||
ctEmpty:
|
|
||||||
ValidationError('Character data is not allowed in EMPTY elements', []);
|
|
||||||
end;
|
|
||||||
|
|
||||||
// Document builder part
|
|
||||||
TextNode := Doc.CreateTextNodeBuf(ch, Count, Whitespace and (FCurrContentType = ctChildren));
|
TextNode := Doc.CreateTextNodeBuf(ch, Count, Whitespace and (FCurrContentType = ctChildren));
|
||||||
FCursorStack[FNesting].InternalAppend(TextNode);
|
FCursorStack[FNesting].InternalAppend(TextNode);
|
||||||
end;
|
end;
|
||||||
@ -3536,11 +3563,6 @@ procedure TXMLReader.DoComment(ch: PWideChar; Count: Integer);
|
|||||||
var
|
var
|
||||||
Node: TDOMComment;
|
Node: TDOMComment;
|
||||||
begin
|
begin
|
||||||
// validation filter part
|
|
||||||
if FCurrContentType = ctEmpty then
|
|
||||||
ValidationError('Comments are not allowed within EMPTY elements', []);
|
|
||||||
|
|
||||||
// DOM builder part
|
|
||||||
if (not FIgnoreComments) and (FState <> rsDTD) then
|
if (not FIgnoreComments) and (FState <> rsDTD) then
|
||||||
begin
|
begin
|
||||||
Node := Doc.CreateCommentBuf(ch, Count);
|
Node := Doc.CreateCommentBuf(ch, Count);
|
||||||
@ -3554,9 +3576,6 @@ var
|
|||||||
begin
|
begin
|
||||||
Assert(not FCDSectionsAsText, 'Should not be called when CDSectionsAsText=True');
|
Assert(not FCDSectionsAsText, 'Should not be called when CDSectionsAsText=True');
|
||||||
|
|
||||||
if FCurrContentType = ctChildren then
|
|
||||||
ValidationError('CDATA sections are not allowed in element-only content',[]);
|
|
||||||
|
|
||||||
SetString(s, ch, Count);
|
SetString(s, ch, Count);
|
||||||
FCursorStack[FNesting].InternalAppend(doc.CreateCDATASection(s));
|
FCursorStack[FNesting].InternalAppend(doc.CreateCDATASection(s));
|
||||||
end;
|
end;
|
||||||
|
Loading…
Reference in New Issue
Block a user