///Fluent XML builder. /// ///Primoz Gabrijelcic /// /// (c) 2009 Primoz Gabrijelcic /// Free for personal and commercial use. No rights reserved. /// /// Author : Primoz Gabrijelcic /// Creation date : 2009-03-30 /// Last modification : 2009-10-04 /// Version : 0.2 /// /// History: /// 0.2: 2009-10-04 /// - Added node value-setting overloads for AddChild and AddSibling. /// 0.1: 2009-03-30 /// - Created. /// unit GpFluentXml; interface uses OmniXML_Types, OmniXML; type IGpFluentXmlBuilder = interface ['{91F596A3-F5E3-451C-A6B9-C5FF3F23ECCC}'] function GetXml: IXmlDocument; // function AddChild(const name: XmlString): IGpFluentXmlBuilder; overload; function AddChild(const name: XmlString; value: Variant): IGpFluentXmlBuilder; overload; function AddComment(const comment: XmlString): IGpFluentXmlBuilder; function AddProcessingInstruction(const target, data: XmlString): IGpFluentXmlBuilder; function AddSibling(const name: XmlString): IGpFluentXmlBuilder; overload; function AddSibling(const name: XmlString; value: Variant): IGpFluentXmlBuilder; overload; function Anchor(var node: IXMLNode): IGpFluentXmlBuilder; function Mark: IGpFluentXmlBuilder; function Return: IGpFluentXmlBuilder; function SetAttrib(const name, value: XmlString): IGpFluentXmlBuilder; function Up: IGpFluentXmlBuilder; property Attrib[const name, value: XmlString]: IGpFluentXmlBuilder read SetAttrib; default; property Xml: IXmlDocument read GetXml; end; { IGpFluentXmlBuilder } function CreateFluentXml: IGpFluentXmlBuilder; implementation uses SysUtils, Classes, OmniXMLUtils; type TGpFluentXmlBuilder = class(TInterfacedObject, IGpFluentXmlBuilder) private fxbActiveNode : IXMLNode; fxbMarkedNodes: IInterfaceList; fxbXmlDoc : IXMLDocument; protected function ActiveNode: IXMLNode; protected function GetXml: IXmlDocument; public constructor Create; destructor Destroy; override; function AddChild(const name: XmlString): IGpFluentXmlBuilder; overload; function AddChild(const name: XmlString; value: Variant): IGpFluentXmlBuilder; overload; function AddComment(const comment: XmlString): IGpFluentXmlBuilder; function AddProcessingInstruction(const target, data: XmlString): IGpFluentXmlBuilder; function AddSibling(const name: XmlString): IGpFluentXmlBuilder; overload; function AddSibling(const name: XmlString; value: Variant): IGpFluentXmlBuilder; overload; function Anchor(var node: IXMLNode): IGpFluentXmlBuilder; function Mark: IGpFluentXmlBuilder; function Return: IGpFluentXmlBuilder; function SetAttrib(const name, value: XmlString): IGpFluentXmlBuilder; function Up: IGpFluentXmlBuilder; end; { TGpFluentXmlBuilder } { globals } function CreateFluentXml: IGpFluentXmlBuilder; begin Result := TGpFluentXmlBuilder.Create; end; { CreateFluentXml } { TGpFluentXmlBuilder } constructor TGpFluentXmlBuilder.Create; begin inherited Create; fxbXmlDoc := CreateXMLDoc; fxbMarkedNodes := TInterfaceList.Create; end; { TGpFluentXmlBuilder.Create } destructor TGpFluentXmlBuilder.Destroy; begin if fxbMarkedNodes.Count > 0 then raise Exception.Create('''Mark'' stack is not completely empty'); inherited; end; { TGpFluentXmlBuilder.Destroy } function TGpFluentXmlBuilder.ActiveNode: IXMLNode; begin if assigned(fxbActiveNode) then Result := fxbActiveNode else begin Result := DocumentElement(fxbXmlDoc); if not assigned(Result) then Result := fxbXmlDoc; end; end; { TGpFluentXmlBuilder.ActiveNode } function TGpFluentXmlBuilder.AddChild(const name: XmlString): IGpFluentXmlBuilder; begin fxbActiveNode := AppendNode(ActiveNode, name); Result := Self; end; { TGpFluentXmlBuilder.AddChild } function TGpFluentXmlBuilder.AddChild(const name: XmlString; value: Variant): IGpFluentXmlBuilder; begin Result := AddChild(name); SetTextChild(fxbActiveNode, XMLVariantToStr(value)); end; { TGpFluentXmlBuilder.AddChild } function TGpFluentXmlBuilder.AddComment(const comment: XmlString): IGpFluentXmlBuilder; begin ActiveNode.AppendChild(fxbXmlDoc.CreateComment(comment)); Result := Self; end; { TGpFluentXmlBuilder.AddComment } function TGpFluentXmlBuilder.AddProcessingInstruction(const target, data: XmlString): IGpFluentXmlBuilder; begin ActiveNode.AppendChild(fxbXmlDoc.CreateProcessingInstruction(target, data)); Result := Self; end; { TGpFluentXmlBuilder.AddProcessingInstruction } function TGpFluentXmlBuilder.AddSibling(const name: XmlString; value: Variant): IGpFluentXmlBuilder; begin Result := AddSibling(name); SetTextChild(fxbActiveNode, XMLVariantToStr(value)); end; { TGpFluentXmlBuilder.AddSibling } function TGpFluentXmlBuilder.AddSibling(const name: XmlString): IGpFluentXmlBuilder; begin Result := Up; fxbActiveNode := AppendNode(ActiveNode, name); end; { TGpFluentXmlBuilder.AddSibling } function TGpFluentXmlBuilder.Anchor(var node: IXMLNode): IGpFluentXmlBuilder; begin node := fxbActiveNode; Result := Self; end; { TGpFluentXmlBuilder.Anchor } function TGpFluentXmlBuilder.GetXml: IXmlDocument; begin Result := fxbXmlDoc; end; { TGpFluentXmlBuilder.GetXml } function TGpFluentXmlBuilder.Mark: IGpFluentXmlBuilder; begin fxbMarkedNodes.Add(ActiveNode); Result := Self; end; { TGpFluentXmlBuilder.Mark } function TGpFluentXmlBuilder.Return: IGpFluentXmlBuilder; begin fxbActiveNode := fxbMarkedNodes.Last as IXMLNode; fxbMarkedNodes.Delete(fxbMarkedNodes.Count - 1); Result := Self; end; { TGpFluentXmlBuilder.Return } function TGpFluentXmlBuilder.SetAttrib(const name, value: XmlString): IGpFluentXmlBuilder; begin SetNodeAttrStr(ActiveNode, name, value); Result := Self; end; { TGpFluentXmlBuilder.SetAttrib } function TGpFluentXmlBuilder.Up: IGpFluentXmlBuilder; begin if not assigned(fxbActiveNode) then raise Exception.Create('Cannot access a parent at the root level') else if fxbActiveNode = DocumentElement(fxbXmlDoc) then raise Exception.Create('Cannot create a parent at the document element level') else fxbActiveNode := ActiveNode.ParentNode; Result := Self; end; { TGpFluentXmlBuilder.Up } end.