/** \author Bartosz Papis, gr. C1ISI, nr 193332
 *  \date   2008-04-01
 *  \file   classAdapterPattern.cpp
 *  \brief  Przykład zastosowania adaptera klas. (zad. 7)
 */
/*  Przykład ilustrujący zastosowanie adaptera klas.
 *  Typ std::list<XmlElement*> jest adaptowany do interfejsu
 *  XmlContentHandler, co umożliwia wczytywanie danych w formacie XML
 *  do listy.
 *
 *    +-----------+
 *    | XmlParser |
 *    +-----------+
 *         |  1
 *         |
 *         [] 1
 *  +-------------------+       +------------------------+
 *  | XmlContentHandler |       | std::list<XmlElement*> |
 *  +-------------------+       +------------------------+
 *          /\                             /\
 *          |                              .
 *          |                              .
 *          \------------------------------/
 *                         |
 *             +-------------------------+
 *             | ListAsXmlContentHandler |
 *             +-------------------------+
 */

#include <list>
#include <iostream>
#include <sstream>

class XmlElement;

/** Klasa definiująca interfejs, który należy zrealizować,
 *  aby móc otrzymywać informacje o kolejnych napotkanych przy
 *  parsowaniu tagach XMLowych.
 */
class XmlContentHandler
{
public:
  /** Metoda wywoływana przez parser dla każdego
   *  napotkanego taga. Przejmuje \a element na własność.
   *
   * \param element napotkany element
   */
  virtual void handleElement(XmlElement* element) = 0;
};

/** Adapter klasy std::list<XmlElement*> do interfejsu XmlContentHandler.
 */
class ListAsXmlContentHandler : public XmlContentHandler,
                                private std::list<XmlElement*>
{
private:
  typedef std::list<XmlElement*> Adapted;

public:
  typedef Adapted::iterator Iterator;
  typedef Adapted::const_iterator ConstIterator;

  ~ListAsXmlContentHandler();

  /** Patrz: XmlContentHandler::handleElement */
  void handleElement(XmlElement* element);

  Iterator begin() { return this->Adapted::begin(); }
  ConstIterator begin() const { return this->Adapted::begin(); }
  Iterator end() { return this->Adapted::end(); }
  ConstIterator end() const { return this->Adapted::end(); }
};

/** Uproszczona na potrzeby przykładu klasa przechowująca informacje o
 *  pojedyńczym fragmencie danych w formacie XML.
 */
class XmlElement
{
public:
  XmlElement(const std::string& name)
    :_name(name)
  {}
  virtual std::string toString() const
  {  return std::string("<") + _name + ">"; }

protected:
  std::string _name;
};

/** Uproszczona na potrzeby przykładu klasa parsująca strumień
 *  danych w formacie XML.
 */
class XmlParser
{
public:
  /** Tworzy instancję parsera, działającego na danym strumieniu.
   *  Nie przejmuje \a inputStream na własność.
   *
   * \param inputStream strumień zawierający dane do przetworzenia
   */
  XmlParser(std::istream* inputStream);

  /** Rozpoczyna parsowanie strumienia danych.
   *  Nie przejmuja \a handler na własność.
   *
   * \param handler obiekt powiadamiany o kolejnych napotkanych tagach.
   */
  virtual void parse(XmlContentHandler* handler);

private:
  virtual void processTag(XmlContentHandler* handler,
                          const std::string& tag);

  std::istream* _stream;
};

/******************************/
/**          Main            **/
/******************************/
int main()
{
  /* Strumien z przykladowymi danymi */
  std::istringstream stream("<tag1><tag2 attr=\"val1\"></tag2></tag1>");

  XmlParser parser(&stream);

  /* Utworzenie obiektu klasy std::list<XmlElement*>
   * zaadaptowanej do interfejsu XmlContentHandler
   */
  ListAsXmlContentHandler* handler = new ListAsXmlContentHandler();
  /* Przetworzenie danych:
   * dla każdego tagu XMLowego zawoła handler->handleElement
   */
  parser.parse(handler);

  std::cout << "Wczytane dane:" << std::endl;

  for (ListAsXmlContentHandler::Iterator i = handler->begin();
       i != handler->end();
       ++i)
    {
      std::cout << (*i)->toString() << std::endl;
    }

  delete handler;
  return 0;
}

/*** ListAsXmlContentHandler ***/

ListAsXmlContentHandler::~ListAsXmlContentHandler()
{
  for (Iterator i = begin();i != end();++i)
    delete *i;
}

void ListAsXmlContentHandler::handleElement(XmlElement* element)
{
  this->Adapted::push_back(element);
}

/*** XmlParser ***/

XmlParser::XmlParser(std::istream* inputStream)
  :_stream(inputStream)
{}

void XmlParser::parse(XmlContentHandler* handler)
{
  std::string buffer;

  getline(*_stream, buffer, '<');

  while (_stream->good())
    {
      getline(*_stream, buffer, '>');
      processTag(handler, buffer);
      getline(*_stream, buffer, '<');
    }
}

void XmlParser::processTag(XmlContentHandler* handler,
                           const std::string& tag)
{
  std::string name;
  ::size_t position = tag.find(' ');

  if (position == std::string::npos)
    {
      if (tag[0] == '/')
        name = tag.substr(1, tag.length() - 1);
      else
        name = tag;
    }
  else
    name = tag.substr(0, position);

  handler->handleElement(new XmlElement(name));
}
