Browse Source

* fcl-xml, moved syntax validation procedure from TXMLTextReader to TAttributeDef
* reduced memory requirements by getting rid of FAttrChunks and creating FForwardRefs on demand.

git-svn-id: trunk@20523 -

sergei 13 years ago
parent
commit
c1531f5c12
2 changed files with 58 additions and 48 deletions
  1. 17 0
      packages/fcl-xml/src/dtdmodel.pp
  2. 41 48
      packages/fcl-xml/src/xmlread.pp

+ 17 - 0
packages/fcl-xml/src/dtdmodel.pp

@@ -79,6 +79,7 @@ type
     destructor Destroy; override;
     destructor Destroy; override;
     function AddEnumToken(Buf: PWideChar; Len: Integer): Boolean;
     function AddEnumToken(Buf: PWideChar; Len: Integer): Boolean;
     function HasEnumToken(const aValue: XMLString): Boolean;
     function HasEnumToken(const aValue: XMLString): Boolean;
+    function ValidateSyntax(const aValue: XMLString; Namespaces: Boolean): Boolean;
     property Data: PNodeData read FData;
     property Data: PNodeData read FData;
     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;
@@ -445,4 +446,20 @@ begin
   Result := False;
   Result := False;
 end;
 end;
 
 
+function TAttributeDef.ValidateSyntax(const aValue: XMLString; Namespaces: Boolean): Boolean;
+begin
+  case FDataType of
+    dtId, dtIdRef, dtEntity: Result := IsXmlName(aValue) and
+      ((not Namespaces) or (Pos(WideChar(':'), aValue) = 0));
+    dtIdRefs, dtEntities: Result := IsXmlNames(aValue) and
+      ((not Namespaces) or (Pos(WideChar(':'), aValue) = 0));
+    dtNmToken: Result := IsXmlNmToken(aValue) and HasEnumToken(aValue);
+    dtNmTokens: Result := IsXmlNmTokens(aValue);
+    // IsXmlName() not necessary - enum is never empty and contains valid names
+    dtNotation: Result := HasEnumToken(aValue);
+  else
+    Result := True;
+  end;
+end;
+
 end.
 end.

+ 41 - 48
packages/fcl-xml/src/xmlread.pp

@@ -156,8 +156,6 @@ type
 
 
   TXMLSourceKind = (skNone, skInternalSubset, skManualPop);
   TXMLSourceKind = (skNone, skInternalSubset, skManualPop);
 
 
-  TLocation = xmlutils.TLocation;
-
   TDOMEntityEx = class(TDOMEntity);
   TDOMEntityEx = class(TDOMEntity);
 
 
   TXMLTextReader = class;
   TXMLTextReader = class;
@@ -327,7 +325,6 @@ type
     function ContextPop(Forced: Boolean = False): Boolean;
     function ContextPop(Forced: Boolean = False): Boolean;
     function ParseQuantity: TCPQuant;
     function ParseQuantity: TCPQuant;
     procedure StoreLocation(out Loc: TLocation);
     procedure StoreLocation(out Loc: TLocation);
-    function ValidateAttrSyntax(AttrDef: TAttributeDef; const aValue: XMLString): Boolean;
     procedure ValidateAttrValue(AttrDef: TAttributeDef; attrData: PNodeData);
     procedure ValidateAttrValue(AttrDef: TAttributeDef; attrData: PNodeData);
     procedure AddForwardRef(Buf: PWideChar; Length: Integer);
     procedure AddForwardRef(Buf: PWideChar; Length: Integer);
     procedure ClearForwardRefs;
     procedure ClearForwardRefs;
@@ -353,7 +350,6 @@ type
     FNodeStack: TNodeDataDynArray;
     FNodeStack: TNodeDataDynArray;
     FValidatorNesting: Integer;
     FValidatorNesting: Integer;
     FValidators: TValidatorDynArray;
     FValidators: TValidatorDynArray;
-    FAttrChunks: TFPList;
     FFreeAttrChunk: PNodeData;
     FFreeAttrChunk: PNodeData;
     FAttrCleanupFlag: Boolean;
     FAttrCleanupFlag: Boolean;
     // ReadAttributeValue state
     // ReadAttributeValue state
@@ -1283,8 +1279,6 @@ begin
   inherited Create;
   inherited Create;
   BufAllocate(FName, 128);
   BufAllocate(FName, 128);
   BufAllocate(FValue, 512);
   BufAllocate(FValue, 512);
-  FForwardRefs := TFPList.Create;
-  FAttrChunks := TFPList.Create;
 
 
   SetLength(FNodeStack, 16);
   SetLength(FNodeStack, 16);
   SetLength(FValidators, 16);
   SetLength(FValidators, 16);
@@ -1354,10 +1348,17 @@ end;
 
 
 destructor TXMLTextReader.Destroy;
 destructor TXMLTextReader.Destroy;
 var
 var
-  i: Integer;
+  cur: PNodeData;
 begin
 begin
-  for i := FAttrChunks.Count-1 downto 0 do
-    Dispose(PNodeData(FAttrChunks.List^[i]));
+  if FAttrCleanupFlag then
+    CleanupAttributes;
+  while Assigned(FFreeAttrChunk) do
+  begin
+    cur := FFreeAttrChunk;
+    FFreeAttrChunk := cur^.FNext;
+    Dispose(cur);
+  end;
+
   if Assigned(FEntityValue.Buffer) then
   if Assigned(FEntityValue.Buffer) then
     FreeMem(FEntityValue.Buffer);
     FreeMem(FEntityValue.Buffer);
   FreeMem(FName.Buffer);
   FreeMem(FName.Buffer);
@@ -1372,7 +1373,6 @@ begin
   FDocType.Release;
   FDocType.Release;
   FIDMap.Free;
   FIDMap.Free;
   FForwardRefs.Free;
   FForwardRefs.Free;
-  FAttrChunks.Free;
   if FNameTableOwned then
   if FNameTableOwned then
     FNameTable.Free;
     FNameTable.Free;
   inherited Destroy;
   inherited Destroy;
@@ -2523,7 +2523,8 @@ 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) and FValidate then
+              if (not DiscardIt) and FValidate and
+                (FDocType.Notations.Get(FName.Buffer,FName.Length)=nil) then
                 AddForwardRef(FName.Buffer, FName.Length);
                 AddForwardRef(FName.Buffer, FName.Length);
               SkipWhitespace;
               SkipWhitespace;
             until not CheckForChar('|');
             until not CheckForChar('|');
@@ -2563,7 +2564,7 @@ begin
 // See comments to valid-sa-094: PE expansion should be disabled in AttDef.
 // See comments to valid-sa-094: PE expansion should be disabled in AttDef.
         ExpectAttValue(AttDef.Data, dt <> dtCDATA);
         ExpectAttValue(AttDef.Data, dt <> dtCDATA);
 
 
-        if not ValidateAttrSyntax(AttDef, AttDef.Data^.FValueStr) then
+        if not AttDef.ValidateSyntax(AttDef.Data^.FValueStr, FNamespaces) then
           ValidationError('Default value for attribute ''%s'' has wrong syntax', [attrName^.Key]);
           ValidationError('Default value for attribute ''%s'' has wrong syntax', [attrName^.Key]);
       end;
       end;
       // SAX: DeclHandler.AttributeDecl(...)
       // SAX: DeclHandler.AttributeDecl(...)
@@ -2635,7 +2636,7 @@ begin
           StoreLocation(FTokenStart);  { needed for AddForwardRef }
           StoreLocation(FTokenStart);  { needed for AddForwardRef }
           CheckName;
           CheckName;
           SetString(Entity.FNotationName, FName.Buffer, FName.Length);
           SetString(Entity.FNotationName, FName.Buffer, FName.Length);
-          if FValidate then
+          if FValidate and (FDocType.Notations.Get(FName.Buffer, FName.Length)=nil) then
             AddForwardRef(FName.Buffer, FName.Length);
             AddForwardRef(FName.Buffer, FName.Length);
           // SAX: DTDHandler.UnparsedEntityDecl(...);
           // SAX: DTDHandler.UnparsedEntityDecl(...);
         end;
         end;
@@ -3157,7 +3158,7 @@ begin
             // TODO: what about normalization of AttDef.Value? (Currently it IS normalized)
             // TODO: what about normalization of AttDef.Value? (Currently it IS normalized)
             if (AttDef.Default = adFixed) and (AttDef.Data^.FValueStr <> attr^.FValueStr) then
             if (AttDef.Default = adFixed) and (AttDef.Data^.FValueStr <> attr^.FValueStr) then
               DoErrorPos(esError, 'Value of attribute ''%s'' does not match its #FIXED default',[attr^.FQName^.Key], attr^.FLoc2);
               DoErrorPos(esError, 'Value of attribute ''%s'' does not match its #FIXED default',[attr^.FQName^.Key], attr^.FLoc2);
-            if not ValidateAttrSyntax(AttDef, attr^.FValueStr) then
+            if not AttDef.ValidateSyntax(attr^.FValueStr, FNamespaces) then
               DoErrorPos(esError, 'Attribute ''%s'' type mismatch', [attr^.FQName^.Key], attr^.FLoc2);
               DoErrorPos(esError, 'Attribute ''%s'' type mismatch', [attr^.FQName^.Key], attr^.FLoc2);
             ValidateAttrValue(AttDef, attr);
             ValidateAttrValue(AttDef, attr);
           end;
           end;
@@ -3838,6 +3839,8 @@ procedure TXMLTextReader.AddForwardRef(Buf: PWideChar; Length: Integer);
 var
 var
   w: PForwardRef;
   w: PForwardRef;
 begin
 begin
+  if FForwardRefs = nil then
+    FForwardRefs := TFPList.Create;
   New(w);
   New(w);
   SetString(w^.Value, Buf, Length);
   SetString(w^.Value, Buf, Length);
   w^.Loc := FTokenStart;
   w^.Loc := FTokenStart;
@@ -3848,20 +3851,26 @@ procedure TXMLTextReader.ClearForwardRefs;
 var
 var
   I: Integer;
   I: Integer;
 begin
 begin
-  for I := 0 to FForwardRefs.Count-1 do
-    Dispose(PForwardRef(FForwardRefs.List^[I]));
-  FForwardRefs.Clear;
+  if Assigned(FForwardRefs) then
+  begin
+    for I := 0 to FForwardRefs.Count-1 do
+      Dispose(PForwardRef(FForwardRefs.List^[I]));
+    FForwardRefs.Clear;
+  end;
 end;
 end;
 
 
 procedure TXMLTextReader.ValidateIdRefs;
 procedure TXMLTextReader.ValidateIdRefs;
 var
 var
   I: Integer;
   I: Integer;
 begin
 begin
-  for I := 0 to FForwardRefs.Count-1 do
-    with PForwardRef(FForwardRefs.List^[I])^ do
-      if (FIDMap = nil) or (FIDMap.Find(PWideChar(Value), Length(Value)) = nil) then
-        DoErrorPos(esError, 'The ID ''%s'' does not match any element', [Value], Loc);
-  ClearForwardRefs;
+  if Assigned(FForwardRefs) then
+  begin
+    for I := 0 to FForwardRefs.Count-1 do
+      with PForwardRef(FForwardRefs.List^[I])^ do
+        if (FIDMap = nil) or (FIDMap.Find(PWideChar(Value), Length(Value)) = nil) then
+          DoErrorPos(esError, 'The ID ''%s'' does not match any element', [Value], Loc);
+    ClearForwardRefs;
+  end;
 end;
 end;
 
 
 procedure TXMLTextReader.ProcessDefaultAttributes(ElDef: TElementDecl);
 procedure TXMLTextReader.ProcessDefaultAttributes(ElDef: TElementDecl);
@@ -3996,22 +4005,6 @@ begin
   Result := True;
   Result := True;
 end;
 end;
 
 
-function TXMLTextReader.ValidateAttrSyntax(AttrDef: TAttributeDef; const aValue: XMLString): Boolean;
-begin
-  case AttrDef.DataType of
-    dtId, dtIdRef, dtEntity: Result := IsXmlName(aValue) and
-      ((not FNamespaces) or (Pos(WideChar(':'), aValue) = 0));
-    dtIdRefs, dtEntities: Result := IsXmlNames(aValue) and
-      ((not FNamespaces) or (Pos(WideChar(':'), aValue) = 0));
-    dtNmToken: Result := IsXmlNmToken(aValue) and AttrDef.HasEnumToken(aValue);
-    dtNmTokens: Result := IsXmlNmTokens(aValue);
-    // IsXmlName() not necessary - enum is never empty and contains valid names
-    dtNotation: Result := AttrDef.HasEnumToken(aValue);
-  else
-    Result := True;
-  end;
-end;
-
 procedure TXMLTextReader.ValidateAttrValue(AttrDef: TAttributeDef; attrData: PNodeData);
 procedure TXMLTextReader.ValidateAttrValue(AttrDef: TAttributeDef; attrData: PNodeData);
 var
 var
   L, StartPos, EndPos: Integer;
   L, StartPos, EndPos: Integer;
@@ -4057,10 +4050,13 @@ procedure TXMLTextReader.ValidateDTD;
 var
 var
   I: Integer;
   I: Integer;
 begin
 begin
-  for I := 0 to FForwardRefs.Count-1 do
-    with PForwardRef(FForwardRefs[I])^ do
-      if FDocType.Notations.Get(PWideChar(Value), Length(Value)) = nil then
-        DoErrorPos(esError, 'Notation ''%s'' is not declared', [Value], Loc);
+  if Assigned(FForwardRefs) then
+  begin
+    for I := 0 to FForwardRefs.Count-1 do
+      with PForwardRef(FForwardRefs[I])^ do
+        if FDocType.Notations.Get(PWideChar(Value), Length(Value)) = nil then
+          DoErrorPos(esError, 'Notation ''%s'' is not declared', [Value], Loc);
+  end;
 end;
 end;
 
 
 procedure TXMLTextReader.DoNotationDecl(const aName, aPubID, aSysID: XMLString);
 procedure TXMLTextReader.DoNotationDecl(const aName, aPubID, aSysID: XMLString);
@@ -4128,17 +4124,13 @@ begin
     chunk^.FNext := nil;
     chunk^.FNext := nil;
   end
   end
   else { no free chunks, create a new one }
   else { no free chunks, create a new one }
-  begin
-    New(chunk);
-    FillChar(chunk^, sizeof(TNodeData), 0);
-    if FState <> rsDTD then
-      FAttrChunks.Add(chunk);
-  end;
+    chunk := AllocMem(sizeof(TNodeData));
   APrev^.FNext := chunk;
   APrev^.FNext := chunk;
   APrev := chunk;
   APrev := chunk;
   { assume text node, for entity refs it is overridden later }
   { assume text node, for entity refs it is overridden later }
   chunk^.FNodeType := ntText;
   chunk^.FNodeType := ntText;
   chunk^.FQName := nil;
   chunk^.FQName := nil;
+  chunk^.FColonPos := -1;
   { without PWideChar typecast and in $T-, FPC treats '@' result as PAnsiChar... }
   { without PWideChar typecast and in $T-, FPC treats '@' result as PAnsiChar... }
   SetString(chunk^.FValueStr, PWideChar(@FValue.Buffer[Offset]), FValue.Length-Offset);
   SetString(chunk^.FValueStr, PWideChar(@FValue.Buffer[Offset]), FValue.Length-Offset);
 end;
 end;
@@ -4173,6 +4165,7 @@ begin
   FCurrNode := @FNodeStack[FNesting];
   FCurrNode := @FNodeStack[FNesting];
   FCurrNode^.FNodeType := typ;
   FCurrNode^.FNodeType := typ;
   FCurrNode^.FQName := AName;
   FCurrNode^.FQName := AName;
+  FCurrNode^.FColonPos := -1;
   FCurrNode^.FValueStart := FValue.Buffer;
   FCurrNode^.FValueStart := FValue.Buffer;
   FCurrNode^.FValueLength := FValue.Length;
   FCurrNode^.FValueLength := FValue.Length;
 end;
 end;