Bladeren bron

* xmlread.pp: maintain ID map at reader side, so it can do ID/IDREF validation without DOM.
* also don't allocate TForwardRef records for backward references, saves some ticks and bytes.

git-svn-id: trunk@16264 -

sergei 14 jaren geleden
bovenliggende
commit
6cb9cdcc0d
3 gewijzigde bestanden met toevoegingen van 43 en 17 verwijderingen
  1. 2 1
      packages/fcl-xml/src/dom.pp
  2. 40 16
      packages/fcl-xml/src/xmlread.pp
  3. 1 0
      packages/fcl-xml/src/xmlutils.pp

+ 2 - 1
packages/fcl-xml/src/dom.pp

@@ -501,8 +501,9 @@ type
     // Extensions to DOM interface:
     // Extensions to DOM interface:
     constructor Create;
     constructor Create;
     destructor Destroy; override;
     destructor Destroy; override;
-    function AddID(Attr: TDOMAttr): Boolean;
+    function AddID(Attr: TDOMAttr): Boolean; deprecated;
     property Names: THashTable read FNames;
     property Names: THashTable read FNames;
+    property IDs: THashTable read FIDList write FIDList;
   end;
   end;
 
 
   TXMLDocument = class(TDOMDocument)
   TXMLDocument = class(TDOMDocument)

+ 40 - 16
packages/fcl-xml/src/xmlread.pp

@@ -305,6 +305,7 @@ type
     FToken: TXMLToken;
     FToken: TXMLToken;
     FNext: TXMLToken;
     FNext: TXMLToken;
     FCurrEntity: TDOMEntityEx;
     FCurrEntity: TDOMEntityEx;
+    FIDMap: THashTable;
 
 
     FNSHelper: TNSSupport;
     FNSHelper: TNSSupport;
     FNsAttHash: TDblHashArray;
     FNsAttHash: TDblHashArray;
@@ -335,7 +336,7 @@ type
     function ParseQuantity: TCPQuant;
     function ParseQuantity: TCPQuant;
     procedure StoreLocation(out Loc: TLocation);
     procedure StoreLocation(out Loc: TLocation);
     function ValidateAttrSyntax(AttrDef: TAttributeDef; const aValue: WideString): Boolean;
     function ValidateAttrSyntax(AttrDef: TAttributeDef; const aValue: WideString): Boolean;
-    procedure ValidateAttrValue(Attr: TDOMAttr; const aValue: WideString);
+    procedure ValidateAttrValue(AttrDef: TAttributeDef; attrData: PNodeData);
     procedure AddForwardRef(Buf: PWideChar; Length: Integer);
     procedure AddForwardRef(Buf: PWideChar; Length: Integer);
     procedure ClearForwardRefs;
     procedure ClearForwardRefs;
     procedure ValidateIdRefs;
     procedure ValidateIdRefs;
@@ -350,6 +351,7 @@ type
     procedure CleanupAttribute(aNode: PNodeData);
     procedure CleanupAttribute(aNode: PNodeData);
     procedure CleanupAttributes;
     procedure CleanupAttributes;
     procedure SetNodeInfoWithValue(typ: TXMLNodeType; AName: PHashItem = nil);
     procedure SetNodeInfoWithValue(typ: TXMLNodeType; AName: PHashItem = nil);
+    function AddId(aNodeData: PNodeData): Boolean;
   protected
   protected
     FNesting: Integer;
     FNesting: Integer;
     FCurrNode: PNodeData;
     FCurrNode: PNodeData;
@@ -1287,7 +1289,7 @@ begin
   FNSHelper.Free;
   FNSHelper.Free;
   if FOwnsDoctype then
   if FOwnsDoctype then
     FDocType.Free;
     FDocType.Free;
-
+  FIDMap.Free;
   FForwardRefs.Free;
   FForwardRefs.Free;
   FAttrChunks.Free;
   FAttrChunks.Free;
   inherited Destroy;
   inherited Destroy;
@@ -1331,8 +1333,11 @@ begin
   if FState < rsRoot then
   if FState < rsRoot then
     FatalError('Root element is missing');
     FatalError('Root element is missing');
 
 
-  if FValidate and Assigned(FDocType) then
+  if FValidate then
     ValidateIdRefs;
     ValidateIdRefs;
+
+  doc.IDs := FIDMap;
+  FIDMap := nil;
 end;
 end;
 
 
 procedure TXMLReader.ProcessFragment(ASource: TXMLCharSource; AOwner: TDOMNode);
 procedure TXMLReader.ProcessFragment(ASource: TXMLCharSource; AOwner: TDOMNode);
@@ -3044,7 +3049,9 @@ begin
   begin
   begin
     Attr := LoadAttribute(doc, @FNodeStack[FNesting+i]);
     Attr := LoadAttribute(doc, @FNodeStack[FNesting+i]);
     NewElem.SetAttributeNode(Attr);
     NewElem.SetAttributeNode(Attr);
-    ValidateAttrValue(Attr, FNodeStack[FNesting+i].FValueStr);
+    // Attach element to ID map entry if necessary
+    if Assigned(FNodeStack[FNesting+i].FIDEntry) then
+      FNodeStack[FNesting+i].FIDEntry^.Data := NewElem;
   end;
   end;
 
 
   if not IsEmpty then
   if not IsEmpty then
@@ -3107,7 +3114,7 @@ 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(AttDef, attrData);
 end;
 end;
 
 
 begin
 begin
@@ -3201,7 +3208,7 @@ var
 begin
 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 Doc.GetElementById(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, Format('The ID ''%s'' does not match any element', [Value]), Loc);
   ClearForwardRefs;
   ClearForwardRefs;
 end;
 end;
@@ -3366,24 +3373,27 @@ begin
   end;
   end;
 end;
 end;
 
 
-procedure TXMLReader.ValidateAttrValue(Attr: TDOMAttr; const aValue: WideString);
+procedure TXMLReader.ValidateAttrValue(AttrDef: TAttributeDef; attrData: PNodeData);
 var
 var
   L, StartPos, EndPos: Integer;
   L, StartPos, EndPos: Integer;
   Entity: TDOMEntity;
   Entity: TDOMEntity;
 begin
 begin
-  L := Length(aValue);
-  case Attr.DataType of
-    dtId: if not Doc.AddID(Attr) then
-            ValidationError('The ID ''%s'' is not unique', [aValue], -1);
+  L := Length(attrData^.FValueStr);
+  case AttrDef.DataType of
+    dtId: begin
+      if not AddID(attrData) then
+        ValidationError('The ID ''%s'' is not unique', [attrData^.FValueStr], -1);
+    end;
 
 
     dtIdRef, dtIdRefs: begin
     dtIdRef, dtIdRefs: begin
       StartPos := 1;
       StartPos := 1;
       while StartPos <= L do
       while StartPos <= L do
       begin
       begin
         EndPos := StartPos;
         EndPos := StartPos;
-        while (EndPos <= L) and (aValue[EndPos] <> #32) do
+        while (EndPos <= L) and (attrData^.FValueStr[EndPos] <> #32) do
           Inc(EndPos);
           Inc(EndPos);
-        AddForwardRef(@aValue[StartPos], EndPos-StartPos);
+        if (FIDMap = nil) or (FIDMap.Find(@attrData^.FValueStr[StartPos], EndPos-StartPos) = nil) then
+          AddForwardRef(@attrData^.FValueStr[StartPos], EndPos-StartPos);
         StartPos := EndPos + 1;
         StartPos := EndPos + 1;
       end;
       end;
     end;
     end;
@@ -3393,14 +3403,14 @@ begin
       while StartPos <= L do
       while StartPos <= L do
       begin
       begin
         EndPos := StartPos;
         EndPos := StartPos;
-        while (EndPos <= L) and (aValue[EndPos] <> #32) do
+        while (EndPos <= L) and (attrData^.FValueStr[EndPos] <> #32) do
           Inc(EndPos);
           Inc(EndPos);
         if Assigned(FGEMap) then
         if Assigned(FGEMap) then
-          Entity := TDOMEntity(FGEMap.Get(@aValue[StartPos], EndPos-StartPos))
+          Entity := TDOMEntity(FGEMap.Get(@attrData^.FValueStr[StartPos], EndPos-StartPos))
         else
         else
           Entity := nil;
           Entity := nil;
         if (Entity = nil) or (Entity.NotationName = '') then
         if (Entity = nil) or (Entity.NotationName = '') then
-          ValidationError('Attribute ''%s'' type mismatch', [Attr.Name], -1);
+          ValidationError('Attribute ''%s'' type mismatch', [attrData^.FQName^.Key], -1);
         StartPos := EndPos + 1;
         StartPos := EndPos + 1;
       end;
       end;
     end;
     end;
@@ -3505,6 +3515,18 @@ begin
     ValidationError('Duplicate notation declaration: ''%s''', [aName]);
     ValidationError('Duplicate notation declaration: ''%s''', [aName]);
 end;
 end;
 
 
+function TXMLReader.AddId(aNodeData: PNodeData): Boolean;
+var
+  e: PHashItem;
+begin
+  if FIDMap = nil then
+    FIDMap := THashTable.Create(256, False);
+  e := FIDMap.FindOrAdd(PWideChar(aNodeData^.FValueStr), Length(aNodeData^.FValueStr), Result);
+  Result := not Result;
+  if Result then
+    aNodeData^.FIDEntry := e;
+end;
+
 function TXMLReader.AllocAttributeData(AName: PHashItem): PNodeData;
 function TXMLReader.AllocAttributeData(AName: PHashItem): PNodeData;
 begin
 begin
   Result := AllocNodeData(FNesting + FAttrCount + 1);
   Result := AllocNodeData(FNesting + FAttrCount + 1);
@@ -3512,6 +3534,7 @@ begin
   Result^.FQName := AName;
   Result^.FQName := AName;
   Result^.FPrefix := nil;
   Result^.FPrefix := nil;
   Result^.FNsUri := nil;
   Result^.FNsUri := nil;
+  Result^.FIDEntry := nil;
   Result^.FIsDefault := False;
   Result^.FIsDefault := False;
   Inc(FAttrCount);
   Inc(FAttrCount);
 end;
 end;
@@ -3592,6 +3615,7 @@ begin
   FCurrNode := AllocNodeData(FNesting);
   FCurrNode := AllocNodeData(FNesting);
   FCurrNode^.FPrefix := nil;
   FCurrNode^.FPrefix := nil;
   FCurrNode^.FNsUri := nil;
   FCurrNode^.FNsUri := nil;
+  FCurrNode^.FIDEntry := nil;
 
 
   if FNesting >= Length(FCursorStack) then
   if FNesting >= Length(FCursorStack) then
   begin
   begin

+ 1 - 0
packages/fcl-xml/src/xmlutils.pp

@@ -151,6 +151,7 @@ type
     FTypeInfo: TObject;
     FTypeInfo: TObject;
     FLoc: TLocation;
     FLoc: TLocation;
     FLoc2: TLocation;              // for attributes: start of value
     FLoc2: TLocation;              // for attributes: start of value
+    FIDEntry: PHashItem;           // ID attributes: entry in ID map
     FNodeType: TXMLNodeType;
     FNodeType: TXMLNodeType;
 
 
     FValueStr: WideString;
     FValueStr: WideString;