Ver código fonte

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

git-svn-id: trunk@16184 -
sergei 15 anos atrás
pai
commit
e0d8556cae
1 arquivos alterados com 69 adições e 19 exclusões
  1. 69 19
      packages/fcl-xml/src/xmlread.pp

+ 69 - 19
packages/fcl-xml/src/xmlread.pp

@@ -438,7 +438,7 @@ type
     function  ExpectName: WideString;                                   // [5]
     function  ExpectName: WideString;                                   // [5]
     function ParseLiteral(var ToFill: TWideCharBuf; aType: TLiteralType;
     function ParseLiteral(var ToFill: TWideCharBuf; aType: TLiteralType;
       Required: Boolean; Normalized: PBoolean = nil): Boolean;
       Required: Boolean; Normalized: PBoolean = nil): Boolean;
-    procedure ExpectAttValue(attr: TDOMAttr);                           // [10]
+    procedure ExpectAttValue(attrData: PNodeData);                      // [10]
     procedure ParseComment(discard: Boolean);                           // [15]
     procedure ParseComment(discard: Boolean);                           // [15]
     procedure ParsePI;                                                  // [16]
     procedure ParsePI;                                                  // [16]
     procedure CreatePINode;
     procedure CreatePINode;
@@ -482,6 +482,7 @@ type
     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)
+    procedure LoadAttribute(src: PNodeData; dest: TDOMAttr);
     procedure DoText(ch: PWideChar; Count: Integer; Whitespace: Boolean=False);
     procedure DoText(ch: PWideChar; Count: Integer; Whitespace: Boolean=False);
     procedure DoComment(ch: PWideChar; Count: Integer);
     procedure DoComment(ch: PWideChar; Count: Integer);
     procedure DoCDSect(ch: PWideChar; Count: Integer);
     procedure DoCDSect(ch: PWideChar; Count: Integer);
@@ -1589,14 +1590,16 @@ const
   AttrDelims: TSetOfChar = [#0, '<', '&', '''', '"', #9, #10, #13];
   AttrDelims: TSetOfChar = [#0, '<', '&', '''', '"', #9, #10, #13];
   GT_Delim: TSetOfChar = [#0, '>'];
   GT_Delim: TSetOfChar = [#0, '>'];
 
 
-procedure TXMLReader.ExpectAttValue(attr: TDOMAttr);
+procedure TXMLReader.ExpectAttValue(AttrData: PNodeData);
 var
 var
   wc: WideChar;
   wc: WideChar;
   Delim: WideChar;
   Delim: WideChar;
   ent: TDOMEntityEx;
   ent: TDOMEntityEx;
   start: TObject;
   start: TObject;
+  curr: PNodeData;
 begin
 begin
   SkipQuote(Delim);
   SkipQuote(Delim);
+  curr := AttrData;
   FValue.Length := 0;
   FValue.Length := 0;
   start := FSource.FEntity;
   start := FSource.FEntity;
   repeat
   repeat
@@ -1613,10 +1616,18 @@ begin
       begin
       begin
         if FValue.Length > 0 then
         if FValue.Length > 0 then
         begin
         begin
-          DoAttrText(attr, FValue.Buffer, FValue.Length);
+          curr := AllocAttributeValueChunk(curr);
+          curr^.FNodeType := ntText;
+          SetString(curr^.FValueStr, FValue.Buffer, FValue.Length);
           FValue.Length := 0;
           FValue.Length := 0;
         end;
         end;
-        AppendReference(attr, ent);
+        curr := AllocAttributeValueChunk(curr);
+        curr^.FNodeType := ntEntityReference;
+        // TODO: this probably should be placed to 'name'
+        if ent = nil then
+          SetString(curr^.FValueStr, FName.Buffer, FName.Length)
+        else
+          curr^.FValueStr := ent.FName;
       end
       end
       else
       else
         ContextPush(ent);
         ContextPush(ent);
@@ -1633,8 +1644,18 @@ begin
     else if (FSource.FEntity = start) or not ContextPop then    // #0
     else if (FSource.FEntity = start) or not ContextPop then    // #0
       FatalError('Literal has no closing quote', -1);
       FatalError('Literal has no closing quote', -1);
   until False;
   until False;
-  if FValue.Length > 0 then
-    DoAttrText(attr, FValue.Buffer, FValue.Length);
+  if Assigned(attrData^.FNext) then  // complex case
+  begin
+    FAttrCleanupFlag := True;
+    if FValue.Length > 0 then
+    begin
+      curr := AllocAttributeValueChunk(curr);
+      curr^.FNodeType := ntText;
+      SetString(curr^.FValueStr, FValue.Buffer, FValue.Length);
+    end;
+  end
+  else
+    SetString(attrData^.FValueStr, FValue.Buffer, FValue.Length);
   FValue.Length := 0;
   FValue.Length := 0;
 end;
 end;
 
 
@@ -2396,6 +2417,7 @@ var
   dt: TAttrDataType;
   dt: TAttrDataType;
   Found, DiscardIt: Boolean;
   Found, DiscardIt: Boolean;
   Offsets: array [Boolean] of Integer;
   Offsets: array [Boolean] of Integer;
+  attrData: PNodeData;
 begin
 begin
   ExpectWhitespace;
   ExpectWhitespace;
   ElDef := FindOrCreateElDef;
   ElDef := FindOrCreateElDef;
@@ -2504,7 +2526,13 @@ begin
           ValidationError('An attribute of type ID cannot have a default value',[]);
           ValidationError('An attribute of type ID cannot have a default value',[]);
 
 
 // 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);
+        attrData := AllocAttributeData(nil);
+        ExpectAttValue(attrData);
+
+        LoadAttribute(attrData, AttDef);   // convert to DOM form
+        CleanupAttributeData;
+        FAttrCount := 0;
+
         if not ValidateAttrSyntax(AttDef, AttDef.NodeValue) then
         if not ValidateAttrSyntax(AttDef, AttDef.NodeValue) then
           ValidationError('Default value for attribute ''%s'' has wrong syntax', [AttDef.Name]);
           ValidationError('Default value for attribute ''%s'' has wrong syntax', [AttDef.Name]);
       end;
       end;
@@ -2777,6 +2805,26 @@ const
     ntWhitespace
     ntWhitespace
   );
   );
 
 
+procedure TXMLReader.LoadAttribute(src: PNodeData; dest: TDOMAttr);
+var
+  curr: PNodeData;
+begin
+  if Assigned(src^.FNext) then
+  begin
+    curr := src^.FNext;
+    while Assigned(curr) do
+    begin
+      case curr^.FNodeType of
+        ntText: dest.InternalAppend(doc.CreateTextNode(curr^.FValueStr));
+        ntEntityReference: dest.InternalAppend(doc.CreateEntityReference(curr^.FValueStr));
+      end;
+      curr := curr^.FNext;
+    end;
+  end
+  else if src^.FValueStr <> '' then
+    dest.InternalAppend(doc.CreateTextNode(src^.FValueStr));
+end;
+
 procedure TXMLReader.ParseContent;
 procedure TXMLReader.ParseContent;
 begin
 begin
   FNext := xtText;
   FNext := xtText;
@@ -3101,8 +3149,9 @@ end;
 procedure TXMLReader.ParseAttribute(Elem: TDOMElement; ElDef: TDOMElementDef);
 procedure TXMLReader.ParseAttribute(Elem: TDOMElement; ElDef: TDOMElementDef);
 var
 var
   attr: TDOMAttr;
   attr: TDOMAttr;
+  attrData: PNodeData;
   AttDef: TDOMAttrDef;
   AttDef: TDOMAttrDef;
-  OldAttr: TDOMNode;
+  i: Integer;
 
 
 procedure CheckValue;
 procedure CheckValue;
 var
 var
@@ -3131,31 +3180,32 @@ end;
 
 
 begin
 begin
   CheckName;
   CheckName;
-  Inc(FAttrCount);
   attr := doc.CreateAttributeBuf(FName.Buffer, FName.Length);
   attr := doc.CreateAttributeBuf(FName.Buffer, FName.Length);
+  attrData := AllocAttributeData(attr.NSI.QName);
 
 
   if Assigned(ElDef) then
   if Assigned(ElDef) then
   begin
   begin
-    AttDef := TDOMAttrDef(ElDef.GetAttributeNode(attr.NSI.QName^.Key));
+    AttDef := TDOMAttrDef(ElDef.GetAttributeNode(attrData^.FQName^.Key));
     if AttDef = nil then
     if AttDef = nil then
-      ValidationError('Using undeclared attribute ''%s'' on element ''%s''',[attr.NSI.QName^.Key, Elem.NSI.QName^.Key], FName.Length)
+      ValidationError('Using undeclared attribute ''%s'' on element ''%s''',
+        [attrData^.FQName^.Key, FNodeStack[FNesting].FQName^.Key], FName.Length)
     else
     else
       AttDef.Tag := FAttrTag;  // indicates that this one is specified
       AttDef.Tag := FAttrTag;  // indicates that this one is specified
   end
   end
   else
   else
     AttDef := nil;
     AttDef := nil;
 
 
-  // !!cannot use TDOMElement.SetAttributeNode because it will free old attribute
-  OldAttr := Elem.Attributes.SetNamedItem(Attr);
-  if Assigned(OldAttr) then
-  begin
-    OldAttr.Free;
-    FatalError('Duplicate attribute', FName.Length);
-  end;
+  // check for duplicates
+  for i := 1 to FAttrCount-1 do
+    if FNodeStack[FNesting+i].FQName = attrData^.FQName then
+      FatalError('Duplicate attribute', FName.Length);
 
 
   ExpectEq;
   ExpectEq;
-  ExpectAttValue(attr);
+  ExpectAttValue(attrData);
+
+  LoadAttribute(attrData, attr);
 
 
+  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
     CheckValue;
     CheckValue;
 end;
 end;