xml.h
#pragma once
#include <iostream>
#include <map>
#include <list>
#include <string>
#include <sstream>
#include <functional>
namespace yazi
{
namespace xml
{
class Vaule
{
public:
Vaule();
Vaule(bool vaule);
Vaule(int vaule);
Vaule(double vaule);
Vaule(const char* vaule);
Vaule(const std::string& vaule);
~Vaule();
Vaule& operator=(bool vaule);
Vaule& operator=(int vaule);
Vaule& operator=(double vaule);
Vaule& operator=(const char* vaule);
Vaule& operator=(const std::string vaule);
bool operator == (const Vaule& other);
bool operator != (const Vaule& other);
operator bool();
operator int();
operator double();
operator std::string();
std::string GetVaule()const { return _vaule; }
// friend std::stringstream& operator<<(std::stringstream& ss, const Vaule& vaule);
private:
std::string _vaule;
};
class Xml
{
public:
Xml();
Xml(const char* name);
Xml(const std::string& name);
Xml(const Xml& other);
Xml& operator=(const Xml& other);
Xml(const std::string& node, const std::map<std::string, Vaule>& mp,const std::string text= "");
//节点名称
std::string name() const;
void name(const std::string& name);
//节点内容
std::string text()const;
void text(const std::string& text);
//节点属性
Vaule& attr(const std::string& key);
void attr(const std::string& key, const Vaule& val);
std::string str()const;
void append(const Xml& child);
Xml& operator[](int index);
Xml& operator[](const char* name);
Xml& operator[](const std::string name);
void remove(int index);
void remove(const char* name);
void remove(const std::string& name);
bool operator_file(const std::string& filename, std::function<void(Xml*)> func);
void clear();
typedef std::list<Xml>::iterator iterator;
iterator begin()
{
return _child->begin();
}
iterator end()
{
return _child->end();
}
iterator erase(iterator it)
{
it->clear();
return _child->erase(it);
}
int size() const
{
return static_cast<int> (_child->size());
}
bool load(const std::string& filename);
bool save(const std::string& filename);
bool parse(const std::string& str);
private:
std::string* _name;
std::string* _text;
std::map<std::string, Vaule>* _attrs;
std::list<Xml>* _child;
};
}
}
xml.cc
#include "Xml.h"
#include "Parser.h"
using namespace yazi::xml;
Vaule::Vaule()
{
}
Vaule::Vaule(bool vaule)
{
*this = vaule;
}
Vaule::Vaule(int vaule)
{
*this = vaule;
}
Vaule::Vaule(double vaule)
{
*this = vaule;
}
Vaule::Vaule(const char* vaule) : _vaule(vaule)
{
}
Vaule::Vaule(const std::string& vaule) : _vaule(vaule)
{
}
Vaule::~Vaule()
{
}
Vaule& Vaule::operator=(bool vaule)
{
_vaule = vaule ? "true" : "false";
return *this;
}
Vaule& Vaule::operator=(int vaule)
{
std::stringstream ss;
ss << vaule;
_vaule = ss.str();
return *this;
}
Vaule& Vaule::operator=(double vaule)
{
std::stringstream ss;
ss << vaule;
_vaule = ss.str();
return *this;
}
Vaule& Vaule::operator=(const char* vaule)
{
_vaule = vaule;
return *this;
}
Vaule& Vaule::operator=(const std::string vaule)
{
_vaule = vaule;
return *this;
}
bool Vaule::operator==(const Vaule& other)
{
return _vaule == other._vaule;
}
bool Vaule::operator!=(const Vaule& other)
{
return !((*this) == other);
}
Vaule::operator bool()
{
if (_vaule == "true")
{
return true;
}
else if (_vaule == "false")
{
return false;
}
else
{
return false;
}
}
Vaule::operator int()
{
return std::atoi(_vaule.c_str());
}
Vaule::operator double()
{
return std::atof(_vaule.c_str());
}
Vaule::operator std::string()
{
return _vaule;
}
Xml::Xml() : _name(nullptr), _text(nullptr), _attrs(nullptr), _child(nullptr)
{
}
Xml::Xml(const char* name) : _name(nullptr), _text(nullptr), _attrs(nullptr), _child(nullptr)
{
_name = new std::string(name);
}
Xml::Xml(const std::string& name) : _name(nullptr), _text(nullptr), _attrs(nullptr), _child(nullptr)
{
_name = new std::string(name);
}
Xml::Xml(const Xml& other)
{
_name = other._name;
_text = other._text;
_attrs = other._attrs;
_child = other._child;
}
Xml::Xml(const std::string& node, const std::map<std::string, Vaule>& mp, const std::string text)
{
_name = new std::string(node);
_attrs = new std::map<std::string, Vaule>(mp);
_text = new std::string(text);
}
Xml& Xml::operator=(const Xml& other)
{
clear();
_name = other._name;
_text = other._text;
_attrs = other._attrs;
_child = other._child;
return *this;
}
std::string Xml::name() const
{
if (_name == nullptr)
{
return "";
}
return *_name;
}
void Xml::name(const std::string& name)
{
if (_name != nullptr)
{
delete _name;
_name = nullptr;
}
_name = new std::string(name);
}
std::string Xml::text() const
{
if (_text == nullptr)
{
return "";
}
return *_text;
}
void Xml::text(const std::string& text)
{
if (_text != nullptr)
{
delete _text;
_text = nullptr;
}
_text = new std::string(text);
}
Vaule& Xml::attr(const std::string& key)
{
if (_attrs == nullptr)
{
_attrs = new std::map<std::string, Vaule>();
}
return (*_attrs)[key];
}
void Xml::attr(const std::string& key, const Vaule& val)
{
if (_attrs == nullptr)
{
_attrs = new std::map<std::string, Vaule>();
}
(*_attrs)[key] = val;
}
std::string Xml::str() const
{
if (_name == nullptr)
{
// throw new std::logic_error("element name is empty");
return "";
}
std::stringstream ss;
ss << "<";
ss << *_name;
if (_attrs != nullptr)
{
for (const auto& e : (*_attrs))
{
ss << " " << e.first << "=\"" << e.second.GetVaule() << "\"";
}
/*for (auto it = _attrs->begin(); it != _attrs->end(); ++it)
{
ss << " " << it->first << "=\"" << it->second << "\"";
}*/
}
ss << ">";
if (_child != nullptr)
{
for (const auto& e : (*_child))
{
ss << e.str();
}
}
if (_text != nullptr)
{
ss << *_text;
}
ss << "</" << *_name << ">";
return ss.str();
}
void Xml::append(const Xml& child)
{
if (_child == nullptr)
{
_child = new std::list<Xml>();
}
_child->push_back(child);
}
Xml& Xml::operator[](int index)
{
if (index < 0)
{
throw new std::logic_error("index less than zero");
}
if (_child == nullptr)
{
_child = new std::list<Xml>();
}
int size = static_cast<int>(_child->size());
if (index >= 0 && index < size)
{
auto it = _child->begin();
for (int i = 0; i < size; ++i)
{
++it;
}
return *it;
}
if (index >= size)
{
for (int i = index; i <= size; ++i)
{
_child->push_back(Xml());
}
}
return _child->back();
}
Xml& Xml::operator[](const char* name)
{
return (*this)[std::string(name)];
}
Xml& Xml::operator[](const std::string name)
{
if (_child == nullptr)
{
_child = new std::list<Xml>();
}
// for(const auto &e : (*child))
// {
// if(e.name() == name)
// {
//
// }
// }
for (auto it = _child->begin(); it != _child->end(); ++it)
{
if (it->name() == name)
{
return *it;
}
}
_child->push_back(Xml(name));
return _child->back();
}
void Xml::remove(int index)
{
if (_child == nullptr)
{
return;
}
int size = static_cast<int>(_child->size());
if (index < 0 || index >= size)
{
return;
}
auto it = _child->begin();
for (int i = 0; i < index; ++i)
{
it++;
}
it->clear();
_child->erase(it);
}
void Xml::remove(const char* name)
{
remove(std::string(name));
}
void Xml::remove(const std::string& name)
{
if (_child == nullptr)
{
return;
}
for (auto it = _child->begin(); it != _child->end();)
{
if (it->name() == name)
{
it->clear();
it = _child->erase(it);
}
else
{
++it;
}
}
}
void Xml::clear()
{
if (_name != nullptr)
{
delete _name;
_name = nullptr;
}
if (_text != nullptr)
{
delete _text;
_text = nullptr;
}
if (_attrs != nullptr)
{
delete _attrs;
_attrs = nullptr;
}
if (_child != nullptr)
{
for (auto it = _child->begin(); it != _child->end(); ++it)
{
it->clear();
}
delete _child;
_child = nullptr;
}
}
bool Xml::load(const std::string& filename)
{
Parser p;
if (!p.load_file(filename))
{
return false;
}
*this = p.parse();
return true;
}
bool Xml::save(const std::string& filename)
{
std::ofstream out(filename);
if (out.fail())
{
return false;
}
out << str();
out.close();
return true;
}
bool Xml::parse(const std::string& str)
{
Parser p;
if (!p.load_string(str))
{
return false;
}
*this = p.parse();
return true;
}
bool Xml::operator_file(const std::string& filename, std::function<void(Xml*)> func)
{
Parser p;
if (!p.load_file(filename))
{
return false;
}
*this = p.parse();
func(this);
save(filename);
return true;
}
Parser.h
#pragma once
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include "Xml.h"
namespace yazi
{
namespace xml
{
class Parser
{
public:
Parser();
bool load_file(const std::string & file);
bool load_string(const std::string& str);
Xml parse();
private:
void skip_white_space(); //去空格
bool parse_declaration();
bool parse_comment();
Xml parse_element();
std::string parse_element_name();
std::string parse_element_text();
std::string parse_element_attr_key();
std::string parse_element_attr_val();
private:
std::string _str;
int _index;
};
}
}
Parser.cc
#include "Parser.h"
using namespace yazi::xml;
Parser::Parser():_str(""),_index(0)
{
}
bool Parser::load_file(const std::string &file)
{
std::ifstream in(file);
if(in.fail())
{
return false;
}
std::stringstream ss;
ss << in.rdbuf();
_str = ss.str();
_index = 0;
in.close();
return true;
}
bool Parser::load_string(const std::string &str)
{
_str = str;
_index = 0;
return true;
}
Xml Parser::parse()
{
//核心
skip_white_space();
//头
if(_str.compare(_index,5,"<?xml") == 0)
{
if(!parse_declaration())
{
throw new std::logic_error("parse declaration error");
}
}
skip_white_space();
//注释
while(_str.compare(_index,4,"<!--") == 0)
{
if(!parse_comment())
{
throw new std::logic_error("parse comment error");
}
skip_white_space();
}
//解析根
if(_str[_index] == '<' && (isalpha(_str[_index + 1]) || _str[_index + 1] == '_'))
{
return parse_element();
}
throw new std::logic_error("parse element error");
}
void Parser::skip_white_space() // 去空格
{
while(_str[_index] == ' ' || _str[_index] == '\r' || _str[_index] == '\n' || _str[_index] == '\t')
{
++_index;
}
}
bool Parser::parse_declaration()
{
if(_str.compare(_index,5,"<?xml") != 0)
{
return false;
}
_index += 5;
size_t pos = _str.find("?>",_index);
if(pos == std::string::npos)
{
return false;
}
_index = static_cast<int>(pos) + 2;
return true;
}
bool Parser::parse_comment()
{
if(_str.compare(_index,4,"<!--") != 0)
{
return false;
}
_index += 4;
size_t pos = _str.find("-->",_index);
if(pos == std::string::npos)
{
return false;
}
_index = static_cast<int> (pos) + 3;
return true;
}
Xml Parser::parse_element()
{
Xml elem;
++_index;
skip_white_space();
std::string name = parse_element_name();
elem.name(std::move(name));
while(_str[_index] != '\0')
{
skip_white_space();
if(_str[_index] == '/')
{
if(_str[_index + 1] == '>')
{
_index += 2;
break;
}
else
{
throw std::logic_error("xml empty element is error");
}
}
else if(_str[_index] == '>')
{
++_index;
std::string text = parse_element_text();
if(text != "")
{
elem.text(std::move(text));
}
}
else if(_str[_index] == '<')
{
if(_str[_index + 1] == '/')
{
std::string end_tag = "</" + name + ">";
size_t pos = _str.find(end_tag,_index);
if(pos == std::string::npos)
{
throw new std::logic_error("xml element" + name + "end tag not found");
}
_index = (pos + end_tag.size());
break;
}
else if(_str.compare(_index,4,"<!--") == 0)
{
if(!parse_comment())
{
throw new std::logic_error("xml coment is error");
}
}
else
{
elem.append(parse_element());
}
}
else
{
std::string key = parse_element_attr_key();
skip_white_space();
if(_str[_index] != '=')
{
throw new std::logic_error("xml emlment attr is error" + key);
}
++_index;
skip_white_space();
std::string val = parse_element_attr_val();
elem.attr(std::move(key),std::move(val));
}
}
return elem;
}
std::string Parser::parse_element_name()
{
int pos = _index;
if(isalpha(_str[_index]) || _str[_index] == '_')
{
++_index;
while(isalnum(_str[_index]) \
|| _str[_index] == '_' \
|| _str[_index] == '-' \
|| _str[_index] == ':' \
|| _str[_index] == '.')
{
++_index;
}
}
return _str.substr(pos, _index - pos);
}
std::string Parser::parse_element_text()
{
int pos = _index;
while(_str[_index] != '<')
{
++_index;
}
return _str.substr(pos,_index - pos);
}
std::string Parser::parse_element_attr_key()
{
int pos = _index;
if(isalpha(_str[_index]) || _str[_index] == '_')
{
++_index;
while(isalnum(_str[_index]) \
|| _str[_index] == '_' \
|| _str[_index] == '-' \
|| _str[_index] == ':')
{
++_index;
}
}
return _str.substr(pos, _index - pos);
}
std::string Parser::parse_element_attr_val()
{
if(_str[_index] != '"')
{
throw new std::logic_error("xml element attr vaule should be in double quotes");
}
++_index;
int pos = _index;
while(_str[_index] != '"')
{
++_index;
}
++_index;
return _str.substr(pos,_index - pos - 1);
}
main.cc
#include "./xml/Xml.h"
using namespace yazi::xml;
#include <vector>
using namespace yazi::xml;
void write_file(Xml* x)
{
Xml ip1("addr", { {"ip","192.168.0.0.1"},{"mac","AB-BI-B7-0C"} });
(*x)["PLANT"]["AVAILABILITY"]["addr"].text("789");
(*x)["PLANT"]["AVAILABILITY"].append(ip1);
(*x)["PLANT"]["AVAILABILITY"].text("789");
// (*x)["PLANT"]["AVAILABILITY"].text("1234");
// x->remove("addr");
//(*x)["PLANT"].remove("addr");
}
int main()
{
Xml root;
root.operator_file("./test_xml", write_file);
/* Xml s1("student1");
s1.attr("id", 100);
s1.attr("name", "张三");
s1.attr("class", "塔利班");
s1.text("我是一个恐怖分子");
Xml s2("student2");
s2.attr("id", true);
s2.attr("name", "李四");
s2.attr("class", "塔利班");
s2.text("我是一个恐怖分子");
int falg = s1.attr("id");
std::cout << falg << std::endl;
Xml root;
root.name("root");
root[0] = s1;
root[1] = s2;
std::cout << root.str() << std::endl;
root.clear();
return 0;*/
/*Xml root;
root.name("root");
Xml Ip("Ip");
Xml ip1("addr", { { "ip", "192.168.0.0.1" }, { "mac","AB-BI-B7-0C" } });
Xml ip2("addr", { { "ip", "192.168.0.0.2" }, { "mac","AB-BI-B7-0C" } });
Xml ip3("addr", { { "ip", "192.168.0.0.3" }, { "mac","AB-BI-B7-0C" } });
Xml ip4("addr", { { "ip", "192.168.0.0.4" }, { "mac","AB-BI-B7-0C" } });
Xml ip5("addr", { { "ip", "192.168.0.0.5" }, { "mac","AB-BI-B7-0C" } });
Ip.append(ip1);
Ip.append(ip2);
Ip.append(ip3);
Ip.append(ip4);
root.append(Ip);
root["Ip"].append(ip5);
std::cout << root.str() << std::endl;*/
/* Xml root;
root.name("root");
Xml ip1("addr", { {"ip","192.168.0.0.1"},{"mac","AB-BI-B7-0C"} });
root.append(ip1);
std::cout << root.str() << std::endl;
root.clear();*/
}
test_xml
<CATALOG><PLANT><COMMON>Bloodroot</COMMON><BOTANICAL>Sanguinaria canadensis</BOTANICAL><ZONE>4</ZONE><LIGHT>Mostly Shady</LIGHT><PRICE>$2.44</PRICE><AVAILABILITY><addr>789</addr><addr ip="192.168.0.0.1" mac="AB-BI-B7-0C"></addr>789</AVAILABILITY>
</PLANT><PLANT><COMMON>Columbine</COMMON><BOTANICAL>Aquilegia canadensis</BOTANICAL><ZONE>3</ZONE><LIGHT>Mostly Shady</LIGHT><PRICE>$9.37</PRICE><AVAILABILITY>030699</AVAILABILITY>
</PLANT><PLANT><COMMON>Marsh Marigold</COMMON><BOTANICAL>Caltha palustris</BOTANICAL><ZONE>4</ZONE><LIGHT>Mostly Sunny</LIGHT><PRICE>$6.81</PRICE><AVAILABILITY>051799</AVAILABILITY>
</PLANT><PLANT><COMMON>Cowslip</COMMON><BOTANICAL>Caltha palustris</BOTANICAL><ZONE>4</ZONE><LIGHT>Mostly Shady</LIGHT><PRICE>$9.90</PRICE><AVAILABILITY>030699</AVAILABILITY>
</PLANT><PLANT><COMMON>Dutchman's-Breeches</COMMON><BOTANICAL>Dicentra cucullaria</BOTANICAL><ZONE>3</ZONE><LIGHT>Mostly Shady</LIGHT><PRICE>$6.44</PRICE><AVAILABILITY>012099</AVAILABILITY>
</PLANT><PLANT><COMMON>Ginger, Wild</COMMON><BOTANICAL>Asarum canadense</BOTANICAL><ZONE>3</ZONE><LIGHT>Mostly Shady</LIGHT><PRICE>$9.03</PRICE><AVAILABILITY>041899</AVAILABILITY>
</PLANT><PLANT><COMMON>Hepatica</COMMON><BOTANICAL>Hepatica americana</BOTANICAL><ZONE>4</ZONE><LIGHT>Mostly Shady</LIGHT><PRICE>$4.45</PRICE><AVAILABILITY>012699</AVAILABILITY>
</PLANT><PLANT><COMMON>Liverleaf</COMMON><BOTANICAL>Hepatica americana</BOTANICAL><ZONE>4</ZONE><LIGHT>Mostly Shady</LIGHT><PRICE>$3.99</PRICE><AVAILABILITY>010299</AVAILABILITY>
</PLANT><PLANT><COMMON>Jack-In-The-Pulpit</COMMON><BOTANICAL>Arisaema triphyllum</BOTANICAL><ZONE>4</ZONE><LIGHT>Mostly Shady</LIGHT><PRICE>$3.23</PRICE><AVAILABILITY>020199</AVAILABILITY>
</PLANT><PLANT><COMMON>Mayapple</COMMON><BOTANICAL>Podophyllum peltatum</BOTANICAL><ZONE>3</ZONE><LIGHT>Mostly Shady</LIGHT><PRICE>$2.98</PRICE><AVAILABILITY>060599</AVAILABILITY>
</PLANT><PLANT><COMMON>Phlox, Woodland</COMMON><BOTANICAL>Phlox divaricata</BOTANICAL><ZONE>3</ZONE><LIGHT>Sun or Shade</LIGHT><PRICE>$2.80</PRICE><AVAILABILITY>012299</AVAILABILITY>
</PLANT><PLANT><COMMON>Phlox, Blue</COMMON><BOTANICAL>Phlox divaricata</BOTANICAL><ZONE>3</ZONE><LIGHT>Sun or Shade</LIGHT><PRICE>$5.59</PRICE><AVAILABILITY>021699</AVAILABILITY>
</PLANT><PLANT><COMMON>Spring-Beauty</COMMON><BOTANICAL>Claytonia Virginica</BOTANICAL><ZONE>7</ZONE><LIGHT>Mostly Shady</LIGHT><PRICE>$6.59</PRICE><AVAILABILITY>020199</AVAILABILITY>
</PLANT><PLANT><COMMON>Trillium</COMMON><BOTANICAL>Trillium grandiflorum</BOTANICAL><ZONE>5</ZONE><LIGHT>Sun or Shade</LIGHT><PRICE>$3.90</PRICE><AVAILABILITY>042999</AVAILABILITY>
</PLANT><PLANT><COMMON>Wake Robin</COMMON><BOTANICAL>Trillium grandiflorum</BOTANICAL><ZONE>5</ZONE><LIGHT>Sun or Shade</LIGHT><PRICE>$3.20</PRICE><AVAILABILITY>022199</AVAILABILITY>
</PLANT><PLANT><COMMON>Violet, Dog-Tooth</COMMON><BOTANICAL>Erythronium americanum</BOTANICAL><ZONE>4</ZONE><LIGHT>Shade</LIGHT><PRICE>$9.04</PRICE><AVAILABILITY>020199</AVAILABILITY>
</PLANT><PLANT><COMMON>Trout Lily</COMMON><BOTANICAL>Erythronium americanum</BOTANICAL><ZONE>4</ZONE><LIGHT>Shade</LIGHT><PRICE>$6.94</PRICE><AVAILABILITY>032499</AVAILABILITY>
</PLANT><PLANT><COMMON>Adder's-Tongue</COMMON><BOTANICAL>Erythronium americanum</BOTANICAL><ZONE>4</ZONE><LIGHT>Shade</LIGHT><PRICE>$9.58</PRICE><AVAILABILITY>041399</AVAILABILITY>
</PLANT><PLANT><COMMON>Anemone</COMMON><BOTANICAL>Anemone blanda</BOTANICAL><ZONE>6</ZONE><LIGHT>Mostly Shady</LIGHT><PRICE>$8.86</PRICE><AVAILABILITY>122698</AVAILABILITY>
</PLANT><PLANT><COMMON>Grecian Windflower</COMMON><BOTANICAL>Anemone blanda</BOTANICAL><ZONE>6</ZONE><LIGHT>Mostly Shady</LIGHT><PRICE>$9.16</PRICE><AVAILABILITY>071099</AVAILABILITY>
</PLANT><PLANT><COMMON>Bee Balm</COMMON><BOTANICAL>Monarda didyma</BOTANICAL><ZONE>4</ZONE><LIGHT>Shade</LIGHT><PRICE>$4.59</PRICE><AVAILABILITY>050399</AVAILABILITY>
</PLANT><PLANT><COMMON>Bergamot</COMMON><BOTANICAL>Monarda didyma</BOTANICAL><ZONE>4</ZONE><LIGHT>Shade</LIGHT><PRICE>$7.16</PRICE><AVAILABILITY>042799</AVAILABILITY>
</PLANT><PLANT><COMMON>Black-Eyed Susan</COMMON><BOTANICAL>Rudbeckia hirta</BOTANICAL><ZONE>Annual</ZONE><LIGHT>Sunny</LIGHT><PRICE>$9.80</PRICE><AVAILABILITY>061899</AVAILABILITY>
</PLANT><PLANT><COMMON>Buttercup</COMMON><BOTANICAL>Ranunculus</BOTANICAL><ZONE>4</ZONE><LIGHT>Shade</LIGHT><PRICE>$2.57</PRICE><AVAILABILITY>061099</AVAILABILITY>
</PLANT><PLANT><COMMON>Crowfoot</COMMON><BOTANICAL>Ranunculus</BOTANICAL><ZONE>4</ZONE><LIGHT>Shade</LIGHT><PRICE>$9.34</PRICE><AVAILABILITY>040399</AVAILABILITY>
</PLANT><PLANT><COMMON>Butterfly Weed</COMMON><BOTANICAL>Asclepias tuberosa</BOTANICAL><ZONE>Annual</ZONE><LIGHT>Sunny</LIGHT><PRICE>$2.78</PRICE><AVAILABILITY>063099</AVAILABILITY>
</PLANT><PLANT><COMMON>Cinquefoil</COMMON><BOTANICAL>Potentilla</BOTANICAL><ZONE>Annual</ZONE><LIGHT>Shade</LIGHT><PRICE>$7.06</PRICE><AVAILABILITY>052599</AVAILABILITY>
</PLANT><PLANT><COMMON>Primrose</COMMON><BOTANICAL>Oenothera</BOTANICAL><ZONE>3 - 5</ZONE><LIGHT>Sunny</LIGHT><PRICE>$6.56</PRICE><AVAILABILITY>013099</AVAILABILITY>
</PLANT><PLANT><COMMON>Gentian</COMMON><BOTANICAL>Gentiana</BOTANICAL><ZONE>4</ZONE><LIGHT>Sun or Shade</LIGHT><PRICE>$7.81</PRICE><AVAILABILITY>051899</AVAILABILITY>
</PLANT><PLANT><COMMON>Blue Gentian</COMMON><BOTANICAL>Gentiana</BOTANICAL><ZONE>4</ZONE><LIGHT>Sun or Shade</LIGHT><PRICE>$8.56</PRICE><AVAILABILITY>050299</AVAILABILITY>
</PLANT><PLANT><COMMON>Jacob's Ladder</COMMON><BOTANICAL>Polemonium caeruleum</BOTANICAL><ZONE>Annual</ZONE><LIGHT>Shade</LIGHT><PRICE>$9.26</PRICE><AVAILABILITY>022199</AVAILABILITY>
</PLANT><PLANT><COMMON>Greek Valerian</COMMON><BOTANICAL>Polemonium caeruleum</BOTANICAL><ZONE>Annual</ZONE><LIGHT>Shade</LIGHT><PRICE>$4.36</PRICE><AVAILABILITY>071499</AVAILABILITY>
</PLANT><PLANT><COMMON>California Poppy</COMMON><BOTANICAL>Eschscholzia californica</BOTANICAL><ZONE>Annual</ZONE><LIGHT>Sun</LIGHT><PRICE>$7.89</PRICE><AVAILABILITY>032799</AVAILABILITY>
</PLANT><PLANT><COMMON>Shooting Star</COMMON><BOTANICAL>Dodecatheon</BOTANICAL><ZONE>Annual</ZONE><LIGHT>Mostly Shady</LIGHT><PRICE>$8.60</PRICE><AVAILABILITY>051399</AVAILABILITY>
</PLANT><PLANT><COMMON>Snakeroot</COMMON><BOTANICAL>Cimicifuga</BOTANICAL><ZONE>Annual</ZONE><LIGHT>Shade</LIGHT><PRICE>$5.63</PRICE><AVAILABILITY>071199</AVAILABILITY>
</PLANT><PLANT><COMMON>Cardinal Flower</COMMON><BOTANICAL>Lobelia cardinalis</BOTANICAL><ZONE>2</ZONE><LIGHT>Shade</LIGHT><PRICE>$3.02</PRICE><AVAILABILITY>022299</AVAILABILITY>
</PLANT>
</CATALOG>