244 lines
6.3 KiB
Haxe
Raw Permalink Normal View History

2025-01-22 16:18:30 +01:00
/*
* Copyright (C)2005-2019 Haxe Foundation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package haxe.xml;
private abstract NodeAccess(Xml) from Xml {
@:op(a.b)
public function resolve(name:String):Access {
var x = this.elementsNamed(name).next();
if (x == null) {
var xname = if (this.nodeType == Xml.Document) "Document" else this.nodeName;
throw xname + " is missing element " + name;
}
return new Access(x);
}
}
private abstract AttribAccess(Xml) from Xml {
@:op(a.b)
public function resolve(name:String):String {
if (this.nodeType == Xml.Document)
throw "Cannot access document attribute " + name;
var v = this.get(name);
if (v == null)
throw this.nodeName + " is missing attribute " + name;
return v;
}
@:op(a.b)
function _hx_set(name:String, value:String):String {
if (this.nodeType == Xml.Document)
throw "Cannot access document attribute " + name;
this.set(name, value);
return value;
}
}
private abstract HasAttribAccess(Xml) from Xml {
@:op(a.b)
public function resolve(name:String):Bool {
if (this.nodeType == Xml.Document)
throw "Cannot access document attribute " + name;
return this.exists(name);
}
}
private abstract HasNodeAccess(Xml) from Xml {
@:op(a.b)
public function resolve(name:String):Bool {
return this.elementsNamed(name).hasNext();
}
}
private abstract NodeListAccess(Xml) from Xml {
@:op(a.b)
public function resolve(name:String):Array<Access> {
var l = [];
for (x in this.elementsNamed(name))
l.push(new Access(x));
return l;
}
}
/**
The `haxe.xml.Access` API helps providing a fast dot-syntax access to the
most common `Xml` methods.
**/
abstract Access(Xml) {
public var x(get, never):Xml;
public inline function get_x()
return this;
/**
The name of the current element. This is the same as `Xml.nodeName`.
**/
public var name(get, never):String;
inline function get_name() {
return if (this.nodeType == Xml.Document) "Document" else this.nodeName;
}
/**
The inner PCDATA or CDATA of the node.
An exception is thrown if there is no data or if there not only data
but also other nodes.
**/
public var innerData(get, never):String;
/**
The XML string built with all the sub nodes, excluding the current one.
**/
public var innerHTML(get, never):String;
/**
Access to the first sub element with the given name.
An exception is thrown if the element doesn't exists.
Use `hasNode` to check the existence of a node.
```haxe
var access = new haxe.xml.Access(Xml.parse("<user><name>John</name></user>"));
var user = access.node.user;
var name = user.node.name;
trace(name.innerData); // John
// Uncaught Error: Document is missing element password
var password = user.node.password;
```
**/
public var node(get, never):NodeAccess;
inline function get_node():NodeAccess
return x;
/**
Access to the List of elements with the given name.
```haxe
var fast = new haxe.xml.Access(Xml.parse("
<users>
<user name='John'/>
<user name='Andy'/>
<user name='Dan'/>
</users>"
));
var users = fast.node.users;
for (user in users.nodes.user) {
trace(user.att.name);
}
```
**/
public var nodes(get, never):NodeListAccess;
inline function get_nodes():NodeListAccess
return this;
/**
Access to a given attribute.
An exception is thrown if the attribute doesn't exists.
Use `has` to check the existence of an attribute.
```haxe
var f = new haxe.xml.Access(Xml.parse("<user name='Mark'></user>"));
var user = f.node.user;
if (user.has.name) {
trace(user.att.name); // Mark
}
```
**/
public var att(get, never):AttribAccess;
inline function get_att():AttribAccess
return this;
/**
Check the existence of an attribute with the given name.
**/
public var has(get, never):HasAttribAccess;
inline function get_has():HasAttribAccess
return this;
/**
Check the existence of a sub node with the given name.
```haxe
var f = new haxe.xml.Access(Xml.parse("<user><age>31</age></user>"));
var user = f.node.user;
if (user.hasNode.age) {
trace(user.node.age.innerData); // 31
}
```
**/
public var hasNode(get, never):HasNodeAccess;
inline function get_hasNode():HasNodeAccess
return x;
/**
The list of all sub-elements which are the nodes with type `Xml.Element`.
**/
public var elements(get, never):Iterator<Access>;
inline function get_elements():Iterator<Access>
return cast this.elements();
public inline function new(x:Xml) {
if (x.nodeType != Xml.Document && x.nodeType != Xml.Element)
throw "Invalid nodeType " + x.nodeType;
this = x;
}
function get_innerData() {
var it = this.iterator();
if (!it.hasNext())
throw name + " does not have data";
var v = it.next();
if (it.hasNext()) {
var n = it.next();
// handle <spaces>CDATA<spaces>
if (v.nodeType == Xml.PCData && n.nodeType == Xml.CData && StringTools.trim(v.nodeValue) == "") {
if (!it.hasNext())
return n.nodeValue;
var n2 = it.next();
if (n2.nodeType == Xml.PCData && StringTools.trim(n2.nodeValue) == "" && !it.hasNext())
return n.nodeValue;
}
throw name + " does not only have data";
}
if (v.nodeType != Xml.PCData && v.nodeType != Xml.CData)
throw name + " does not have data";
return v.nodeValue;
}
function get_innerHTML() {
var s = new StringBuf();
for (x in this)
s.add(x.toString());
return s.toString();
}
}