alpar@83: /* -*- C++ -*-
alpar@83:  *
alpar@174:  * This file is a part of LEMON, a generic C++ optimization library
alpar@174:  *
alpar@174:  * Copyright (C) 2003-2006
alpar@174:  * Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport
alpar@83:  * (Egervary Research Group on Combinatorial Optimization, EGRES).
alpar@83:  *
alpar@83:  * Permission to use, modify and distribute this software is granted
alpar@83:  * provided that this copyright notice appears in all copies. For
alpar@83:  * precise terms see the accompanying LICENSE file.
alpar@83:  *
alpar@83:  * This software is provided "AS IS" with no warranty of any kind,
alpar@83:  * express or implied, and with no claim as to its suitability for any
alpar@83:  * purpose.
alpar@83:  *
alpar@83:  */
alpar@2: 
alpar@2: #include <iostream>
alpar@2: #include <string>
alpar@2: #include <vector>
alpar@2: #include <list>
alpar@2: #include <map>
alpar@39: #include <lemon/error.h>
hegyi@150: #include <lemon/dim2.h>
alpar@2: 
alpar@39: namespace lemon {
alpar@2: 
alpar@83:   class XmlIo 
alpar@39:   {
alpar@83:     bool _writeMode;
alpar@83:   public:
alpar@83:     ///Check if XmlIo is in write mode.
alpar@83:     bool write() { return _writeMode;}
alpar@83:     ///Check if XmlIo is in read mode.
alpar@83:     bool read() { return !_writeMode;}
alpar@83: 
alpar@39:     std::ostream& os;
alpar@39:     int level;
alpar@83:     
alpar@39:   protected:
alpar@39:     void indent(int level) {
alpar@39:       os << std::endl;
alpar@39:       for(int i=0;i<level;i++) os << ' ';
alpar@39:     }
alpar@39:     void tag(const std::string &_tag) {
alpar@39:       os << '<' << _tag << '>';
alpar@39:     }
alpar@39:     void etag(const std::string &_tag) {
alpar@39:       os << "</" << _tag << '>';
alpar@39:     }
alpar@39:     void itag(const std::string &_tag) { indent();tag(_tag); }
alpar@39:     void ietag(const std::string &_tag) { indent();etag(_tag); }
alpar@2: 
alpar@39:     void beginTag(const std::string &_tag) {
alpar@39:       itag(_tag);
alpar@39:       level++;
alpar@39:     }
alpar@39:     void endTag(const std::string &_tag) {
alpar@39:       level--;
alpar@39:       ietag(_tag);
alpar@39:     }
alpar@2: 
alpar@39:   public:
alpar@83:     ///Indent the line according to its level.
alpar@39: 
alpar@83:     ///\warning It can only be used in write mode.
alpar@83:     ///
alpar@83:     void indent()
alpar@39:     {
alpar@83:       if(write())
alpar@83: 	if(level>=0) indent(level);
alpar@83: 	else level=0;
alpar@83:       else throw LogicError();	
alpar@39:     }
alpar@2:   
alpar@83:     ///Read/write a tag
alpar@2:   
alpar@83:     ///Read/write a tag.
alpar@83:     ///In write mode it does not start a new line.
alpar@39:     class ContTag
alpar@39:     {
alpar@83:       XmlIo &ix;
alpar@39:       const std::string _tag;
alpar@39:     public:
alpar@39:       ///\e
alpar@2:   
alpar@39:       ///\e
alpar@39:       ///
alpar@83:       ContTag(XmlIo &_ix,const std::string &_t) :
alpar@39: 	ix(_ix), _tag(_t)
alpar@39:       {
alpar@83: 	if(ix.write()) ix.tag(_tag);
alpar@83: 	else ix.useTag(_tag);
alpar@39:       }
alpar@83:       ~ContTag() { 
alpar@83: 	if(ix.write()) ix.etag(_tag);
alpar@83: 	else if(!std::uncaught_exception()) ix.useTag('/'+_tag);      
alpar@83:       }
alpar@39:     };
alpar@39: 
alpar@83:     ///Read/write a tag
alpar@83:   
alpar@83:     ///Read/write a tag.
alpar@124:     ///The whole \<foo\> ... \</foo\> will be placed in a single line.
alpar@39:     class LineTag
alpar@2:     {
alpar@83:       XmlIo &ix;
alpar@39:       const std::string _tag;
alpar@39:     public:
alpar@39:       ///\e
alpar@39:     
alpar@39:       ///\e
alpar@39:       ///
alpar@83:       LineTag(XmlIo &_ix,const std::string &_t) :
alpar@39: 	ix(_ix), _tag(_t)
alpar@39:       {
alpar@83: 	if(ix.write()) ix.itag(_tag);
alpar@83: 	else ix.useTag(_tag);
alpar@39:       }
alpar@83:       ~LineTag() { 
alpar@83: 	if(ix.write()) ix.etag(_tag);
alpar@83: 	else if(!std::uncaught_exception()) ix.useTag('/'+_tag);
alpar@83:       }
alpar@39:     };
alpar@39: 
alpar@83:     ///Read/write a tag
alpar@39:   
alpar@83:     ///Read/write a tag.
alpar@39:     ///
alpar@39:     class Tag
alpar@39:     {
alpar@83:       XmlIo &ix;
alpar@39:       const std::string _tag;
alpar@39:     public:
alpar@39:       ///\e
alpar@39:   
alpar@39:       ///\e
alpar@39:       ///
alpar@83:       Tag(XmlIo &_ix,const std::string &_t) :
alpar@39: 	ix(_ix), _tag(_t)
alpar@39:       {
alpar@83: 	if(ix.write()) ix.beginTag(_tag);
alpar@83: 	else ix.useTag(_tag);
alpar@39:       }
alpar@83:       ~Tag() {
alpar@83: 	if(ix.write()) ix.endTag(_tag);
alpar@83: 	else if(!std::uncaught_exception()) ix.useTag('/'+_tag);
alpar@39:       }
alpar@39:     };
alpar@83: 
alpar@83:   private:
alpar@83:     std::istream& is;
alpar@83:     std::string next_tag;
alpar@83:     int line_number;
alpar@83: 
alpar@83:     void skipWhiteSpaces()
alpar@83:     {
alpar@83:       if(write()) throw LogicError();
alpar@83:       {
alpar@83: 	char c;
alpar@83: 	while (is.get(c) && std::isspace(c,is.getloc()))
alpar@83: 	  if(c=='\n') line_number++;
alpar@83: 	is.unget();
alpar@83:       }
alpar@83:     }
alpar@83:   protected:
alpar@83:     /// Use the next tag.
alpar@83: 
alpar@83:     ///\e
alpar@83:     ///
alpar@83:     void useTag() {next_tag.clear();}
alpar@83:   
alpar@83:     ///Use the next tag and check if it is equal with \c _tag
alpar@83:   
alpar@83:     ///\e
alpar@83:     ///
alpar@83:     void useTag(const std::string &_tag) {
alpar@83:       if(nextTag()==_tag) useTag();
alpar@83:       else throw DataFormatError("",line_number,"Unexpected token name");
alpar@83:     }
alpar@83:   public:
alpar@83:     ///Return the next tag (if a tag follows on the stream).
alpar@83:   
alpar@83:     ///\warning It can only be used in read mode.
alpar@83:     ///
alpar@83:     const std::string &nextTag() 
alpar@83:     {
alpar@83:       if(write()) throw LogicError();
alpar@83:       else if(next_tag.empty()) {
alpar@83: 	char c;
alpar@83: 	skipWhiteSpaces();
alpar@83: 	if(!is.get(c) || c!='<')
alpar@83: 	  throw DataFormatError("",line_number,"Bad format");
alpar@83: 	next_tag.clear();
alpar@83: 	while (is.get(c) && c!='>') next_tag.push_back(c);
alpar@83: 	if(c!='>')
alpar@83: 	  throw DataFormatError("",line_number,"Bad format");
alpar@83:       }
alpar@83:       return next_tag;
alpar@83:     }
alpar@83: 
alpar@83:     /**********************************************************************/
alpar@83: 
alpar@83: 
alpar@39:     ///\e
alpar@39:   
alpar@39:     ///\e
alpar@39:     ///
alpar@83:     XmlIo(std::ostream& _os) : _writeMode(true), os(_os),
alpar@83: 			       level(-1),
alpar@127: 			       is(std::cin) {}
alpar@83:     ///\e
alpar@83:     ///
alpar@83:     XmlIo(std::istream& _is) : _writeMode(false),
alpar@127: 			       os(std::cout), is(_is),
alpar@83: 			       line_number(1) {}
alpar@83: 
alpar@83:     ~XmlIo() { if(write()) os<< std::endl; }
alpar@39:   
alpar@83: 
alpar@83: 
alpar@83:     XmlIo &operator()(const int &v) 
alpar@39:     { 
alpar@83:       if(write()) os << v;
alpar@83:       else {
alpar@83: 	skipWhiteSpaces();
alpar@83: 	if(!(is >> const_cast<int &>(v))) 
alpar@83: 	  throw DataFormatError("",line_number,"Not an 'int'");
alpar@83:       }
alpar@39:       return *this;
alpar@2:     }
alpar@83:     XmlIo &operator()(const double &v) 
alpar@83:     {
alpar@83:       if(write()) os << v;
alpar@83:       else {
alpar@83: 	skipWhiteSpaces();
alpar@83: 	if(!(is >> const_cast<double &>(v))) 
alpar@83: 	  throw DataFormatError("",line_number,"Not an 'double'");
alpar@83:       }
alpar@83:       return *this;
alpar@83:     }
alpar@83:     XmlIo &operator()(const std::string &v)
alpar@83:     {
alpar@83:       if(write())
alpar@83: 	for(std::string::const_iterator i=v.begin();i!=v.end();++i)
alpar@83: 	  switch(*i) {
alpar@83: 	  case '\\':
alpar@83: 	    os << "\\\\";
alpar@83: 	    break;
alpar@83: 	  case '<':
alpar@83: 	    os << "\\<";
alpar@83: 	    break;
alpar@83: 	  case '&':
alpar@83: 	    os << "\\&";
alpar@83: 	    break;
alpar@83: 	  case '\n':
alpar@83: 	    os << "\\n";
alpar@83: 	    break;
alpar@83: 	  default:
alpar@83: 	    os<<*i;
alpar@83: 	    break;
alpar@83: 	  }
alpar@83:       else {
alpar@83: 	std::string &w = const_cast<std::string &>(v);
alpar@83: 	w.clear();
alpar@83: 	char c;
alpar@83: 	while (is.get(c) && c!='<')
alpar@83: 	  if(c=='\\')
alpar@83: 	    if(!is.get(c))
alpar@83: 	      throw DataFormatError("",line_number,"Bad string");
alpar@83: 	    else switch(c) {
alpar@83: 	    case 'n':
alpar@83: 	      w.push_back('\n');
alpar@83: 	      break;
alpar@83: 	    default:
alpar@83: 	      w.push_back(c);
alpar@83: 	      break;
alpar@83: 	    }
alpar@83: 	  else {
alpar@83: 	    if(c=='\n') line_number++;
alpar@83: 	    w.push_back(c);
alpar@83: 	  }
alpar@83: 	if(c!='<')
alpar@83: 	  throw DataFormatError("",line_number,"Unexpected eof");
alpar@83: 	is.unget();
alpar@83:       }
alpar@83:       return *this;
alpar@83:     }
alpar@83: 
alpar@83: 
alpar@83:     XmlIo &operator()(const std::string &_tag,const int &v) 
alpar@39:     { 
alpar@39:       LineTag t(*this,_tag);
alpar@39:       (*this)(v);
alpar@39:       return *this;
alpar@39:     }
alpar@83:     XmlIo &operator()(const std::string &_tag,const double &v) 
alpar@39:     {
alpar@39:       LineTag t(*this,_tag);
alpar@39:       (*this)(v);
alpar@39:       return *this;
alpar@39:     }
alpar@83:     XmlIo &operator()(const std::string &_tag,const std::string &v)
alpar@39:     {
alpar@39:       LineTag t(*this,_tag);
alpar@39:       (*this)(v);
alpar@39:       return *this;
alpar@39:     }
alpar@39:     ///\e
alpar@39:   
alpar@39:     ///\e
alpar@39:     ///
alpar@39:     template<class V>
alpar@83:     XmlIo &operator()(const std::string &_tag,const V &v)
alpar@39:     {
alpar@39:       Tag t(*this,_tag);
alpar@83:       xml(*this,const_cast<V &>(v));
alpar@39:       return *this;
alpar@39:     }
alpar@39:     ///\e
alpar@39:   
alpar@39:     ///\e
alpar@39:     ///
alpar@39:     template<class V>
alpar@83:     XmlIo &operator()(const V &v)
alpar@39:     {
alpar@83:       xml(*this,const_cast<V &>(v));
alpar@39:       return *this;
alpar@39:     }
alpar@83:     };
alpar@2: 
alpar@39:   //////////////////////////////////////////////////////////////////////
alpar@39:   //////////////////////////////////////////////////////////////////////
alpar@39: 
alpar@83:   ///\e
alpar@83:   
alpar@83:   ///\relates XmlIo
alpar@83:   ///
alpar@39:   template<class A>
alpar@83:   void xml(XmlIo &x,std::auto_ptr<A> &v)
alpar@39:   {
alpar@83:     if(x.write()) v=new A;
alpar@39:     x(*v);
alpar@39:   }
alpar@83:   
alpar@83:   ///\e
alpar@83:   
alpar@83:   ///\relates XmlIo
alpar@83:   ///
alpar@39:   template<class A,class B>
alpar@83:   void xml(XmlIo &x,std::pair<A,B> &v)
alpar@39:   {
alpar@39:     x("first",v.first);
alpar@39:     x("second",v.second);
alpar@39:   }
alpar@39: 
alpar@83:   ///\e
alpar@83:   
alpar@83:   ///\relates XmlIo
alpar@83:   ///
alpar@83:   template<class T>
alpar@83:   void xml(XmlIo &x,std::list<T> &v)
alpar@39:   {
alpar@83:     if(x.write())
alpar@83:       for(typename std::list<T>::const_iterator it=v.begin();
alpar@83: 	  it!=v.end();++it) x("item",*it);
alpar@83:     else while(x.nextTag()=="item")
alpar@39:       {
alpar@39: 	v.push_back(T());
alpar@39: 	x("item",v.back());
alpar@39:       }
alpar@39:   }
alpar@83:   ///\e
alpar@83:   
alpar@83:   ///\relates XmlIo
alpar@83:   ///
alpar@39:   template<class T>
alpar@83:   void xml(XmlIo &x,std::vector<T> &v)
alpar@39:   {
alpar@83:     if(x.write())
alpar@83:       for(typename std::vector<T>::const_iterator it=v.begin();
alpar@83: 	  it!=v.end();++it) x("item",*it);
alpar@83:     else while(x.nextTag()=="item")
alpar@39:       {
alpar@39: 	v.push_back(T());
alpar@39: 	x("item",v.back());      
alpar@39:       }
alpar@39:   }
alpar@39: 
alpar@83:   ///\e
alpar@83:   
alpar@83:   ///\relates XmlIo
alpar@83:   ///
alpar@39:   template<class K,class V>
alpar@83:   void xml(XmlIo &x,std::map<K,V> &v)
alpar@39:   {
alpar@83:     if(x.write()) 
alpar@83:       for(typename std::map<K,V>::const_iterator it=v.begin();
alpar@83: 	  it!=v.end();++it) x("item",*it);
alpar@83:     else while(x.nextTag()=="item")
alpar@39:       {
alpar@39: 	typename std::map<K,V>::value_type it;
alpar@39: 	x("item",it);
alpar@39: 	v.insert(it);
alpar@39:       }
alpar@39:   }
alpar@39: 
alpar@83:   ///\e
alpar@83:   
alpar@83:   ///\relates XmlIo
alpar@83:   ///
alpar@39:   template<class T>
hegyi@150:   void xml(XmlIo &x,lemon::dim2::Point<T> &v)
alpar@39:   {
alpar@83:     { XmlIo::LineTag t(x,"x"); x(v.x); }
alpar@83:     { XmlIo::ContTag t(x,"y"); x(v.y); }
alpar@39:   }
alpar@39: 
alpar@83:   ///\e
alpar@83:   
alpar@83:   ///\relates XmlIo
alpar@83:   ///
alpar@39:   template<class T>
hegyi@150:   void xml(XmlIo &x,lemon::dim2::BoundingBox<T> &v)
alpar@39:   {
alpar@83:     if(x.write()) {
alpar@83:       if(!v.empty()) {
alpar@83: 	x("point",v.bottomLeft());
alpar@83: 	if(v.bottomLeft()!=v.topRight()) x("point",v.topRight());
alpar@83:       }
alpar@39:     }
alpar@83:     else {
alpar@83:       v.clear();
alpar@83:       while(x.nextTag()=="point") {
hegyi@150: 	lemon::dim2::Point<T> co;
alpar@83: 	x("point",co);
alpar@83: 	v.add(co);
alpar@83:       }
alpar@39:     }
alpar@39:   }
alpar@2:   
alpar@2: }
alpar@83: