use std::error::Error;
use dom::*;
use xpath_impl::parser::*;
use xpath_impl::eval::*;
use xpath_impl::xitem::*;
use xpath_impl::xsequence::*;
impl NodePtr {
pub fn eval_xpath(&self, xpath: &str) -> Result<Sequence, Box<Error>> {
let xnode = compile_xpath(&String::from(xpath))?;
let result = match_xpath(self, &xnode)?;
return Ok(new_sequence(&result));
}
pub fn get_first_node(&self, xpath: &str) -> Option<NodePtr> {
let node_set_array = match self.get_nodeset(xpath) {
Ok(n) => n,
Err(_) => return None,
};
if node_set_array.len() != 0 {
return Some(node_set_array[0].rc_clone());
} else {
return None;
}
}
pub fn each_node<F>(&self, xpath: &str, mut func: F) -> Result<(), Box<Error>>
where F: FnMut(NodePtr) -> () {
let node_set_array = self.get_nodeset(xpath)?;
for node in node_set_array {
func(node.rc_clone());
}
return Ok(());
}
pub fn get_nodeset(&self, xpath: &str) -> Result<Vec<NodePtr>, Box<Error>> {
let xnode = compile_xpath(&String::from(xpath))?;
let result = match_xpath(self, &xnode)?;
let nodeset = result.to_nodeset();
return Ok(nodeset);
}
}
#[derive(Debug)]
pub struct Sequence {
seq: XSequence,
}
fn new_sequence(xseq: &XSequence) -> Sequence {
return Sequence{seq: xseq.clone()};
}
pub struct Item {
item: XItem,
}
fn new_item(xitem: &XItem) -> Item {
return Item{item: xitem.clone()};
}
impl Sequence {
pub fn to_string(&self) -> String {
return self.seq.to_string();
}
pub fn len(&self) -> usize {
return self.seq.len();
}
pub fn get_item(&self, pos: usize) -> Item {
let xitem = self.seq.get_item(pos);
return new_item(xitem);
}
}
impl Item {
pub fn to_string(&self) -> String {
return self.item.to_string();
}
pub fn as_nodeptr(&self) -> Option<NodePtr> {
return self.item.as_nodeptr();
}
}
#[cfg(test)]
mod test {
use super::*;
use xpath_impl::helpers::compress_spaces;
use xpath_impl::helpers::subtest_xpath;
use xpath_impl::helpers::subtest_eval_xpath;
#[test]
fn test_sample_01() {
let xml = compress_spaces(r#"
<?xml version='1.0' encoding='UTF-8'?>
<root>
<chap base="base">
<para img="春"/>
<div img="夏"/>
<para img="秋"/>
<div img="冬"/>
</chap>
</root>
"#);
subtest_xpath("01", &xml, false, &[
( "child::para", "春秋" ),
( "para", "春秋" ),
( "child::*", "春夏秋冬" ),
( "*", "春夏秋冬" ),
]);
}
#[test]
fn test_sample_02() {
let xml = compress_spaces(r#"
<?xml version='1.0' encoding='UTF-8'?>
<root>
<chap base="base">
<para img="春">はる</para>
なつ
<para img="秋">あき</para>
ふゆ
</chap>
</root>
"#);
subtest_xpath("02", &xml, true, &[
( "child::text()", "なつふゆ" ),
( "text()", "なつふゆ" ),
( "text()[preceding-sibling::*[1]/@img = '春']", "なつ" ),
( "text()[following-sibling::*[1]/@img = '秋']", "なつ" ),
]);
}
#[test]
fn test_sample_03() {
let xml = compress_spaces(r#"
<?xml version='1.0' encoding='UTF-8'?>
<root>
<chap base="base">
<para img="春">はる</para>
なつ
<sub img="秋">あき</sub>
ふゆ
<!--季節-->
</chap>
</root>
"#);
subtest_xpath("03", &xml, true, &[
( "child::node()", "なつふゆ季節" ),
( "node()", "なつふゆ季節" ),
( "text()", "なつふゆ" ),
( "comment()", "季節" ),
]);
subtest_xpath("03", &xml, false, &[
( "child::node()", "春秋" ),
( "node()", "春秋" ),
]);
}
#[test]
fn test_sample_04() {
let xml = compress_spaces(r#"
<?xml version='1.0' encoding='UTF-8'?>
<root>
<chap base="base" name="四季" ruby="しき" ns:e="seasons">
<para img="春">はる</para>
</chap>
</root>
"#);
subtest_xpath("04", &xml, true, &[
( "attribute::name", "四季" ),
( "@name", "四季" ),
( "attribute::*", "base四季しきseasons" ),
( "@*", "base四季しきseasons" ),
( "attribute::ns:e", "seasons" ),
( "@ns:e", "seasons" ),
( "attribute::ns:*", "seasons" ),
( "@ns:*", "seasons" ),
]);
}
#[test]
fn test_sample_05() {
let xml = compress_spaces(r#"
<?xml version='1.0' encoding='UTF-8'?>
<root>
<para base="base" img="四季">
<para img="春">はる</para>
<div img="夏">
<para img="夏" />
<span>
<para img="秋" />
</span>
</div>
</para>
</root>
"#);
subtest_xpath("05", &xml, false, &[
( "descendant::para", "春夏秋" ),
( ".//para", "春夏秋" ),
]);
}
#[test]
fn test_sample_06() {
let xml = compress_spaces(r#"
<?xml version='1.0' encoding='UTF-8'?>
<root>
<div img="四季">
<div img="春">はる</div>
<section img="夏">
<div base="base" img="秋" />
<span>
<div img="冬" />
</span>
</section>
</div>
</root>
"#);
subtest_xpath("06", &xml, false, &[
( "ancestor::div", "四季" ),
( "ancestor-or-self::div", "四季秋" ),
]);
}
#[test]
fn test_sample_07() {
let xml = compress_spaces(r#"
<?xml version='1.0' encoding='UTF-8'?>
<root>
<para base="base" img="四季">
<para img="春">はる</para>
<section img="夏">
<para img="秋" />
<span>
<para img="冬" />
</span>
</section>
</para>
</root>
"#);
subtest_xpath("07", &xml, false, &[
( "descendant-or-self::para", "四季春秋冬" ),
( ".//para", "春秋冬" ),
( "..//para", "四季春秋冬" ),
]);
}
#[test]
fn test_sample_08() {
let xml = compress_spaces(r#"
<?xml version='1.0' encoding='UTF-8'?>
<root>
<para base="base" img="四季">
<para img="春">はる</para>
</para>
</root>
"#);
subtest_xpath("08a", &xml, false, &[
]);
let xml2 = compress_spaces(r#"
<?xml version='1.0' encoding='UTF-8'?>
<root>
<section base="base" img="四季">
<para img="春">はる</para>
</section>
</root>
"#);
subtest_xpath("08b", &xml2, false, &[
( "self::para", "" ),
]);
}
#[test]
fn test_sample_09() {
let xml = compress_spaces(r#"
<?xml version='1.0' encoding='UTF-8'?>
<root>
<division base="base" img="四季">
<chapter img="chap-1">
<para img="春">はる</para>
<note img="梅雨">つゆ</note>
<span>
<para img="夏">なつ</para>
</span>
</chapter>
<chapter img="chap-2">
<para img="秋">あき</para>
<span>
<para img="冬">ふゆ</para>
</span>
</chapter>
</division>
</root>
"#);
subtest_xpath("09", &xml, false, &[
( "child::chapter/descendant::para", "春夏秋冬" ),
( "chapter//para", "春夏秋冬" ),
( "child::*/child::para", "春秋" ),
( "*/para", "春秋" ),
]);
}
#[test]
fn test_sample_10() {
let xml = compress_spaces(r#"
<?xml version='1.0' encoding='UTF-8'?>
<?style-sheet alt="1" src="sample.css"?>
<root>
<div base="base" img="四季">
<chapter img="chap-1" />
</div>
</root>
"#);
let doc = new_document(&xml).unwrap();
let base_node = doc.get_first_node(r#"//*[@base="base"]"#).unwrap();
let document_root = base_node.get_first_node("/").unwrap();
let pi = document_root.get_first_node("processing-instruction()").unwrap();
assert_eq!(pi.name(), "style-sheet");
}
#[test]
fn test_sample_11() {
let xml = compress_spaces(r#"
<?xml version='1.0' encoding='UTF-8'?>
<root>
<para base="base" img="四季">
<para img="春">はる</para>
<div img="夏">
<para img="夏" />
<span>
<para img="秋" />
</span>
</div>
</para>
</root>
"#);
subtest_xpath("11", &xml, false, &[
( "/descendant::para", "四季春夏秋" ),
( "//para", "四季春夏秋" ),
]);
}
#[test]
fn test_sample_12() {
let xml = compress_spaces(r#"
<?xml version='1.0' encoding='UTF-8'?>
<root>
<para base="base" img="四季" />
<div>
<item img="x1">XX</item>
</div>
<olist>
<item img="春" />
<item img="夏" />
<ulist>
<item img="秋" />
</ulist>
</olist>
</root>
"#);
subtest_xpath("12", &xml, false, &[
( "/descendant::olist/child::item", "春夏" ),
( "//olist/item", "春夏" ),
]);
}
#[test]
fn test_sample_13() {
let xml = compress_spaces(r#"
<?xml version='1.0' encoding='UTF-8'?>
<root>
<para base="base" img="四季">
<note img="きせつ"/>
<para img="春">はる</para>
<para img="夏">なつ</para>
<para img="秋">あき</para>
<note img="季節"/>
</para>
</root>
"#);
subtest_xpath("13", &xml, false, &[
( "child::para[position()=1]", "春" ),
( "para[1]", "春" ),
( "child::para[position()=last()]", "秋" ),
( "para[last()]", "秋" ),
( "child::para[position()=last()-1]", "夏" ),
( "child::para[position()>1]", "夏秋" ),
]);
}
#[test]
fn test_sample_14() {
let xml = compress_spaces(r#"
<?xml version='1.0' encoding='UTF-8'?>
<root>
<para img="四季">
<chapter img="甲"/>
<chapter img="乙"/>
<chapter img="丙"/>
<chapter img="丁" base="base"/>
<chapter img="戊"/>
<chapter img="己"/>
<chapter img="庚"/>
<chapter img="辛"/>
<chapter img="壬"/>
<chapter img="癸"/>
</para>
</root>
"#);
subtest_xpath("14", &xml, false, &[
( "following-sibling::chapter[position()=1]", "戊" ),
( "preceding-sibling::chapter[position()=1]", "丙" ),
]);
}
#[test]
fn test_sample_15() {
let xml = compress_spaces(r#"
<?xml version='1.0' encoding='UTF-8'?>
<root>
<para img="p1">
<figure img="甲"/>
<span>
<figure img="乙"/>
</span>
</para>
<para base="base" img="p2">
<figure img="丙"/>
<span>
<figure img="丁"/>
<figure img="戊"/>
</span>
</para>
<para img="p3">
<figure img="己"/>
<figure img="庚"/>
<figure img="辛"/>
<figure img="壬"/>
<figure img="癸"/>
</para>
</root>
"#);
subtest_xpath("15", &xml, false, &[
( "/descendant::figure[position()=4]", "丁" ),
( "/descendant::figure[position()=6]", "己" ),
]);
}
#[test]
fn test_sample_16() {
let xml = compress_spaces(r#"
<?xml version='1.0' encoding='UTF-8'?>
<doc>
<chapter img="c1">
</chapter>
<chapter img="c2">
<section img="S21" base="base"/>
</chapter>
<chapter img="c3">
</chapter>
<chapter img="c4">
</chapter>
<chapter img="c5">
<section img="S51"/>
<section img="S52"/>
<section img="S53"/>
</chapter>
<chapter img="c6">
</chapter>
</doc>
"#);
subtest_xpath("20", &xml, false, &[
( "/child::doc/child::chapter[position()=5]/child::section[position()=2]", "S52" ),
( "/doc/chapter[5]/section[2]", "S52" ),
]);
}
#[test]
fn test_sample_17() {
let xml = compress_spaces(r#"
<?xml version='1.0' encoding='UTF-8'?>
<doc>
<chapter img="c1" base="base">
<para img="甲" type="warning"/>
<para img="乙"/>
<para img="丙"/>
<para img="丁" type="warning"/>
<para img="戊" type="warning"/>
<para img="己"/>
<para img="庚" type="warning"/>
<para img="辛" type="warning"/>
<para img="壬"/>
<para img="癸" type="warning"/>
</chapter>
</doc>
"#);
subtest_xpath("17", &xml, false, &[
( "child::para[attribute::type='warning']", "甲丁戊庚辛癸" ),
( "para[@type='warning']", "甲丁戊庚辛癸" ),
( "child::para[attribute::type='warning'][position()=5]", "辛" ),
( "para[@type='warning'][5]", "辛" ),
( "child::para[position()=5][attribute::type='warning']", "戊" ),
( "para[5][@type='warning']", "戊" ),
]);
}
#[test]
fn test_sample_18() {
let xml = compress_spaces(r#"
<?xml version='1.0' encoding='UTF-8'?>
<root>
<doc base="base">
<chapter img="春">
</chapter>
<chapter img="夏">
<title><bold>I</bold>ntroduction</title>
</chapter>
<chapter img="秋">
<title>NextStep</title>
</chapter>
<chapter img="冬">
</chapter>
</doc>
</root>
"#);
subtest_xpath("18", &xml, false, &[
( "child::chapter[child::title='Introduction']", "夏" ),
( "chapter[title='Introduction']", "夏" ),
( "child::chapter[child::title]", "夏秋" ),
( "chapter[title]", "夏秋" ),
]);
}
#[test]
fn test_sample_19() {
let xml = compress_spaces(r#"
<?xml version='1.0' encoding='UTF-8'?>
<root>
<doc base="base">
<chapter img="春">
</chapter>
<note img="梅雨">ばいう</note>
<chapter img="夏">
なつ
</chapter>
<note img="重陽" />
<chapter img="秋">
あき
</chapter>
<chapter img="冬">
</chapter>
<appendix img="正月">
</appendix>
<appendix img="四季">
</appendix>
</doc>
</root>
"#);
subtest_xpath("19", &xml, false, &[
( "child::*[self::chapter or self::appendix]", "春夏秋冬正月四季" ),
( "child::*[self::chapter or self::appendix][position()=last()]", "四季" ),
]);
}
#[test]
fn test_sample_20() {
let xml = compress_spaces(r#"
<?xml version='1.0' encoding='UTF-8'?>
<root lang="en" img="root">
<doc lang="ja" img="四季">
<chapter base="base" lang="ja-JP" img="春">
</chapter>
</doc>
</root>
"#);
subtest_xpath("20", &xml, false, &[
( ".", "春" ),
( "..", "四季" ),
]);
subtest_xpath("20", &xml, true, &[
( "../@lang", "ja" ),
]);
}
#[test]
fn test_sample_21() {
let xml = compress_spaces(r#"
<?xml version='1.0' encoding='UTF-8'?>
<root>
<hr base="base">
<employee img="John" secretary="t">John</employee>
<employee img="Jack" assistant="t">Jack</employee>
<employee img="Betty" secretary="t" assistant="t">Betty</employee>
<employee img="Tom" president="t">Tom</employee>
</hr>
</root>
"#);
subtest_xpath("21", &xml, false, &[
( "employee[@secretary and @assistant]", "Betty" ),
]);
}
#[test]
fn test_sample_51a() {
let xml = compress_spaces(r#"
<?xml version='1.0' encoding='UTF-8'?>
<root>
<chap img="上" base="base">
<para img="春"/>
<para img="夏"/>
</chap>
<chap img="下">
<para img="秋"/>
<para img="冬"/>
</chap>
</root>
"#);
subtest_xpath("51a", &xml, false, &[
( "//para[1]", "春秋" ),
( "/descendant::para[1]", "春" ),
]);
}
#[test]
fn test_sample_51b() {
let xml = compress_spaces(r#"
<?xml version='1.0' encoding='UTF-8'?>
<file base="base">
<body>
<unit img="A"/>
<group>
<unit img="B"/>
<unit img="C"/>
</group>
<unit img="D"/>
<unit img="E"/>
</body>
</file>
"#);
subtest_xpath("51b", &xml, false, &[
( "body//unit", "ABCDE" ),
( "body//unit[2]", "CD" ), ( "body/descendant-or-self::*/unit[2]", "CD" ), ( "body/descendant-or-self::unit[2]", "B" ), ( "body/descendant::unit[2]", "B" ), ( "body//unit[3]", "E" ),
( "(body//unit)[1]", "A" ),
]);
}
#[test]
fn test_sample_52() {
let xml = compress_spaces(r#"
<?xml version='1.0' encoding='UTF-8'?>
<root>
<foo img="上">
<foo img="春"/>
<baa img="夏"/>
</foo>
<foo img="下">
<foo img="秋" base="base"/>
<baa img="冬"/>
</foo>
</root>
"#);
subtest_xpath("52", &xml, false, &[
( "preceding::foo[1]", "春" ),
( "(preceding::foo)[1]", "上" ),
( "/descendant::foo[preceding::foo[1]/@img = '下']", "" ),
( "/descendant::foo[preceding::foo[1]/@img = '春']", "下秋" ),
( "/descendant::foo[preceding::foo[2]/@img = '上']", "下秋" ),
( "/descendant::foo[(preceding::foo)[1]/@img = '上']", "下秋" ),
( "/descendant::foo[(preceding::foo)[2]/@img = '春']", "下秋" ),
( "/descendant::foo[(preceding::foo)[1]/@img = '春']", "" ),
]);
}
#[test]
fn test_basic_xpath() {
let xml = compress_spaces(r#"
<root base="base">
<student>
<name>George</name>
<exam subject="math" point="70"/>
<exam subject="science" point="90"/>
</student>
<student>
<name>Harry</name>
<exam subject="math" point="85"/>
<exam subject="science" point="95"/>
</student>
<student>
<name>Ivonne</name>
<exam subject="math" point="60"/>
<exam subject="science" point="75"/>
</student>
</root>
"#);
subtest_eval_xpath("basic_xpath", &xml, &[
( r#"
for $student in /root/student return
($student/name/text(),
every $exam in $student/exam satisfies number($exam/@point) > 80)
"#,
r#"(George, false, Harry, true, Ivonne, false)"# ),
]);
}
}