laz2_read: keep attribute order

git-svn-id: trunk@35251 -
This commit is contained in:
mattias 2012-02-08 23:27:48 +00:00
parent 52b3608f2e
commit ffd6e0f8b5

View File

@ -379,15 +379,18 @@ type
// NamedNodeMap // NamedNodeMap
// ------------------------------------------------------- // -------------------------------------------------------
{ TDOMNamedNodeMap }
TDOMNamedNodeMap = class(TObject) TDOMNamedNodeMap = class(TObject)
protected protected
FOwner: TDOMNode; FOwner: TDOMNode;
FNodeType: Integer; FNodeType: Integer;
FList: TFPList; FSortedList: TFPList; // list of TDOMNode sorted via CompareName
function GetItem(index: LongWord): TDOMNode; FPosList: TFPList; // list of TDOMNode not sorted
function GetPosItem(index: LongWord): TDOMNode;
function GetLength: LongWord; function GetLength: LongWord;
function Find(const name: DOMString; out Index: LongWord): Boolean; function FindSorted(const name: DOMString; out Index: LongWord): Boolean;
function Delete(index: LongWord): TDOMNode; function DeleteSorted(index: LongWord): TDOMNode;
procedure RestoreDefault(const name: DOMString); procedure RestoreDefault(const name: DOMString);
function InternalRemove(const name: DOMString): TDOMNode; function InternalRemove(const name: DOMString): TDOMNode;
function ValidateInsert(arg: TDOMNode): Integer; function ValidateInsert(arg: TDOMNode): Integer;
@ -403,7 +406,7 @@ type
function setNamedItemNS(arg: TDOMNode): TDOMNode; virtual; function setNamedItemNS(arg: TDOMNode): TDOMNode; virtual;
function removeNamedItemNS(const namespaceURI,localName: DOMString): TDOMNode; virtual; function removeNamedItemNS(const namespaceURI,localName: DOMString): TDOMNode; virtual;
property Item[index: LongWord]: TDOMNode read GetItem; default; property Item[index: LongWord]: TDOMNode read GetPosItem; default;
property Length: LongWord read GetLength; property Length: LongWord read GetLength;
end; end;
@ -856,7 +859,7 @@ type
TAttributeMap = class(TDOMNamedNodeMap) TAttributeMap = class(TDOMNamedNodeMap)
private private
function FindNS(nsIndex: Integer; const aLocalName: DOMString; function FindNS(nsIndex: Integer; const aLocalName: DOMString;
out Index: LongWord): Boolean; out SortedIndex: LongWord): Boolean;
function InternalRemoveNS(const nsURI, aLocalName: DOMString): TDOMNode; function InternalRemoveNS(const nsURI, aLocalName: DOMString): TDOMNode;
public public
function getNamedItemNS(const namespaceURI, localName: DOMString): TDOMNode; override; function getNamedItemNS(const namespaceURI, localName: DOMString): TDOMNode; override;
@ -1775,43 +1778,45 @@ begin
inherited Create; inherited Create;
FOwner := AOwner; FOwner := AOwner;
FNodeType := ANodeType; FNodeType := ANodeType;
FList := TFPList.Create; FSortedList := TFPList.Create;
FPosList := TFPList.Create;
end; end;
destructor TDOMNamedNodeMap.Destroy; destructor TDOMNamedNodeMap.Destroy;
var var
I: Integer; I: Integer;
begin begin
for I := FList.Count-1 downto 0 do for I := FPosList.Count-1 downto 0 do
TDOMNode(FList[I]).Free; TDOMNode(FPosList[I]).Free;
FList.Free; FSortedList.Free;
FPosList.Free;
inherited Destroy; inherited Destroy;
end; end;
function TDOMNamedNodeMap.GetItem(index: LongWord): TDOMNode; function TDOMNamedNodeMap.GetPosItem(index: LongWord): TDOMNode;
begin begin
if index < LongWord(FList.Count) then if index < LongWord(FPosList.Count) then
Result := TDOMNode(FList.List^[index]) Result := TDOMNode(FPosList.List^[index])
else else
Result := nil; Result := nil;
end; end;
function TDOMNamedNodeMap.GetLength: LongWord; function TDOMNamedNodeMap.GetLength: LongWord;
begin begin
Result := FList.Count; Result := FPosList.Count;
end; end;
function TDOMNamedNodeMap.Find(const name: DOMString; out Index: LongWord): Boolean; function TDOMNamedNodeMap.FindSorted(const name: DOMString; out Index: LongWord): Boolean;
var var
L, H, I, C: Integer; L, H, I, C: Integer;
begin begin
Result := False; Result := False;
L := 0; L := 0;
H := FList.Count - 1; H := FSortedList.Count - 1;
while L <= H do while L <= H do
begin begin
I := (L + H) shr 1; I := (L + H) shr 1;
C := TDOMNode(FList.List^[I]).CompareName(name); C := TDOMNode(FSortedList.List^[I]).CompareName(name);
if C > 0 then L := I + 1 else if C > 0 then L := I + 1 else
begin begin
H := I - 1; H := I - 1;
@ -1829,8 +1834,8 @@ function TDOMNamedNodeMap.GetNamedItem(const name: DOMString): TDOMNode;
var var
i: Cardinal; i: Cardinal;
begin begin
if Find(name, i) then if FindSorted(name, i) then
Result := TDOMNode(FList.List^[i]) Result := TDOMNode(FSortedList.List^[i])
else else
Result := nil; Result := nil;
end; end;
@ -1874,20 +1879,26 @@ begin
if FNodeType = ATTRIBUTE_NODE then if FNodeType = ATTRIBUTE_NODE then
begin begin
TDOMAttr(arg).FOwnerElement := TDOMElement(FOwner); TDOMAttr(arg).FOwnerElement := TDOMElement(FOwner);
Exists := Find(TDOMAttr(arg).Name, i); // optimization Exists := FindSorted(TDOMAttr(arg).Name, i); // optimization
end end
else else
Exists := Find(arg.NodeName, i); Exists := FindSorted(arg.NodeName, i);
if Exists then if Exists then
begin begin
Result := TDOMNode(FList.List^[i]); Result := TDOMNode(FSortedList.List^[i]);
if (Result <> arg) and (FNodeType = ATTRIBUTE_NODE) then if (Result <> arg) then
TDOMAttr(Result).FOwnerElement := nil; begin
FList.List^[i] := arg; if (FNodeType = ATTRIBUTE_NODE) then
TDOMAttr(Result).FOwnerElement := nil;
FSortedList.List^[i] := arg;
i:=FPosList.IndexOf(Result);
FPosList.List^[i] := arg;
end;
exit; exit;
end; end;
FList.Insert(i, arg); FSortedList.Insert(i, arg);
FPosList.Add(arg);
Result := nil; Result := nil;
end; end;
@ -1901,10 +1912,11 @@ begin
Result := SetNamedItem(arg); Result := SetNamedItem(arg);
end; end;
function TDOMNamedNodeMap.Delete(index: LongWord): TDOMNode; function TDOMNamedNodeMap.DeleteSorted(index: LongWord): TDOMNode;
begin begin
Result := TDOMNode(FList.List^[index]); Result := TDOMNode(FSortedList.List^[index]);
FList.Delete(index); FSortedList.Delete(index);
FPosList.Remove(Result);
if FNodeType = ATTRIBUTE_NODE then if FNodeType = ATTRIBUTE_NODE then
TDOMAttr(Result).FOwnerElement := nil; TDOMAttr(Result).FOwnerElement := nil;
end; end;
@ -1934,9 +1946,9 @@ var
i: Cardinal; i: Cardinal;
begin begin
Result := nil; Result := nil;
if Find(name, i) then if FindSorted(name, i) then
begin begin
Result := Delete(I); Result := DeleteSorted(I);
RestoreDefault(name); RestoreDefault(name);
end; end;
end; end;
@ -1963,14 +1975,14 @@ end;
// Since list is kept sorted by nodeName, we must use linear search here. // Since list is kept sorted by nodeName, we must use linear search here.
// This routine is not called while parsing, so parsing speed is not lowered. // This routine is not called while parsing, so parsing speed is not lowered.
function TAttributeMap.FindNS(nsIndex: Integer; const aLocalName: DOMString; function TAttributeMap.FindNS(nsIndex: Integer; const aLocalName: DOMString;
out Index: LongWord): Boolean; out SortedIndex: LongWord): Boolean;
var var
I: Integer; I: Integer;
P: DOMPChar; P: DOMPChar;
begin begin
for I := 0 to FList.Count-1 do for I := 0 to FSortedList.Count-1 do
begin begin
with TDOMAttr(FList.List^[I]) do with TDOMAttr(FSortedList.List^[I]) do
begin begin
if nsIndex = FNSI.NSIndex then if nsIndex = FNSI.NSIndex then
begin begin
@ -1979,13 +1991,14 @@ begin
Inc(P, FNSI.PrefixLen); Inc(P, FNSI.PrefixLen);
if CompareDOMStrings(DOMPChar(aLocalName), P, System.Length(aLocalName), System.Length(FNSI.QName^.Key) - FNSI.PrefixLen) = 0 then if CompareDOMStrings(DOMPChar(aLocalName), P, System.Length(aLocalName), System.Length(FNSI.QName^.Key) - FNSI.PrefixLen) = 0 then
begin begin
Index := I; SortedIndex := I;
Result := True; Result := True;
Exit; Exit;
end; end;
end; end;
end; end;
end; end;
SortedIndex := -1;
Result := False; Result := False;
end; end;
@ -1998,7 +2011,7 @@ begin
nsIndex := FOwner.FOwnerDocument.IndexOfNS(nsURI); nsIndex := FOwner.FOwnerDocument.IndexOfNS(nsURI);
if (nsIndex >= 0) and FindNS(nsIndex, aLocalName, i) then if (nsIndex >= 0) and FindNS(nsIndex, aLocalName, i) then
begin begin
Result := Delete(I); Result := DeleteSorted(I);
RestoreDefault(TDOMAttr(Result).FNSI.QName^.Key); RestoreDefault(TDOMAttr(Result).FNSI.QName^.Key);
end; end;
end; end;
@ -2010,7 +2023,7 @@ var
begin begin
nsIndex := FOwner.FOwnerDocument.IndexOfNS(namespaceURI); nsIndex := FOwner.FOwnerDocument.IndexOfNS(namespaceURI);
if (nsIndex >= 0) and FindNS(nsIndex, localName, i) then if (nsIndex >= 0) and FindNS(nsIndex, localName, i) then
Result := TDOMNode(FList.List^[i]) Result := TDOMNode(FSortedList.List^[i])
else else
Result := nil; Result := nil;
end; end;
@ -2031,18 +2044,23 @@ begin
// calling LocalName is no good... but it is done once // calling LocalName is no good... but it is done once
if FindNS(FNSI.NSIndex, localName, i) then if FindNS(FNSI.NSIndex, localName, i) then
begin begin
Result := TDOMNode(FList.List^[i]); Result := TDOMNode(FSortedList.List^[i]);
FList.Delete(i); FSortedList.Delete(i);
FPosList.Remove(Result);
end; end;
// Do a non-namespace search in order to keep the list sorted on nodeName // Do a non-namespace search in order to keep the list sorted on nodeName
Exists := Find(FNSI.QName^.Key, i); Exists := FindSorted(FNSI.QName^.Key, i);
if Exists and (Result = nil) then // case when arg has no namespace if Exists and (Result = nil) then // case when arg has no namespace
begin begin
Result := TDOMNode(FList.List^[i]); Result := TDOMNode(FSortedList.List^[i]);
FList.List^[i] := arg; FSortedList.List^[i] := arg;
i:=FPosList.IndexOf(Result);
FPosList.List^[i] := arg;
end end
else else begin
FList.Insert(i, arg); FSortedList.Insert(i, arg);
FPosList.Add(arg);
end;
end; end;
if Assigned(Result) then if Assigned(Result) then
TDOMAttr(Result).FOwnerElement := nil; TDOMAttr(Result).FOwnerElement := nil;
@ -2992,13 +3010,14 @@ var
attr: TDOMAttr; attr: TDOMAttr;
begin begin
Changing; Changing;
if Attributes.Find(name, I) then if Attributes.FindSorted(name, I) then
Attr := FAttributes[I] as TDOMAttr Attr := FAttributes[I] as TDOMAttr
else else
begin begin
Attr := FOwnerDocument.CreateAttribute(name); Attr := FOwnerDocument.CreateAttribute(name);
Attr.FOwnerElement := Self; Attr.FOwnerElement := Self;
FAttributes.FList.Insert(I, Attr); FAttributes.FSortedList.Insert(I, Attr);
FAttributes.FPosList.Add(Attr);
end; end;
attr.NodeValue := value; attr.NodeValue := value;
end; end;
@ -3036,7 +3055,8 @@ begin
begin begin
Attr := TDOMAttr(FAttributes[I]); Attr := TDOMAttr(FAttributes[I]);
// need to reinsert because the nodeName may change // need to reinsert because the nodeName may change
FAttributes.FList.Delete(I); FAttributes.FPosList.Remove(FAttributes.FSortedList.List^[i]);
FAttributes.FSortedList.Delete(I);
end end
else else
begin begin
@ -3047,8 +3067,9 @@ begin
Include(Attr.FFlags, nfLevel2); Include(Attr.FFlags, nfLevel2);
end; end;
// keep list sorted by DOM Level 1 name // keep list sorted by DOM Level 1 name
FAttributes.Find(qualifiedName, I); FAttributes.FindSorted(qualifiedName, I);
FAttributes.FList.Insert(I, Attr); FAttributes.FSortedList.Insert(I, Attr);
FAttributes.FPosList.Add(Attr);
// TODO: rehash properly, same issue as with Node.SetPrefix() // TODO: rehash properly, same issue as with Node.SetPrefix()
Attr.FNSI.QName := FOwnerDocument.FNames.FindOrAdd(DOMPChar(qualifiedName), Length(qualifiedName)); Attr.FNSI.QName := FOwnerDocument.FNames.FindOrAdd(DOMPChar(qualifiedName), Length(qualifiedName));
Attr.FNSI.PrefixLen := Word(prefIdx); Attr.FNSI.PrefixLen := Word(prefIdx);
@ -3086,8 +3107,9 @@ function TDOMElement.RemoveAttributeNode(OldAttr: TDOMAttr): TDOMAttr;
begin begin
Changing; Changing;
Result:=OldAttr; Result:=OldAttr;
if Assigned(FAttributes) and (FAttributes.FList.Remove(OldAttr) > -1) then if Assigned(FAttributes) and (FAttributes.FSortedList.Remove(OldAttr) > -1) then
begin begin
FAttributes.FPosList.Remove(OldAttr);
if Assigned(OldAttr.FNSI.QName) then // safeguard if Assigned(OldAttr.FNSI.QName) then // safeguard
FAttributes.RestoreDefault(OldAttr.FNSI.QName^.Key); FAttributes.RestoreDefault(OldAttr.FNSI.QName^.Key);
Result.FOwnerElement := nil; Result.FOwnerElement := nil;