Index: xml.h
===================================================================
--- xml.h	(revision 39)
+++ xml.h	(revision 83)
@@ -1,3 +1,17 @@
-/* -*- C++ -*- */
+/* -*- C++ -*-
+ * gui/xml.h - Part of LEMON, a generic C++ optimization library
+ *
+ * Copyright (C) 2005 Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport
+ * (Egervary Research Group on Combinatorial Optimization, EGRES).
+ *
+ * Permission to use, modify and distribute this software is granted
+ * provided that this copyright notice appears in all copies. For
+ * precise terms see the accompanying LICENSE file.
+ *
+ * This software is provided "AS IS" with no warranty of any kind,
+ * express or implied, and with no claim as to its suitability for any
+ * purpose.
+ *
+ */
 
 #include <iostream>
@@ -11,9 +25,16 @@
 namespace lemon {
 
-  class XmlWriter 
-  {
+  class XmlIo 
+  {
+    bool _writeMode;
+  public:
+    ///Check if XmlIo is in write mode.
+    bool write() { return _writeMode;}
+    ///Check if XmlIo is in read mode.
+    bool read() { return !_writeMode;}
+
     std::ostream& os;
     int level;
-
+    
   protected:
     void indent(int level) {
@@ -40,18 +61,23 @@
 
   public:
-
-    void indent() 
-    {
-      if(level>=0) indent(level);
-      else level=0;
-    }
-  
-    ///\e
-  
-    ///\e
-    ///
+    ///Indent the line according to its level.
+
+    ///\warning It can only be used in write mode.
+    ///
+    void indent()
+    {
+      if(write())
+	if(level>=0) indent(level);
+	else level=0;
+      else throw LogicError();	
+    }
+  
+    ///Read/write a tag
+  
+    ///Read/write a tag.
+    ///In write mode it does not start a new line.
     class ContTag
     {
-      XmlWriter &ix;
+      XmlIo &ix;
       const std::string _tag;
     public:
@@ -60,15 +86,23 @@
       ///\e
       ///
-      ContTag(XmlWriter &_ix,const std::string &_t) :
+      ContTag(XmlIo &_ix,const std::string &_t) :
 	ix(_ix), _tag(_t)
       {
-	ix.tag(_tag);
-      }
-      ~ContTag() { ix.etag(_tag);}
+	if(ix.write()) ix.tag(_tag);
+	else ix.useTag(_tag);
+      }
+      ~ContTag() { 
+	if(ix.write()) ix.etag(_tag);
+	else if(!std::uncaught_exception()) ix.useTag('/'+_tag);      
+      }
     };
 
+    ///Read/write a tag
+  
+    ///Read/write a tag.
+    ///The whole <foo> ... </foo> will be places in a single line.
     class LineTag
     {
-      XmlWriter &ix;
+      XmlIo &ix;
       const std::string _tag;
     public:
@@ -77,19 +111,23 @@
       ///\e
       ///
-      LineTag(XmlWriter &_ix,const std::string &_t) :
+      LineTag(XmlIo &_ix,const std::string &_t) :
 	ix(_ix), _tag(_t)
       {
-	ix.itag(_tag);
-      }
-      ~LineTag() { ix.etag(_tag);}
+	if(ix.write()) ix.itag(_tag);
+	else ix.useTag(_tag);
+      }
+      ~LineTag() { 
+	if(ix.write()) ix.etag(_tag);
+	else if(!std::uncaught_exception()) ix.useTag('/'+_tag);
+      }
     };
 
-    ///\e
-  
-    ///\e
+    ///Read/write a tag
+  
+    ///Read/write a tag.
     ///
     class Tag
     {
-      XmlWriter &ix;
+      XmlIo &ix;
       const std::string _tag;
     public:
@@ -98,27 +136,157 @@
       ///\e
       ///
-      Tag(XmlWriter &_ix,const std::string &_t) :
+      Tag(XmlIo &_ix,const std::string &_t) :
 	ix(_ix), _tag(_t)
       {
-	ix.beginTag(_tag);
-      }
-      ~Tag() { 
-	ix.endTag(_tag);
+	if(ix.write()) ix.beginTag(_tag);
+	else ix.useTag(_tag);
+      }
+      ~Tag() {
+	if(ix.write()) ix.endTag(_tag);
+	else if(!std::uncaught_exception()) ix.useTag('/'+_tag);
       }
     };
-      
-    ///\e
-  
-    ///\e
-    ///
-    XmlWriter(std::ostream& _os) : os(_os),level(-1) {}
-    ~XmlWriter() { os<< std::endl; }
-  
-    XmlWriter &operator()(int v) 
+
+  private:
+    std::istream& is;
+    std::string next_tag;
+    int line_number;
+
+    void skipWhiteSpaces()
+    {
+      if(write()) throw LogicError();
+      {
+	char c;
+	while (is.get(c) && std::isspace(c,is.getloc()))
+	  if(c=='\n') line_number++;
+	is.unget();
+      }
+    }
+  protected:
+    /// Use the next tag.
+
+    ///\e
+    ///
+    void useTag() {next_tag.clear();}
+  
+    ///Use the next tag and check if it is equal with \c _tag
+  
+    ///\e
+    ///
+    void useTag(const std::string &_tag) {
+      if(nextTag()==_tag) useTag();
+      else throw DataFormatError("",line_number,"Unexpected token name");
+    }
+  public:
+    ///Return the next tag (if a tag follows on the stream).
+  
+    ///\warning It can only be used in read mode.
+    ///
+    const std::string &nextTag() 
+    {
+      if(write()) throw LogicError();
+      else if(next_tag.empty()) {
+	char c;
+	skipWhiteSpaces();
+	if(!is.get(c) || c!='<')
+	  throw DataFormatError("",line_number,"Bad format");
+	next_tag.clear();
+	while (is.get(c) && c!='>') next_tag.push_back(c);
+	if(c!='>')
+	  throw DataFormatError("",line_number,"Bad format");
+      }
+      return next_tag;
+    }
+
+    /**********************************************************************/
+
+
+    ///\e
+  
+    ///\e
+    ///
+    XmlIo(std::ostream& _os) : _writeMode(true), os(_os),
+			       level(-1),
+			       is(*(std::istream*)(NULL)) {}
+    ///\e
+    ///
+    XmlIo(std::istream& _is) : _writeMode(false),
+			       os(*(std::ostream*)(NULL)), is(_is),
+			       line_number(1) {}
+
+    ~XmlIo() { if(write()) os<< std::endl; }
+  
+
+
+    XmlIo &operator()(const int &v) 
     { 
-      os << v;
-      return *this;
-    }
-    XmlWriter &operator()(const std::string &_tag,int v) 
+      if(write()) os << v;
+      else {
+	skipWhiteSpaces();
+	if(!(is >> const_cast<int &>(v))) 
+	  throw DataFormatError("",line_number,"Not an 'int'");
+      }
+      return *this;
+    }
+    XmlIo &operator()(const double &v) 
+    {
+      if(write()) os << v;
+      else {
+	skipWhiteSpaces();
+	if(!(is >> const_cast<double &>(v))) 
+	  throw DataFormatError("",line_number,"Not an 'double'");
+      }
+      return *this;
+    }
+    XmlIo &operator()(const std::string &v)
+    {
+      if(write())
+	for(std::string::const_iterator i=v.begin();i!=v.end();++i)
+	  switch(*i) {
+	  case '\\':
+	    os << "\\\\";
+	    break;
+	  case '<':
+	    os << "\\<";
+	    break;
+	  case '&':
+	    os << "\\&";
+	    break;
+	  case '\n':
+	    os << "\\n";
+	    break;
+	  default:
+	    os<<*i;
+	    break;
+	  }
+      else {
+	std::string &w = const_cast<std::string &>(v);
+	w.clear();
+	char c;
+	while (is.get(c) && c!='<')
+	  if(c=='\\')
+	    if(!is.get(c))
+	      throw DataFormatError("",line_number,"Bad string");
+	    else switch(c) {
+	    case 'n':
+	      w.push_back('\n');
+	      break;
+	    default:
+	      w.push_back(c);
+	      break;
+	    }
+	  else {
+	    if(c=='\n') line_number++;
+	    w.push_back(c);
+	  }
+	if(c!='<')
+	  throw DataFormatError("",line_number,"Unexpected eof");
+	is.unget();
+      }
+      return *this;
+    }
+
+
+    XmlIo &operator()(const std::string &_tag,const int &v) 
     { 
       LineTag t(*this,_tag);
@@ -126,10 +294,5 @@
       return *this;
     }
-    XmlWriter &operator()(double v) 
-    {
-      os << v;
-      return *this;
-    }
-    XmlWriter &operator()(const std::string &_tag,double v) 
+    XmlIo &operator()(const std::string &_tag,const double &v) 
     {
       LineTag t(*this,_tag);
@@ -137,27 +300,5 @@
       return *this;
     }
-    XmlWriter &operator()(const std::string &v)
-    {
-      for(std::string::const_iterator i=v.begin();i!=v.end();++i)
-	switch(*i) {
-	case '\\':
-	  os << "\\\\";
-	  break;
-	case '<':
-	  os << "\\<";
-	  break;
-	case '&':
-	  os << "\\&";
-	  break;
-	case '\n':
-	  os << "\\n";
-	  break;
-	default:
-	  os<<*i;
-	  break;
-	}
-      return *this;
-    }
-    XmlWriter &operator()(const std::string &_tag,const std::string &v)
+    XmlIo &operator()(const std::string &_tag,const std::string &v)
     {
       LineTag t(*this,_tag);
@@ -170,8 +311,8 @@
     ///
     template<class V>
-    XmlWriter &operator()(const std::string &_tag,const V &v)
+    XmlIo &operator()(const std::string &_tag,const V &v)
     {
       Tag t(*this,_tag);
-      out(*this,v);
+      xml(*this,const_cast<V &>(v));
       return *this;
     }
@@ -181,181 +322,31 @@
     ///
     template<class V>
-    XmlWriter &operator()(const V &v)
-    {
-      out(*this,v);
-      return *this;
-    }
-  };
+    XmlIo &operator()(const V &v)
+    {
+      xml(*this,const_cast<V &>(v));
+      return *this;
+    }
+    };
 
   //////////////////////////////////////////////////////////////////////
-
-  class XmlReader 
-  {
-    std::istream& is;
-    std::string next_tag;
-    int line_number;
-
-    void skipWhiteSpaces()
-    {
-      char c;
-      while (is.get(c) && std::isspace(c,is.getloc())) if(c=='\n') line_number++;
-      is.unget();
-    }
-  protected:
-    ///\e
-  
-    ///\e
-    ///
-    void useTag() {next_tag.clear();}
-  
-    void useTag(const std::string &_tag) {
-      if(nextTag()==_tag) useTag();
-      else throw DataFormatError("",line_number,"Unexpected token name");
-    }
-  public:
-    ///\e
-  
-    ///\e
-    ///
-    const std::string &nextTag() 
-    {
-      if(next_tag.empty()) {
-	char c;
-	skipWhiteSpaces();
-	if(!is.get(c) || c!='<')
-	  throw DataFormatError("",line_number,"Bad token");
-	next_tag.clear();
-	while (is.get(c) && c!='>') next_tag.push_back(c);
-	if(c!='>')
-	  throw DataFormatError("",line_number,"Bad token");
-      }
-      return next_tag;
-    }
-  
-    ///\e
-  
-    ///\e
-    ///
-    class Tag
-    {
-      XmlReader &ix;
-      const std::string tag;
-    public:
-      ///\e
-    
-      ///\e
-      ///
-      Tag(XmlReader &_ix,const std::string &_tag) :
-	ix(_ix), tag(_tag)
-      {
-	ix.useTag(_tag);
-      }
-      ~Tag() {
-	if(!std::uncaught_exception()) 
-	  ix.useTag('/'+tag);
-      }
-    };
-
-    ///\e
-  
-    ///\e
-    ///
-    XmlReader(std::istream& _is) : is(_is), line_number(1) {}
-  
-    int operator()(const std::string &tag,int &v)
-    { 
-      Tag t(*this,tag);
-      skipWhiteSpaces();
-      if(!(is >> v)) throw DataFormatError("",line_number,"Not an 'int'");
-      return v;
-    }
-    double operator()(const std::string &tag,double &v) 
-    {
-      Tag t(*this,tag);
-      skipWhiteSpaces();
-      if(!(is >> v))
-	throw DataFormatError("",line_number,"Not a 'double'");
-      return v;
-    }
-    std::string &operator()(const std::string &tag,std::string &v)
-    {
-      Tag t(*this,tag);
-      v.clear();
-      char c;
-      while (is.get(c) && c!='<')
-	if(c=='\\')
-	  if(!is.get(c))
-	    throw DataFormatError("",line_number,"Bad string");
-	  else switch(c) {
-	  case 'n':
-	    v.push_back('\n');
-	    break;
-	  default:
-	    v.push_back(c);
-	    break;
-	  }
-	else {
-	  if(c=='\n') line_number++;
-	  v.push_back(c);
-	}
-      if(c!='<')
-	throw DataFormatError("",line_number,"Unexpected eof");
-      is.unget();
-      return v;
-    }
-    ///\e
-  
-    ///\e
-    ///
-    template<class V>
-    V &operator()(const std::string &tag,V &v)
-    {
-      Tag t(*this,tag);
-      in(*this,v);
-      return v;
-    }
-    ///\e
-  
-    ///\e
-    ///
-    template<class V>
-    V &operator()(V &v)
-    {
-      in(*this,v);
-      return v;
-    }
-    ///\e
-  
-    ///\e
-    ///
-    template<class V>
-    V load(const std::string &tag)
-    {
-      Tag t(*this,tag);
-      V v;
-      (*this)(tag,v);
-      return v;
-    }
-  };
-
   //////////////////////////////////////////////////////////////////////
 
+  ///\e
+  
+  ///\relates XmlIo
+  ///
   template<class A>
-  void out(XmlWriter &x,const std::auto_ptr<A> &v)
-  {
+  void xml(XmlIo &x,std::auto_ptr<A> &v)
+  {
+    if(x.write()) v=new A;
     x(*v);
   }
-
-  template<class A>
-  void in(XmlReader &x,std::auto_ptr<A> &v)
-  {
-    v=new A;
-    x(*v);
-  }
-
-  //////////////////////////////
-
+  
+  ///\e
+  
+  ///\relates XmlIo
+  ///
   template<class A,class B>
-  void out(XmlWriter &x,const std::pair<A,B> &v)
+  void xml(XmlIo &x,std::pair<A,B> &v)
   {
     x("first",v.first);
@@ -363,24 +354,15 @@
   }
 
-  template<class A,class B>
-  void in(XmlReader &x,std::pair<A,B> &v)
-  {
-    x("first",v.first);
-    x("second",v.second);
-  }
-
-  //////////////////////////////
-
+  ///\e
+  
+  ///\relates XmlIo
+  ///
   template<class T>
-  void out(XmlWriter &x,const std::list<T> &v)
-  {
-    for(typename std::list<T>::const_iterator it=v.begin();
-	it!=v.end();++it) x("item",*it);
-  }
-
-  template<class T>
-  void in(XmlReader &x,std::list<T> &v)
-  {
-    while(x.nextTag()=="item")
+  void xml(XmlIo &x,std::list<T> &v)
+  {
+    if(x.write())
+      for(typename std::list<T>::const_iterator it=v.begin();
+	  it!=v.end();++it) x("item",*it);
+    else while(x.nextTag()=="item")
       {
 	v.push_back(T());
@@ -388,18 +370,15 @@
       }
   }
-
-  //////////////////////////////
-
+  ///\e
+  
+  ///\relates XmlIo
+  ///
   template<class T>
-  void out(XmlWriter &x,const std::vector<T> &v)
-  {
-    for(typename std::vector<T>::const_iterator it=v.begin();
-	it!=v.end();++it) x("item",*it);
-  }
-
-  template<class T>
-  void in(XmlReader &x,std::vector<T> &v)
-  {
-    while(x.nextTag()=="item")
+  void xml(XmlIo &x,std::vector<T> &v)
+  {
+    if(x.write())
+      for(typename std::vector<T>::const_iterator it=v.begin();
+	  it!=v.end();++it) x("item",*it);
+    else while(x.nextTag()=="item")
       {
 	v.push_back(T());
@@ -408,17 +387,15 @@
   }
 
-  //////////////////////////////
-
+  ///\e
+  
+  ///\relates XmlIo
+  ///
   template<class K,class V>
-  void out(XmlWriter &x,const std::map<K,V> &v)
-  {
-    for(typename std::map<K,V>::const_iterator it=v.begin();
-	it!=v.end();++it) x("item",*it);
-  }
-
-  template<class K,class V>
-  void in(XmlReader &x,std::map<K,V> &v)
-  {
-    while(x.nextTag()=="item")
+  void xml(XmlIo &x,std::map<K,V> &v)
+  {
+    if(x.write()) 
+      for(typename std::map<K,V>::const_iterator it=v.begin();
+	  it!=v.end();++it) x("item",*it);
+    else while(x.nextTag()=="item")
       {
 	typename std::map<K,V>::value_type it;
@@ -428,43 +405,38 @@
   }
 
-  //////////////////////////////
-
+  ///\e
+  
+  ///\relates XmlIo
+  ///
   template<class T>
-  void out(XmlWriter &x,const lemon::xy<T> &v)
-  {
-    //   x("x",v.x);
-    //   x("y",v.y);
-    { XmlWriter::LineTag t(x,"x"); x(v.x); }
-    { XmlWriter::ContTag t(x,"y"); x(v.y); }
-  }
-
+  void xml(XmlIo &x,lemon::xy<T> &v)
+  {
+    { XmlIo::LineTag t(x,"x"); x(v.x); }
+    { XmlIo::ContTag t(x,"y"); x(v.y); }
+  }
+
+  ///\e
+  
+  ///\relates XmlIo
+  ///
   template<class T>
-  void in(XmlReader &x,lemon::xy<T> &v)
-  {
-    x("x",v.x);
-    x("y",v.y);
-  }
-
-  //////////////////////////////
-
-  template<class T>
-  void out(XmlWriter &x,const lemon::BoundingBox<T> &v)
-  {
-    if(!v.empty()) {
-      x("point",v.bottomLeft());
-      if(v.bottomLeft()!=v.topRight()) x("point",v.topRight());
-    }
-  }
-
-  template<class T>
-  void in(XmlReader &x,lemon::BoundingBox<T> &v)
-  {
-    v.clear();
-    while(x.nextTag()=="point") {
-      lemon::xy<T> co;
-      x("point",co);
-      v.add(co);
+  void xml(XmlIo &x,lemon::BoundingBox<T> &v)
+  {
+    if(x.write()) {
+      if(!v.empty()) {
+	x("point",v.bottomLeft());
+	if(v.bottomLeft()!=v.topRight()) x("point",v.topRight());
+      }
+    }
+    else {
+      v.clear();
+      while(x.nextTag()=="point") {
+	lemon::xy<T> co;
+	x("point",co);
+	v.add(co);
+      }
     }
   }
   
 }
+
