Browse Source

* xmlread.pp: split handling of node and validation stacks. Validation stack is now maintained only in validation mode.

git-svn-id: trunk@16973 -
sergei 14 years ago
parent
commit
e46c51e1bf
1 changed files with 47 additions and 54 deletions
  1. 47 54
      packages/fcl-xml/src/xmlread.pp

+ 47 - 54
packages/fcl-xml/src/xmlread.pp

@@ -249,6 +249,8 @@ type
     FElementDef: TElementDecl;
     FCurCP: TContentParticle;
     FFailed: Boolean;
+    FSaViolation: Boolean;
+    FContentType: TElementContentType;       // =ctAny when FElementDef is nil
     function IsElementAllowed(Def: TElementDecl): Boolean;
     function Incomplete: Boolean;
   end;
@@ -284,8 +286,6 @@ type
     FDocType: TDTDModel;
     FPEMap: THashTable;
     FForwardRefs: TFPList;
-    FCurrContentType: TElementContentType;
-    FSaViolation: Boolean;
     FDTDStartPos: PWideChar;
     FIntSubset: TWideCharBuf;
     FAttrTag: Cardinal;
@@ -347,6 +347,7 @@ type
     FPrefixedAttrs: Integer;
     FSpecifiedAttrs: Integer;
     FNodeStack: TNodeDataDynArray;
+    FValidatorNesting: Integer;
     FValidators: TValidatorDynArray;
     FAttrChunks: TFPList;
     FFreeAttrChunk: PNodeData;
@@ -413,7 +414,6 @@ type
 
     procedure PushVC(aElDef: TElementDecl);
     procedure PopVC;
-    procedure UpdateConstraints;
     procedure ValidateDTD;
     procedure ValidateCurrentNode;
     procedure ValidationError(const Msg: string; const args: array of const; LineOffs: Integer = -1);
@@ -1316,6 +1316,7 @@ begin
   FNameTable := doc.Names;
   FState := rsProlog;
   FNesting := 0;
+  FValidatorNesting := 0;
   FCurrNode := @FNodeStack[0];
   FFragmentMode := False;
   NSPrepare;
@@ -1344,6 +1345,7 @@ begin
   FNameTable := doc.Names;
   FState := rsRoot;
   FNesting := 0;
+  FValidatorNesting := 0;
   FCurrNode := @FNodeStack[0];
   FFragmentMode := True;
   FXML11 := doc.InheritsFrom(TXMLDocument) and (TXMLDocument(doc).XMLVersion = '1.1');
@@ -2673,7 +2675,6 @@ 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
@@ -2688,9 +2689,11 @@ begin
         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
+        if not FValidators[FValidatorNesting].IsElementAllowed(ElDef) then
           DoErrorPos(esError, 'Element ''%s'' is not allowed in this context',[FCurrNode^.FQName^.Key], FCurrNode^.FLoc);
 
+        PushVC(ElDef);
+
         { Validate attributes }
         for i := 1 to FAttrCount do
         begin
@@ -2720,32 +2723,37 @@ begin
 
     ntEndElement:
       begin
-        if FValidators[FNesting].Incomplete then
+        if FValidators[FValidatorNesting].Incomplete then
           ValidationError('Element ''%s'' is missing required sub-elements', [FCurrNode^.FQName^.Key], -1);
+        if FValidatorNesting > 0 then
+          Dec(FValidatorNesting);
       end;
 
-    ntText, ntWhitespace:
-      case FCurrContentType of
+    ntText, ntSignificantWhitespace:
+      case FValidators[FValidatorNesting].FContentType of
         ctChildren:
           if FCurrNode^.FNodeType = ntText then
             ValidationError('Character data is not allowed in element-only content',[])
           else
-            if FSaViolation then
+          begin
+            if FValidators[FValidatorNesting].FSaViolation then
               StandaloneError(-1);
+            FCurrNode^.FNodeType := ntWhitespace;
+          end;
         ctEmpty:
           ValidationError('Character data is not allowed in EMPTY elements', []);
       end;
 
     ntCDATA:
-      if FCurrContentType = ctChildren then
+      if FValidators[FValidatorNesting].FContentType = ctChildren then
         ValidationError('CDATA sections are not allowed in element-only content',[]);
 
     ntProcessingInstruction:
-      if FCurrContentType = ctEmpty then
+      if FValidators[FValidatorNesting].FContentType = ctEmpty then
         ValidationError('Processing instructions are not allowed within EMPTY elements', []);
 
     ntComment:
-      if FCurrContentType = ctEmpty then
+      if FValidators[FValidatorNesting].FContentType = ctEmpty then
         ValidationError('Comments are not allowed within EMPTY elements', []);
 
     ntDocumentType:
@@ -2765,12 +2773,12 @@ end;
 
 procedure TXMLReader.HandleEntityEnd;
 begin
-  FValidators[FNesting-1] := FValidators[FNesting];
   ContextPop(True);
-  PopVC;
+  if FNesting > 0 then Dec(FNesting);
   FCurrNode := @FNodeStack[FNesting+1];
   FCurrNode^.FNodeType := ntEndEntity;
   // TODO: other properties of FCurrNode
+  FNext := xtText;
 end;
 
 procedure TXMLReader.ResolveEntity;
@@ -2787,7 +2795,8 @@ procedure TXMLReader.DoStartEntity;
 var
   src: TXMLCharSource;
 begin
-  PushVC(nil);
+  Inc(FNesting);
+  FCurrNode := AllocNodeData(FNesting);
   if Assigned(FCurrEntity) then
     ContextPush(FCurrEntity)
   else
@@ -2797,10 +2806,6 @@ begin
     src.Kind := skManualPop;
     Initialize(src);
   end;
-
-  { Compensate for an extra entry in node stack }
-  FValidators[FNesting] := FValidators[FNesting-1];
-  UpdateConstraints;
   FNext := xtText;
 end;
 
@@ -2873,7 +2878,7 @@ const
   );
 
   textNodeTypes: array[Boolean] of TXMLNodeType = (
-    ntWhitespace,
+    ntSignificantWhitespace,
     ntText
   );
 
@@ -2892,9 +2897,9 @@ begin
       ntText:
         cursor.InternalAppend(doc.CreateTextNodeBuf(FValue.Buffer, FValue.Length, False));
 
-      ntWhitespace:
+      ntWhitespace, ntSignificantWhitespace:
         if FPreserveWhitespace then
-          cursor.InternalAppend(doc.CreateTextNodeBuf(FValue.Buffer, FValue.Length, FCurrContentType = ctChildren))
+          cursor.InternalAppend(doc.CreateTextNodeBuf(FValue.Buffer, FValue.Length, FCurrNode^.FNodeType = ntWhitespace))
         else
           Continue;
 
@@ -3062,7 +3067,7 @@ begin
       if FState <> rsRoot then
         FatalError('Illegal at document level');
 
-      if FCurrContentType = ctEmpty then
+      if FValidators[FValidatorNesting].FContentType = ctEmpty then
         ValidationError('References are illegal in EMPTY elements', []);
 
       if ParseRef(FValue) or ResolvePredefined then
@@ -3157,8 +3162,9 @@ begin
   FAttrCount := 0;
   FPrefixedAttrs := 0;
   FSpecifiedAttrs := 0;
-  PushVC(ElDef);           // this increases FNesting
 
+  Inc(FNesting);
+  FCurrNode := AllocNodeData(FNesting);
   FCurrNode^.FQName := ElName;
   FCurrNode^.FNodeType := ntElement;
   FCurrNode^.FColonPos := FColonPos;
@@ -3592,9 +3598,6 @@ function TXMLReader.AllocAttributeData: PNodeData;
 begin
   Result := AllocNodeData(FNesting + FAttrCount + 1);
   Result^.FNodeType := ntAttribute;
-  Result^.FPrefix := nil;
-  Result^.FNsUri := nil;
-  Result^.FIDEntry := nil;
   Result^.FIsDefault := False;
   Inc(FAttrCount);
 end;
@@ -3606,6 +3609,9 @@ begin
     SetLength(FNodeStack, AIndex * 2 + 2);
 
   Result := @FNodeStack[AIndex];
+  Result^.FPrefix := nil;
+  Result^.FNsUri := nil;
+  Result^.FIDEntry := nil;
 end;
 
 function TXMLReader.AllocAttributeValueChunk(APrev: PNodeData): PNodeData;
@@ -3671,21 +3677,23 @@ end;
 
 procedure TXMLReader.PushVC(aElDef: TElementDecl);
 begin
-  Inc(FNesting);
-  FCurrNode := AllocNodeData(FNesting);
-  FCurrNode^.FPrefix := nil;
-  FCurrNode^.FNsUri := nil;
-  FCurrNode^.FIDEntry := nil;
+  Inc(FValidatorNesting);
+  if FValidatorNesting >= Length(FValidators) then
+    SetLength(FValidators, FValidatorNesting * 2);
 
-  if FNesting >= Length(FValidators) then
+  with FValidators[FValidatorNesting] do
   begin
-    SetLength(FValidators, FNesting * 2);
+    FElementDef := aElDef;
+    FCurCP := nil;
+    FFailed := False;
+    FContentType := ctAny;
+    FSaViolation := False;
+    if Assigned(aElDef) then
+    begin
+      FContentType := aElDef.ContentType;
+      FSaViolation := FStandalone and aElDef.ExternallyDeclared;
+    end;
   end;
-
-  FValidators[FNesting].FElementDef := aElDef;
-  FValidators[FNesting].FCurCP := nil;
-  FValidators[FNesting].FFailed := False;
-  UpdateConstraints;
 end;
 
 procedure TXMLReader.PopVC;
@@ -3694,24 +3702,9 @@ begin
     FState := rsEpilog;
   if FNesting > 0 then Dec(FNesting);
   FCurrNode := @FNodeStack[FNesting];
-  UpdateConstraints;
   FNext := xtText;
 end;
 
-procedure TXMLReader.UpdateConstraints;
-begin
-  if FValidate and Assigned(FValidators[FNesting].FElementDef) then
-  begin
-    FCurrContentType := FValidators[FNesting].FElementDef.ContentType;
-    FSaViolation := FStandalone and (FValidators[FNesting].FElementDef.ExternallyDeclared);
-  end
-  else
-  begin
-    FCurrContentType := ctAny;
-    FSaViolation := False;
-  end;
-end;
-
 { TElementValidator }
 
 function TElementValidator.IsElementAllowed(Def: TElementDecl): Boolean;