{ BEWARE !!! This is a TEMPORARY file. As soon as it is moved to the fcl, it will be removed. } { $Id$ This file is part of the Free Component Library Implementation of DOM interfaces Copyright (c) 1999-2000 by Sebastian Guenther, sg@freepascal.org See the file COPYING.modifiedLGPL.txt, included in this distribution, for details about the copyright. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. **********************************************************************} { This unit provides classes which implement the interfaces defined in the DOM (Document Object Model) specification. The current state is: DOM Level 1 - Almost completely implemented DOM Level 2 - Partially implemented Specification used for this implementation: "Document Object Model (DOM) Level 2 Specification Version 1.0 W3C Candidate Recommendation 07 March, 2000" http://www.w3.org/TR/2000/CR-DOM-Level-2-20000307 } unit Laz_DOM; {$MODE objfpc} {$H+} interface {off $DEFINE MEM_CHECK} uses {$IFDEF MEM_CHECK}MemCheck,{$ENDIF} SysUtils, Classes, Avl_Tree; type TDOMImplementation = class; TDOMDocumentFragment = class; TDOMDocument = class; TDOMNode = class; TDOMNodeList = class; TDOMNamedNodeMap = class; TDOMCharacterData = class; TDOMAttr = class; TDOMElement = class; TDOMText = class; TDOMComment = class; TDOMCDATASection = class; TDOMDocumentType = class; TDOMNotation = class; TDOMEntity = class; TDOMEntityReference = class; TDOMProcessingInstruction = class; // ------------------------------------------------------- // DOMString // ------------------------------------------------------- DOMString = String; DOMPChar = PChar; // DOMString = WideString; // DOMPChar = PWideChar; // ------------------------------------------------------- // DOMException // ------------------------------------------------------- const // DOM Level 1 exception codes: INDEX_SIZE_ERR = 1; // index or size is negative, or greater than the allowed value DOMSTRING_SIZE_ERR = 2; // Specified range of text does not fit into a DOMString HIERARCHY_REQUEST_ERR = 3; // node is inserted somewhere it does not belong WRONG_DOCUMENT_ERR = 4; // node is used in a different document than the one that created it (that does not support it) INVALID_CHARACTER_ERR = 5; // invalid or illegal character is specified, such as in a name NO_DATA_ALLOWED_ERR = 6; // data is specified for a node which does not support data NO_MODIFICATION_ALLOWED_ERR = 7; // an attempt is made to modify an object where modifications are not allowed NOT_FOUND_ERR = 8; // an attempt is made to reference a node in a context where it does not exist NOT_SUPPORTED_ERR = 9; // implementation does not support the type of object requested INUSE_ATTRIBUTE_ERR = 10; // an attempt is made to add an attribute that is already in use elsewhere // DOM Level 2 exception codes: INVALID_STATE_ERR = 11; // an attempt is made to use an object that is not, or is no longer, usable SYNTAX_ERR = 12; // invalid or illegal string specified INVALID_MODIFICATION_ERR = 13; // an attempt is made to modify the type of the underlying object NAMESPACE_ERR = 14; // an attempt is made to create or change an object in a way which is incorrect with regard to namespaces INVALID_ACCESS_ERR = 15; // parameter or operation is not supported by the underlying object type EDOMError = class(Exception) public Code: Integer; constructor Create(ACode: Integer; const ASituation: String); end; EDOMIndexSize = class(EDOMError) public constructor Create(const ASituation: String); end; EDOMHierarchyRequest = class(EDOMError) public constructor Create(const ASituation: String); end; EDOMWrongDocument = class(EDOMError) public constructor Create(const ASituation: String); end; EDOMNotFound = class(EDOMError) public constructor Create(const ASituation: String); end; EDOMNotSupported = class(EDOMError) public constructor Create(const ASituation: String); end; EDOMInUseAttribute = class(EDOMError) public constructor Create(const ASituation: String); end; EDOMInvalidState = class(EDOMError) public constructor Create(const ASituation: String); end; EDOMSyntax = class(EDOMError) public constructor Create(const ASituation: String); end; EDOMInvalidModification = class(EDOMError) public constructor Create(const ASituation: String); end; EDOMNamespace = class(EDOMError) public constructor Create(const ASituation: String); end; EDOMInvalidAccess = class(EDOMError) public constructor Create(const ASituation: String); end; // ------------------------------------------------------- // Node // ------------------------------------------------------- const ELEMENT_NODE = 1; ATTRIBUTE_NODE = 2; TEXT_NODE = 3; CDATA_SECTION_NODE = 4; ENTITY_REFERENCE_NODE = 5; ENTITY_NODE = 6; PROCESSING_INSTRUCTION_NODE = 7; COMMENT_NODE = 8; DOCUMENT_NODE = 9; DOCUMENT_TYPE_NODE = 10; DOCUMENT_FRAGMENT_NODE = 11; NOTATION_NODE = 12; type TRefClass = class protected RefCounter: LongInt; public constructor Create; function AddRef: LongInt; virtual; function Release: LongInt; virtual; end; { TDOMNode } TDOMNode = class protected FNodeName, FNodeValue: DOMString; FNodeType: Integer; FParentNode: TDOMNode; FPreviousSibling, FNextSibling: TDOMNode; FOwnerDocument: TDOMDocument; function GetNodeValue: DOMString; virtual; procedure SetNodeValue(const AValue: DOMString); virtual; function GetFirstChild: TDOMNode; virtual; function GetLastChild: TDOMNode; virtual; function GetAttributes: TDOMNamedNodeMap; virtual; public constructor Create(AOwner: TDOMDocument); // Free NodeList with TDOMNodeList.Release! function GetChildNodes: TDOMNodeList; virtual; property NodeName: DOMString read FNodeName; property NodeValue: DOMString read GetNodeValue write SetNodeValue; property NodeType: Integer read FNodeType; property ParentNode: TDOMNode read FParentNode; property FirstChild: TDOMNode read GetFirstChild; property LastChild: TDOMNode read GetLastChild; property ChildNodes: TDOMNodeList read GetChildNodes; property PreviousSibling: TDOMNode read FPreviousSibling; property NextSibling: TDOMNode read FNextSibling; property Attributes: TDOMNamedNodeMap read GetAttributes; property OwnerDocument: TDOMDocument read FOwnerDocument; function InsertBefore(NewChild, RefChild: TDOMNode): TDOMNode; virtual; function ReplaceChild(NewChild, OldChild: TDOMNode): TDOMNode; virtual; function RemoveChild(OldChild: TDOMNode): TDOMNode; virtual; function AppendChild(NewChild: TDOMNode): TDOMNode; virtual; function HasChildNodes: Boolean; virtual; function CloneNode(deep: Boolean): TDOMNode; overload; function IsEmpty: Boolean; virtual; // Extensions to DOM interface: function CloneNode(deep: Boolean; ACloneOwner: TDOMDocument): TDOMNode; overload; virtual; function FindNode(const ANodeName: DOMString): TDOMNode; virtual; end; { The following class is an implementation specific extension, it is just an extended implementation of TDOMNode, the generic DOM::Node interface implementation. (Its main purpose is to save memory in a big node tree) } TDOMNode_WithChildren = class(TDOMNode) protected FFirstChild, FLastChild: TDOMNode; FChildNodeTree: TAVLTree;// tree of TDOMNode sorted for Name (=> there can be doubles) function GetFirstChild: TDOMNode; override; function GetLastChild: TDOMNode; override; procedure CloneChildren(ACopy: TDOMNode; ACloneOwner: TDOMDocument); procedure AddToChildNodeTree(NewNode: TDOMNode); procedure RemoveFromChildNodeTree(OldNode: TDOMNode); public destructor Destroy; override; function InsertBefore(NewChild, RefChild: TDOMNode): TDOMNode; override; function ReplaceChild(NewChild, OldChild: TDOMNode): TDOMNode; override; function RemoveChild(OldChild: TDOMNode): TDOMNode; override; function AppendChild(NewChild: TDOMNode): TDOMNode; override; function HasChildNodes: Boolean; override; function FindNode(const ANodeName: DOMString): TDOMNode; override; end; // ------------------------------------------------------- // NodeList // ------------------------------------------------------- TDOMNodeList = class(TRefClass) protected node: TDOMNode; filter: DOMString; UseFilter: Boolean; function GetCount: LongInt; function GetItem(index: LongWord): TDOMNode; public constructor Create(ANode: TDOMNode; const AFilter: DOMString); property Item[index: LongWord]: TDOMNode read GetItem; property Count: LongInt read GetCount; end; // ------------------------------------------------------- // NamedNodeMap // ------------------------------------------------------- TDOMNamedNodeMap = class(TFPList) protected OwnerDocument: TDOMDocument; function GetItem(index: LongWord): TDOMNode; procedure SetItem(index: LongWord; AItem: TDOMNode); function GetLength: LongInt; public constructor Create(AOwner: TDOMDocument); function GetNamedItem(const name: DOMString): TDOMNode; function SetNamedItem(arg: TDOMNode): TDOMNode; function RemoveNamedItem(const name: DOMString): TDOMNode; property Item[index: LongWord]: TDOMNode read GetItem write SetItem; default; property Length: LongInt read GetLength; end; // ------------------------------------------------------- // CharacterData // ------------------------------------------------------- TDOMCharacterData = class(TDOMNode) protected function GetLength: LongInt; public property Data: DOMString read FNodeValue; property Length: LongInt read GetLength; function SubstringData(offset, count: LongWord): DOMString; procedure AppendData(const arg: DOMString); procedure InsertData(offset: LongWord; const arg: DOMString); procedure DeleteData(offset, count: LongWord); procedure ReplaceData(offset, count: LongWord; const arg: DOMString); end; // ------------------------------------------------------- // DOMImplementation // ------------------------------------------------------- TDOMImplementation = class public function HasFeature(const feature, version: DOMString): Boolean; // Introduced in DOM Level 2: function CreateDocumentType(const QualifiedName, PublicID, SystemID: DOMString): TDOMDocumentType; function CreateDocument(const NamespaceURI, QualifiedName: DOMString; doctype: TDOMDocumentType): TDOMDocument; end; // ------------------------------------------------------- // DocumentFragment // ------------------------------------------------------- TDOMDocumentFragment = class(TDOMNode_WithChildren) public constructor Create(AOwner: TDOMDocument); end; // ------------------------------------------------------- // Document // ------------------------------------------------------- TDOMDocument = class(TDOMNode_WithChildren) protected FDocType: TDOMDocumentType; FImplementation: TDOMImplementation; function GetDocumentElement: TDOMElement; public property DocType: TDOMDocumentType read FDocType; property Impl: TDOMImplementation read FImplementation; property DocumentElement: TDOMElement read GetDocumentElement; function CreateElement(const tagName: DOMString): TDOMElement; virtual; function CreateDocumentFragment: TDOMDocumentFragment; function CreateTextNode(const data: DOMString): TDOMText; function CreateComment(const data: DOMString): TDOMComment; function CreateCDATASection(const data: DOMString): TDOMCDATASection; virtual; function CreateProcessingInstruction(const target, data: DOMString): TDOMProcessingInstruction; virtual; function CreateAttribute(const name: DOMString): TDOMAttr; virtual; function CreateEntityReference(const name: DOMString): TDOMEntityReference; virtual; // Free NodeList with TDOMNodeList.Release! function GetElementsByTagName(const tagname: DOMString): TDOMNodeList; // Extensions to DOM interface: constructor Create; function CreateEntity(const data: DOMString): TDOMEntity; end; TXMLDocument = class(TDOMDocument) public // These fields are extensions to the DOM interface: XMLVersion, Encoding, StylesheetType, StylesheetHRef: DOMString; function CreateCDATASection(const data: DOMString): TDOMCDATASection; override; function CreateProcessingInstruction(const target, data: DOMString): TDOMProcessingInstruction; override; function CreateEntityReference(const name: DOMString): TDOMEntityReference; override; end; // ------------------------------------------------------- // Attr // ------------------------------------------------------- TDOMAttr = class(TDOMNode_WithChildren) protected FSpecified: Boolean; AttrOwner: TDOMNamedNodeMap; function GetNodeValue: DOMString; override; procedure SetNodeValue(const AValue: DOMString); override; public constructor Create(AOwner: TDOMDocument); function CloneNode(deep: Boolean; ACloneOwner: TDOMDocument): TDOMNode; overload; override; property Name: DOMString read FNodeName; property Specified: Boolean read FSpecified; property Value: DOMString read GetNodeValue write SetNodeValue; end; // ------------------------------------------------------- // Element // ------------------------------------------------------- { TDOMElement } TDOMElement = class(TDOMNode_WithChildren) private FAttributes: TDOMNamedNodeMap; protected function GetAttributes: TDOMNamedNodeMap; override; public constructor Create(AOwner: TDOMDocument); destructor Destroy; override; function CloneNode(deep: Boolean; ACloneOwner: TDOMDocument): TDOMNode; overload; override; property TagName: DOMString read FNodeName; function GetAttribute(const name: DOMString): DOMString; procedure SetAttribute(const name, value: DOMString); procedure RemoveAttribute(const name: DOMString); function GetAttributeNode(const name: DOMString): TDOMAttr; procedure SetAttributeNode(NewAttr: TDOMAttr); function RemoveAttributeNode(OldAttr: TDOMAttr): TDOMAttr; // Free NodeList with TDOMNodeList.Release! function GetElementsByTagName(const name: DOMString): TDOMNodeList; function IsEmpty: Boolean; override; procedure Normalize; property AttribStrings[const Name: DOMString]: DOMString read GetAttribute write SetAttribute; default; end; // ------------------------------------------------------- // Text // ------------------------------------------------------- TDOMText = class(TDOMCharacterData) public constructor Create(AOwner: TDOMDocument); function CloneNode(deep: Boolean; ACloneOwner: TDOMDocument): TDOMNode; overload; override; function SplitText(offset: LongWord): TDOMText; end; // ------------------------------------------------------- // Comment // ------------------------------------------------------- TDOMComment = class(TDOMCharacterData) public constructor Create(AOwner: TDOMDocument); function CloneNode(deep: Boolean; ACloneOwner: TDOMDocument): TDOMNode; overload; override; end; // ------------------------------------------------------- // CDATASection // ------------------------------------------------------- TDOMCDATASection = class(TDOMText) public constructor Create(AOwner: TDOMDocument); function CloneNode(deep: Boolean; ACloneOwner: TDOMDocument): TDOMNode; overload; override; end; // ------------------------------------------------------- // DocumentType // ------------------------------------------------------- TDOMDocumentType = class(TDOMNode) protected FEntities, FNotations: TDOMNamedNodeMap; public constructor Create(AOwner: TDOMDocument); function CloneNode(deep: Boolean; ACloneOwner: TDOMDocument): TDOMNode; overload; override; property Name: DOMString read FNodeName; property Entities: TDOMNamedNodeMap read FEntities; property Notations: TDOMNamedNodeMap read FEntities; end; // ------------------------------------------------------- // Notation // ------------------------------------------------------- TDOMNotation = class(TDOMNode) protected FPublicID, FSystemID: DOMString; public constructor Create(AOwner: TDOMDocument); function CloneNode(deep: Boolean; ACloneOwner: TDOMDocument): TDOMNode; overload; override; property PublicID: DOMString read FPublicID; property SystemID: DOMString read FSystemID; end; // ------------------------------------------------------- // Entity // ------------------------------------------------------- TDOMEntity = class(TDOMNode_WithChildren) protected FPublicID, FSystemID, FNotationName: DOMString; public constructor Create(AOwner: TDOMDocument); property PublicID: DOMString read FPublicID; property SystemID: DOMString read FSystemID; property NotationName: DOMString read FNotationName; end; // ------------------------------------------------------- // EntityReference // ------------------------------------------------------- TDOMEntityReference = class(TDOMNode_WithChildren) public constructor Create(AOwner: TDOMDocument); end; // ------------------------------------------------------- // ProcessingInstruction // ------------------------------------------------------- TDOMProcessingInstruction = class(TDOMNode) public constructor Create(AOwner: TDOMDocument); property Target: DOMString read FNodeName; property Data: DOMString read FNodeValue; end; // ======================================================= // ======================================================= implementation constructor TRefClass.Create; begin inherited Create; RefCounter := 1; end; function TRefClass.AddRef: LongInt; begin Inc(RefCounter); Result := RefCounter; end; function TRefClass.Release: LongInt; begin Dec(RefCounter); Result := RefCounter; if RefCounter <= 0 then Free; end; // ------------------------------------------------------- // DOM Exception // ------------------------------------------------------- constructor EDOMError.Create(ACode: Integer; const ASituation: String); begin Code := ACode; inherited Create(Self.ClassName + ' in ' + ASituation); end; constructor EDOMIndexSize.Create(const ASituation: String); // 1 begin inherited Create(INDEX_SIZE_ERR, ASituation); end; constructor EDOMHierarchyRequest.Create(const ASituation: String); // 3 begin inherited Create(HIERARCHY_REQUEST_ERR, ASituation); end; constructor EDOMWrongDocument.Create(const ASituation: String); // 4 begin inherited Create(WRONG_DOCUMENT_ERR, ASituation); end; constructor EDOMNotFound.Create(const ASituation: String); // 8 begin inherited Create(NOT_FOUND_ERR, ASituation); end; constructor EDOMNotSupported.Create(const ASituation: String); // 9 begin inherited Create(NOT_SUPPORTED_ERR, ASituation); end; constructor EDOMInUseAttribute.Create(const ASituation: String); // 10 begin inherited Create(INUSE_ATTRIBUTE_ERR, ASituation); end; constructor EDOMInvalidState.Create(const ASituation: String); // 11 begin inherited Create(INVALID_STATE_ERR, ASituation); end; constructor EDOMSyntax.Create(const ASituation: String); // 12 begin inherited Create(SYNTAX_ERR, ASituation); end; constructor EDOMInvalidModification.Create(const ASituation: String); // 13 begin inherited Create(INVALID_MODIFICATION_ERR, ASituation); end; constructor EDOMNamespace.Create(const ASituation: String); // 14 begin inherited Create(NAMESPACE_ERR, ASituation); end; constructor EDOMInvalidAccess.Create(const ASituation: String); // 15 begin inherited Create(INVALID_ACCESS_ERR, ASituation); end; // ------------------------------------------------------- // Node // ------------------------------------------------------- constructor TDOMNode.Create(AOwner: TDOMDocument); begin FOwnerDocument := AOwner; inherited Create; end; function TDOMNode.GetNodeValue: DOMString; begin Result := FNodeValue; end; procedure TDOMNode.SetNodeValue(const AValue: DOMString); begin FNodeValue := AValue; end; function TDOMNode.GetChildNodes: TDOMNodeList; begin Result := TDOMNodeList.Create(Self, '*'); end; function TDOMNode.GetFirstChild: TDOMNode; begin Result := nil; end; function TDOMNode.GetLastChild: TDOMNode; begin Result := nil; end; function TDOMNode.GetAttributes: TDOMNamedNodeMap; begin Result := nil; end; function TDOMNode.InsertBefore(NewChild, RefChild: TDOMNode): TDOMNode; begin raise EDOMHierarchyRequest.Create('Node.InsertBefore'); if (NewChild=nil) and (RefChild=nil) then ; Result:=nil; end; function TDOMNode.ReplaceChild(NewChild, OldChild: TDOMNode): TDOMNode; begin raise EDOMHierarchyRequest.Create('Node.ReplaceChild'); if (NewChild=nil) and (OldChild=nil) then ; Result:=nil; end; function TDOMNode.RemoveChild(OldChild: TDOMNode): TDOMNode; begin raise EDOMHierarchyRequest.Create('Node.RemoveChild'); if (OldChild=nil) then ; Result:=nil; end; function TDOMNode.AppendChild(NewChild: TDOMNode): TDOMNode; begin raise EDOMHierarchyRequest.Create('Node.AppendChild'); if (NewChild=nil) then ; Result:=nil; end; function TDOMNode.HasChildNodes: Boolean; begin Result := False; end; function TDOMNode.CloneNode(deep: Boolean): TDOMNode; begin if deep then ; Result:=CloneNode(deep, FOwnerDocument); end; function TDOMNode.IsEmpty: Boolean; begin Result:=true; end; function TDOMNode.CloneNode(deep: Boolean; ACloneOwner: TDOMDocument): TDOMNode; begin raise EDOMNotSupported.Create('CloneNode not implemented for ' + ClassName); if (deep) and (ACloneOwner=nil) then ; Result:=nil; end; function TDOMNode.FindNode(const ANodeName: DOMString): TDOMNode; var child: TDOMNode; begin child := FirstChild; while Assigned(child) do begin if child.NodeName = ANodeName then begin Result := child; exit; end; child := child.NextSibling; end; Result := nil; end; //------------------------------------------------------------------------------ function CompareDOMStrings(const s1, s2: DOMPChar; l1, l2: integer): integer; var i: integer; begin Result:=l1-l2; i:=0; while (inil then begin FChildNodeTree.Free; FChildNodeTree:=nil; end; child := FirstChild; while Assigned(child) do begin next := child.NextSibling; child.Free; child := next; end; inherited Destroy; end; function TDOMNode_WithChildren.InsertBefore(NewChild, RefChild: TDOMNode): TDOMNode; begin Result := NewChild; if not Assigned(RefChild) then begin AppendChild(NewChild); exit; end; if NewChild.FOwnerDocument <> FOwnerDocument then raise EDOMWrongDocument.Create('NodeWC.InsertBefore'); if RefChild.ParentNode <> Self then raise EDOMHierarchyRequest.Create('NodeWC.InsertBefore'); if NewChild.NodeType = DOCUMENT_FRAGMENT_NODE then raise EDOMNotSupported.Create('NodeWC.InsertBefore for DocumentFragment'); NewChild.FNextSibling := RefChild; if RefChild = FFirstChild then FFirstChild := NewChild else begin RefChild.FPreviousSibling.FNextSibling := NewChild; NewChild.FPreviousSibling := RefChild.FPreviousSibling; end; RefChild.FPreviousSibling := NewChild; NewChild.FParentNode := Self; AddToChildNodeTree(NewChild); end; function TDOMNode_WithChildren.ReplaceChild(NewChild, OldChild: TDOMNode): TDOMNode; begin InsertBefore(NewChild, OldChild); if Assigned(OldChild) then RemoveChild(OldChild); Result := NewChild; end; function TDOMNode_WithChildren.RemoveChild(OldChild: TDOMNode): TDOMNode; begin if OldChild.ParentNode <> Self then raise EDOMHierarchyRequest.Create('NodeWC.RemoveChild'); if OldChild = FFirstChild then FFirstChild := FFirstChild.NextSibling else OldChild.FPreviousSibling.FNextSibling := OldChild.FNextSibling; if OldChild = FLastChild then FLastChild := FLastChild.FPreviousSibling else OldChild.FNextSibling.FPreviousSibling := OldChild.FPreviousSibling; RemoveFromChildNodeTree(OldChild); OldChild.Free; Result:=nil; end; function TDOMNode_WithChildren.AppendChild(NewChild: TDOMNode): TDOMNode; var Parent: TDOMNode; begin //writeln('TDOMNode_WithChildren.AppendChild ',NodeName,' NewChild=',NewChild.NodeName); if NewChild.FOwnerDocument <> FOwnerDocument then raise EDOMWrongDocument.Create('NodeWC.AppendChild'); Parent := Self; while Assigned(Parent) do begin if Parent = NewChild then raise EDOMHierarchyRequest.Create('NodeWC.AppendChild (cycle in tree)'); Parent := Parent.ParentNode; end; if NewChild.FParentNode<>nil then begin //writeln('TDOMNode_WithChildren.AppendChild old NewChild.FParentNode=',NewChild.FParentNode.NodeName); NewChild.FParentNode.RemoveChild(NewChild); end; if NewChild.NodeType = DOCUMENT_FRAGMENT_NODE then raise EDOMNotSupported.Create('NodeWC.AppendChild for DocumentFragments') else begin if Assigned(FFirstChild) then begin FLastChild.FNextSibling := NewChild; NewChild.FPreviousSibling := FLastChild; end else FFirstChild := NewChild; FLastChild := NewChild; NewChild.FParentNode := Self; end; AddToChildNodeTree(NewChild); Result := NewChild; end; function TDOMNode_WithChildren.HasChildNodes: Boolean; begin Result := Assigned(FFirstChild); end; function TDOMNode_WithChildren.FindNode(const ANodeName: DOMString): TDOMNode; var AVLNode: TAVLTreeNode; begin Result:=nil; if FChildNodeTree<>nil then begin // use tree for fast search //if FChildNodeTree.ConsistencyCheck<>0 then // raise exception.Create('TDOMNode_WithChildren.FindNode'); AVLNode:=FChildNodeTree.FindKey(DOMPChar(Pointer(ANodeName)), @CompareDOMStringWithDOMNode); if AVLNode<>nil then Result:=TDOMNode(AVLNode.Data); end else begin // search in list Result := FirstChild; while Assigned(Result) do begin if CompareDOMStringWithDOMNode(DOMPChar(Pointer(ANodeName)),Result)=0 then exit; Result := Result.NextSibling; end; end; end; procedure TDOMNode_WithChildren.CloneChildren(ACopy: TDOMNode; ACloneOwner: TDOMDocument); var node: TDOMNode; begin node := FirstChild; while Assigned(node) do begin ACopy.AppendChild(node.CloneNode(True, ACloneOwner)); node := node.NextSibling; end; end; procedure TDOMNode_WithChildren.AddToChildNodeTree(NewNode: TDOMNode); var ChildCount: Integer; ANode: TDOMNode; NewNodeAdded: Boolean; begin if (FChildNodeTree=nil) then begin // there is no childnodetree yet // Most xml trees contains nodes with only a few child nodes. It would be // overhead to create a tree for only a few childs. ChildCount := 0; ANode := FirstChild; while Assigned(ANode) do begin inc(ChildCount); ANode := ANode.NextSibling; end; if ChildCount>5 then begin FChildNodeTree:=TAVLTree.Create(@CompareDOMNodeWithDOMNode); // add all existing childs ANode := FirstChild; NewNodeAdded:=false; while Assigned(ANode) do begin if ANode=NewNode then NewNodeAdded:=true; FChildNodeTree.Add(ANode); ANode := ANode.NextSibling; end; if not NewNodeAdded then FChildNodeTree.Add(NewNode); end; end else begin {if (FChildNodeTree.Find(NewNode)<>nil) then begin writeln('TDOMNode_WithChildren.AddToChildNodeTree adding same value ',NewNOde.NodeName); CTDumpStack; end;} FChildNodeTree.Add(NewNode); end; //if FChildNodeTree.ConsistencyCheck<>0 then // raise exception.Create('TDOMNode_WithChildren.FindNode'); end; procedure TDOMNode_WithChildren.RemoveFromChildNodeTree(OldNode: TDOMNode); begin if FChildNodeTree<>nil then FChildNodeTree.RemovePointer(OldNode);// doubles are allowed, so Remove can not be used //if (FChildNodeTree<>nil) and (FChildNodeTree.ConsistencyCheck<>0) then // raise exception.Create('TDOMNode_WithChildren.FindNode'); end; // ------------------------------------------------------- // NodeList // ------------------------------------------------------- constructor TDOMNodeList.Create(ANode: TDOMNode; const AFilter: DOMString); begin inherited Create; node := ANode; filter := AFilter; UseFilter := filter <> '*'; end; function TDOMNodeList.GetCount: LongInt; var child: TDOMNode; begin Result := 0; child := node.FirstChild; while Assigned(child) do begin if (not UseFilter) or (child.NodeName = filter) then Inc(Result); child := child.NextSibling; end; end; function TDOMNodeList.GetItem(index: LongWord): TDOMNode; var child: TDOMNode; begin Result := nil; child := node.FirstChild; while Assigned(child) do begin if index = 0 then begin Result := child; break; end; if (not UseFilter) or (child.NodeName = filter) then Dec(index); child := child.NextSibling; end; end; // ------------------------------------------------------- // NamedNodeMap // ------------------------------------------------------- constructor TDOMNamedNodeMap.Create(AOwner: TDOMDocument); begin inherited Create; OwnerDocument := AOwner; end; function TDOMNamedNodeMap.GetItem(index: LongWord): TDOMNode; begin Result := TDOMNode(Items[index]); end; procedure TDOMNamedNodeMap.SetItem(index: LongWord; AItem: TDOMNode); begin Items[index] := AItem; end; function TDOMNamedNodeMap.GetLength: LongInt; begin Result := Count; end; function TDOMNamedNodeMap.GetNamedItem(const name: DOMString): TDOMNode; var i: Integer; begin for i := 0 to Count - 1 do begin Result := Item[i]; if Result.NodeName = name then exit; end; Result := nil; end; function TDOMNamedNodeMap.SetNamedItem(arg: TDOMNode): TDOMNode; var i: Integer; begin if arg.FOwnerDocument <> OwnerDocument then raise EDOMWrongDocument.Create('NamedNodeMap.SetNamedItem'); if arg.NodeType = ATTRIBUTE_NODE then begin if Assigned(TDOMAttr(arg).AttrOwner) then raise EDOMInUseAttribute.Create('NamedNodeMap.SetNamedItem'); TDOMAttr(arg).AttrOwner := Self; end; for i := 0 to Count - 1 do if Item[i].NodeName = arg.NodeName then begin Result := Item[i]; Item[i] := arg; exit; end; Add(arg); Result := nil; end; function TDOMNamedNodeMap.RemoveNamedItem(const name: DOMString): TDOMNode; var i: Integer; begin for i := 0 to Count - 1 do if Item[i].NodeName = name then begin Result := Item[i]; Result.FParentNode := nil; exit; end; raise EDOMNotFound.Create('NamedNodeMap.RemoveNamedItem'); end; // ------------------------------------------------------- // CharacterData // ------------------------------------------------------- function TDOMCharacterData.GetLength: LongInt; begin Result := system.Length(FNodeValue); end; function TDOMCharacterData.SubstringData(offset, count: LongWord): DOMString; begin if (offset < 0) or (longint(offset) > Length) or (count < 0) then raise EDOMIndexSize.Create('CharacterData.SubstringData'); Result := Copy(FNodeValue, offset + 1, count); end; procedure TDOMCharacterData.AppendData(const arg: DOMString); begin FNodeValue := FNodeValue + arg; end; procedure TDOMCharacterData.InsertData(offset: LongWord; const arg: DOMString); begin if (offset < 0) or (longint(offset) > Length) then raise EDOMIndexSize.Create('CharacterData.InsertData'); FNodeValue := Copy(FNodeValue, 1, offset) + arg + Copy(FNodeValue, offset + 1, Length); end; procedure TDOMCharacterData.DeleteData(offset, count: LongWord); begin if (offset < 0) or (longint(offset) > Length) or (count < 0) then raise EDOMIndexSize.Create('CharacterData.DeleteData'); FNodeValue := Copy(FNodeValue, 1, offset) + Copy(FNodeValue, offset + count + 1, Length); end; procedure TDOMCharacterData.ReplaceData(offset, count: LongWord; const arg: DOMString); begin DeleteData(offset, count); InsertData(offset, arg); end; // ------------------------------------------------------- // DocumentFragmet // ------------------------------------------------------- constructor TDOMDocumentFragment.Create(AOwner: TDOMDocument); begin FNodeType := DOCUMENT_FRAGMENT_NODE; FNodeName := '#document-fragment'; inherited Create(AOwner); end; // ------------------------------------------------------- // DOMImplementation // ------------------------------------------------------- function TDOMImplementation.HasFeature(const feature, version: DOMString): Boolean; begin Result := False; if (feature='') and (version='') then ; end; function TDOMImplementation.CreateDocumentType(const QualifiedName, PublicID, SystemID: DOMString): TDOMDocumentType; begin // !!!: Implement this method (easy to do) raise EDOMNotSupported.Create('DOMImplementation.CreateDocumentType'); if (QualifiedName='') and (PublicID='') and (SystemID='') then ; Result:=nil; end; function TDOMImplementation.CreateDocument(const NamespaceURI, QualifiedName: DOMString; doctype: TDOMDocumentType): TDOMDocument; begin // !!!: Implement this method (easy to do) raise EDOMNotSupported.Create('DOMImplementation.CreateDocument'); if (NamespaceURI='') and (QualifiedName='') and (doctype=nil) then ; Result:=nil; end; // ------------------------------------------------------- // Document // ------------------------------------------------------- constructor TDOMDocument.Create; begin FNodeType := DOCUMENT_NODE; FNodeName := '#document'; inherited Create(nil); FOwnerDocument := Self; end; function TDOMDocument.GetDocumentElement: TDOMElement; var node: TDOMNode; begin node := FFirstChild; while Assigned(node) do begin if node.FNodeType = ELEMENT_NODE then begin Result := TDOMElement(node); exit; end; node := node.NextSibling; end; Result := nil; end; function TDOMDocument.CreateElement(const tagName: DOMString): TDOMElement; begin Result := TDOMElement.Create(Self); Result.FNodeName := tagName; end; function TDOMDocument.CreateDocumentFragment: TDOMDocumentFragment; begin Result := TDOMDocumentFragment.Create(Self); end; function TDOMDocument.CreateTextNode(const data: DOMString): TDOMText; begin Result := TDOMText.Create(Self); Result.FNodeValue := data; end; function TDOMDocument.CreateComment(const data: DOMString): TDOMComment; begin Result := TDOMComment.Create(Self); Result.FNodeValue := data; end; function TDOMDocument.CreateCDATASection(const data: DOMString): TDOMCDATASection; begin raise EDOMNotSupported.Create('DOMDocument.CreateCDATASection'); if data='' then ; Result:=nil; end; function TDOMDocument.CreateProcessingInstruction(const target, data: DOMString): TDOMProcessingInstruction; begin raise EDOMNotSupported.Create('DOMDocument.CreateProcessingInstruction'); if (target='') and (data='') then ; Result:=nil; end; function TDOMDocument.CreateAttribute(const name: DOMString): TDOMAttr; begin Result := TDOMAttr.Create(Self); Result.FNodeName := name; end; function TDOMDocument.CreateEntityReference(const name: DOMString): TDOMEntityReference; begin raise EDOMNotSupported.Create('DOMDocument.CreateEntityReference'); if name='' then ; Result:=nil; end; function TDOMDocument.CreateEntity(const data: DOMString): TDOMEntity; begin Result := TDOMEntity.Create(Self); Result.FNodeName := data; end; function TDOMDocument.GetElementsByTagName(const tagname: DOMString): TDOMNodeList; begin Result := TDOMNodeList.Create(Self, tagname); end; function TXMLDocument.CreateCDATASection(const data: DOMString): TDOMCDATASection; begin Result := TDOMCDATASection.Create(Self); Result.FNodeValue := data; end; function TXMLDocument.CreateProcessingInstruction(const target, data: DOMString): TDOMProcessingInstruction; begin Result := TDOMProcessingInstruction.Create(Self); Result.FNodeName := target; Result.FNodeValue := data; end; function TXMLDocument.CreateEntityReference(const name: DOMString): TDOMEntityReference; begin Result := TDOMEntityReference.Create(Self); Result.FNodeName := name; end; // ------------------------------------------------------- // Attr // ------------------------------------------------------- constructor TDOMAttr.Create(AOwner: TDOMDocument); begin FNodeType := ATTRIBUTE_NODE; inherited Create(AOwner); end; function TDOMAttr.CloneNode(deep: Boolean; ACloneOwner: TDOMDocument): TDOMNode; begin Result := TDOMAttr.Create(ACloneOwner); Result.FNodeName := FNodeName; TDOMAttr(Result).FSpecified := FSpecified; if deep then CloneChildren(Result, ACloneOwner); end; function TDOMAttr.GetNodeValue: DOMString; var child: TDOMNode; begin SetLength(Result, 0); if Assigned(FFirstChild) then begin child := FFirstChild; while Assigned(child) do begin if child.NodeType = ENTITY_REFERENCE_NODE then Result := Result + '&' + child.NodeName + ';' else Result := Result + child.NodeValue; child := child.NextSibling; end; end; end; procedure TDOMAttr.SetNodeValue(const AValue: DOMString); var tn: TDOMText; begin FSpecified := True; tn := TDOMText.Create(FOwnerDocument); tn.FNodeValue := AValue; if Assigned(FFirstChild) then ReplaceChild(tn, FFirstChild) else AppendChild(tn); end; // ------------------------------------------------------- // Element // ------------------------------------------------------- constructor TDOMElement.Create(AOwner: TDOMDocument); begin FNodeType := ELEMENT_NODE; inherited Create(AOwner); end; destructor TDOMElement.Destroy; var i: Integer; begin {As the attributes are _not_ childs of the element node, we have to free them manually here:} if FAttributes<>nil then begin for i := 0 to FAttributes.Count - 1 do FAttributes[i].Free; FAttributes.Free; FAttributes:=nil; end; inherited Destroy; end; function TDOMElement.CloneNode(deep: Boolean; ACloneOwner: TDOMDocument): TDOMNode; var i: Integer; begin Result := TDOMElement.Create(ACloneOwner); Result.FNodeName := FNodeName; if FAttributes<>nil then begin TDOMElement(Result).GetAttributes; for i := 0 to FAttributes.Count - 1 do TDOMElement(Result).FAttributes.Add(FAttributes[i].CloneNode(True, ACloneOwner)); end; if deep then CloneChildren(Result, ACloneOwner); end; function TDOMElement.GetAttributes: TDOMNamedNodeMap; begin if FAttributes=nil then FAttributes := TDOMNamedNodeMap.Create(FOwnerDocument); Result := FAttributes; end; function TDOMElement.GetAttribute(const name: DOMString): DOMString; var i: Integer; begin if FAttributes<>nil then begin for i := 0 to FAttributes.Count - 1 do if FAttributes[i].NodeName = name then begin Result := FAttributes[i].NodeValue; exit; end; end; SetLength(Result, 0); end; procedure TDOMElement.SetAttribute(const name, value: DOMString); var i: Integer; attr: TDOMAttr; begin GetAttributes; for i := 0 to FAttributes.Count - 1 do if FAttributes[i].NodeName = name then begin FAttributes[i].NodeValue := value; exit; end; attr := TDOMAttr.Create(FOwnerDocument); attr.FNodeName := name; attr.NodeValue := value; FAttributes.Add(attr); end; procedure TDOMElement.RemoveAttribute(const name: DOMString); var i: Integer; begin if FAttributes=nil then exit; for i := 0 to FAttributes.Count - 1 do if FAttributes[i].NodeName = name then begin FAttributes[i].Free; FAttributes.Delete(i); exit; end; end; function TDOMElement.GetAttributeNode(const name: DOMString): TDOMAttr; var i: Integer; begin if FAttributes<>nil then begin for i := 0 to FAttributes.Count - 1 do if FAttributes[i].NodeName = name then begin Result := TDOMAttr(FAttributes[i]); exit; end; end; Result := nil; end; procedure TDOMElement.SetAttributeNode(NewAttr: TDOMAttr); var i: Integer; begin if FAttributes=nil then exit; for i := 0 to FAttributes.Count - 1 do if FAttributes[i].NodeName = NewAttr.NodeName then begin FAttributes[i].Free; FAttributes[i] := NewAttr; exit; end; end; function TDOMElement.RemoveAttributeNode(OldAttr: TDOMAttr): TDOMAttr; var i: Integer; node: TDOMNode; begin Result:=nil; if FAttributes=nil then exit; for i := 0 to FAttributes.Count - 1 do begin node := FAttributes[i]; if node = OldAttr then begin FAttributes.Delete(i); Result := TDOMAttr(node); exit; end; end; end; function TDOMElement.GetElementsByTagName(const name: DOMString): TDOMNodeList; begin Result := TDOMNodeList.Create(Self, name); end; function TDOMElement.IsEmpty: Boolean; begin Result:=(FAttributes=nil) or (FAttributes.Count=0) end; procedure TDOMElement.Normalize; begin // !!!: Not implemented end; // ------------------------------------------------------- // Text // ------------------------------------------------------- constructor TDOMText.Create(AOwner: TDOMDocument); begin FNodeType := TEXT_NODE; FNodeName := '#text'; inherited Create(AOwner); end; function TDOMText.CloneNode(deep: Boolean; ACloneOwner: TDOMDocument): TDOMNode; begin Result := TDOMText.Create(ACloneOwner); Result.FNodeValue := FNodeValue; if deep and (ACloneOwner=nil) then ; end; function TDOMText.SplitText(offset: LongWord): TDOMText; begin if longint(offset) > Length then raise EDOMIndexSize.Create('Text.SplitText'); Result := TDOMText.Create(FOwnerDocument); Result.FNodeValue := Copy(FNodeValue, offset + 1, Length); FNodeValue := Copy(FNodeValue, 1, offset); FParentNode.InsertBefore(Result, FNextSibling); end; // ------------------------------------------------------- // Comment // ------------------------------------------------------- constructor TDOMComment.Create(AOwner: TDOMDocument); begin FNodeType := COMMENT_NODE; FNodeName := '#comment'; inherited Create(AOwner); end; function TDOMComment.CloneNode(deep: Boolean; ACloneOwner: TDOMDocument): TDOMNode; begin Result := TDOMComment.Create(ACloneOwner); Result.FNodeValue := FNodeValue; if deep and (ACloneOwner=nil) then ; end; // ------------------------------------------------------- // CDATASection // ------------------------------------------------------- constructor TDOMCDATASection.Create(AOwner: TDOMDocument); begin inherited Create(AOwner); FNodeType := CDATA_SECTION_NODE; FNodeName := '#cdata-section'; end; function TDOMCDATASection.CloneNode(deep: Boolean; ACloneOwner: TDOMDocument): TDOMNode; begin Result := TDOMCDATASection.Create(ACloneOwner); Result.FNodeValue := FNodeValue; if deep and (ACloneOwner=nil) then ; end; // ------------------------------------------------------- // DocumentType // ------------------------------------------------------- constructor TDOMDocumentType.Create(AOwner: TDOMDocument); begin FNodeType := DOCUMENT_TYPE_NODE; inherited Create(AOwner); end; function TDOMDocumentType.CloneNode(deep: Boolean; ACloneOwner: TDOMDocument): TDOMNode; begin Result := TDOMDocumentType.Create(ACloneOwner); Result.FNodeName := FNodeName; if deep and (ACloneOwner=nil) then ; end; // ------------------------------------------------------- // Notation // ------------------------------------------------------- constructor TDOMNotation.Create(AOwner: TDOMDocument); begin FNodeType := NOTATION_NODE; inherited Create(AOwner); end; function TDOMNotation.CloneNode(deep: Boolean; ACloneOwner: TDOMDocument): TDOMNode; begin Result := TDOMNotation.Create(ACloneOwner); Result.FNodeName := FNodeName; if deep and (ACloneOwner=nil) then ; end; // ------------------------------------------------------- // Entity // ------------------------------------------------------- constructor TDOMEntity.Create(AOwner: TDOMDocument); begin FNodeType := ENTITY_NODE; inherited Create(AOwner); end; // ------------------------------------------------------- // EntityReference // ------------------------------------------------------- constructor TDOMEntityReference.Create(AOwner: TDOMDocument); begin FNodeType := ENTITY_REFERENCE_NODE; inherited Create(AOwner); end; // ------------------------------------------------------- // ProcessingInstruction // ------------------------------------------------------- constructor TDOMProcessingInstruction.Create(AOwner: TDOMDocument); begin FNodeType := PROCESSING_INSTRUCTION_NODE; inherited Create(AOwner); end; end.