/** @file

  A brief file description

  @section license License

  Licensed to the Apache Software Foundation (ASF) under one
  or more contributor license agreements.  See the NOTICE file
  distributed with this work for additional information
  regarding copyright ownership.  The ASF licenses this file
  to you under the Apache License, Version 2.0 (the
  "License"); you may not use this file except in compliance
  with the License.  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
 */



#ifndef INK_XML_H
#define INK_XML_H

#include "List.h"

/*-------------------------------------------------------------------------
  InkXml.h

  This file defines the interface for parsing XML-style config files.  Such
  a file contains various XML objects, each with tags (or attibutes).  The
  parser's job is to build a database of these objects that can then be
  queried later to extract the desired information.
  -------------------------------------------------------------------------*/

/*-------------------------------------------------------------------------
  InkXmlAttr
  -------------------------------------------------------------------------*/

class InkXmlAttr
{
public:
  InkXmlAttr(const char *tag, const char *value);
  ~InkXmlAttr();

  char *tag() const
  {
    return m_tag;
  }
  char *value() const
  {
    return m_value;
  }
  void display(FILE * fd = stdout);

private:
  char *m_tag;
  char *m_value;

public:
  LINK(InkXmlAttr, link);

private:
  // -- member functions that are not allowed --
  InkXmlAttr();
  InkXmlAttr(const InkXmlAttr &);
  InkXmlAttr & operator=(InkXmlAttr &);
};

/*-------------------------------------------------------------------------
  InkXmlObject

  An XmlObject has a name and a set of <tag,value> attributes.
  -------------------------------------------------------------------------*/

class InkXmlObject
{
public:
  InkXmlObject(const char *object_name, bool dup_attrs_allowed = true);
  ~InkXmlObject();

  void clear_tags();
  int add_tag(const char *tag, const char *value);
  int add_attr(InkXmlAttr * attr);
  char *tag_value(const char *tag_name);
  void display(FILE * fd = stdout);

  char *object_name() const
  {
    return m_object_name;
  }
  InkXmlAttr *first() const
  {
    return m_tags.head;
  }
  InkXmlAttr *next(InkXmlAttr * here) const
  {
    return (here->link).next;
  }

private:
  char *m_object_name;
  bool m_dup_attrs_allowed;
  Queue<InkXmlAttr> m_tags;

public:
  LINK(InkXmlObject, link);

private:
  InkXmlObject * get_next_xml_object(int fd);

private:
  // -- member functions that are not allowed --
  InkXmlObject();
  InkXmlObject(const InkXmlObject &);
  InkXmlObject & operator=(InkXmlObject &);
};

/*-------------------------------------------------------------------------
  InkXmlConfigFile

  This object is used to parse an XML-style config file and create a
  database of resulting XML objects.
  -------------------------------------------------------------------------*/

class InkXmlConfigFile
{

public:
  InkXmlConfigFile(const char *config_file);
  ~InkXmlConfigFile();

  void clear_objects();
  /*                                                                     */
  int parse(int fd);
  /*                                                                     */
  int parse();
  void add_object(InkXmlObject * object);
  InkXmlObject *find_object(const char *object_name);
  void display(FILE * fd = stdout);

  InkXmlObject *first()
  {
    return m_objects.head;
  }
  InkXmlObject *next(InkXmlObject * here)
  {
    return (here->link).next;
  }

private:
  InkXmlObject * get_next_xml_object(int fd);
  InkXmlObject *parse_error();
  InkXmlObject *scan_object(int fd, char token);
  InkXmlAttr *scan_attr(int fd, const char *ident);
  char next_token(int fd, bool eat_whitespace = true);
  char scan_comment(int fd);

private:
  char *m_config_file;
  unsigned m_line;
  unsigned m_col;
  Queue<InkXmlObject> m_objects;

private:
  // -- member functions that are not allowed --
  InkXmlConfigFile();
  InkXmlConfigFile(const InkXmlConfigFile &);
  InkXmlConfigFile & operator=(InkXmlConfigFile &);
};

class NameList
{
private:
  struct ListElem
  {
    char *m_name;
    LINK(ListElem, link);

    ListElem(char *name):m_name(name)
    { }
  };

public:
  NameList():m_count(0) {
  }
  ~NameList() {
    clear();
  }

  void enqueue(char *name)
  {
    ListElem *e = new ListElem(name);

    m_list.enqueue(e);
    m_count++;
  }

  char *dequeue()
  {
    char *ret = NULL;
    ListElem *e = m_list.dequeue();

    if (e) {
      ret = e->m_name;
      delete e;
      m_count--;
    }
    return ret;
  }

  void clear()
  {
    ListElem *e;

    while ((e = m_list.dequeue()) != NULL) {
      delete e;
    }
    m_count = 0;
  }

  unsigned count()
  {
    return m_count;
  }

private:
  Queue<ListElem> m_list;
  unsigned m_count;
};

#endif /* INK_XML_H */
