import '../xml/extensions/sibling.dart'; import '../xml/nodes/attribute.dart'; import '../xml/nodes/cdata.dart'; import '../xml/nodes/comment.dart'; import '../xml/nodes/document.dart'; import '../xml/nodes/element.dart'; import '../xml/nodes/node.dart'; import '../xml/nodes/processing.dart'; import '../xml/nodes/text.dart'; extension XPathGenerator on XmlNode { /// Returns an XPath string that can be used to query for this [XmlNode]. /// /// If [byId] is giving a fully qualified attribute name, the presence of /// the attribute causes the generation of a shorter lookup expression. String xpathGenerate({String? byId}) { final result = []; for (XmlNode? current = this; current != null; current = current.parent) { switch (current) { case XmlAttribute(qualifiedName: final name): result.add( _createSegment( current, where: (each) => each is XmlAttribute && each.qualifiedName == name, filter: '@$name', ), ); case XmlElement(qualifiedName: final name): if (byId != null) { final attribute = current.getAttributeNode(byId); if (attribute != null) { result.add('//*[@${attribute.toXmlString()}]'); return result.reversed.join('/'); } } result.add( _createSegment( current, where: (each) => each is XmlElement && each.qualifiedName == name, filter: name, ), ); case XmlText(): case XmlCDATA(): result.add( _createSegment( current, where: (each) => each is XmlText || each is XmlCDATA, filter: 'text()', ), ); case XmlComment(): result.add( _createSegment( current, where: (each) => each is XmlComment, filter: 'comment()', ), ); case XmlProcessing(): result.add( _createSegment( current, where: (each) => each is XmlProcessing, filter: 'processing-instruction()', ), ); case XmlDocument(): result.add(this == current ? '/' : ''); default: result.add( _createSegment(current, where: (each) => true, filter: 'node()'), ); } } return result.reversed.join('/'); } } String _createSegment( XmlNode node, { required bool Function(XmlNode) where, required String filter, }) { final buffer = StringBuffer(filter); final siblings = node.hasParent ? node.siblings.where(where).toList() : [node]; if (siblings.length > 1) { buffer.write('[${1 + siblings.indexOf(node)}]'); } return buffer.toString(); }