//===--- DeclXML.cpp - XML implementation for Decl ASTs -------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements the XML document class, which provides the means to
// dump out the AST in a XML form that exposes type details and other fields.
//
//===----------------------------------------------------------------------===//

#include "clang/Frontend/DocumentXML.h"
#include "clang/AST/DeclVisitor.h"
#include "clang/AST/Expr.h"

namespace clang {

//---------------------------------------------------------
class DocumentXML::DeclPrinter : public DeclVisitor<DocumentXML::DeclPrinter> {
  DocumentXML& Doc;

  void addSubNodes(FunctionDecl* FD) {
    for (unsigned i = 0, e = FD->getNumParams(); i != e; ++i) {
      Visit(FD->getParamDecl(i));
      Doc.toParent();
    }
  }

  void addFunctionBody(FunctionDecl* FD) {
    if (FD->isThisDeclarationADefinition()) {
      Doc.addSubNode("Body");
      Doc.PrintStmt(FD->getBody());
      Doc.toParent();
    }
  }

  void addSubNodes(RecordDecl* RD) {
    for (RecordDecl::field_iterator i = RD->field_begin(),
                                    e = RD->field_end(); i != e; ++i) {
      Visit(*i);
      Doc.toParent();
    }
  }

  void addSubNodes(CXXRecordDecl* RD) {
    addSubNodes(cast<RecordDecl>(RD));

    if (RD->isDefinition()) {
      // FIXME: This breaks XML generation
      //Doc.addAttribute("num_bases", RD->getNumBases());

      for (CXXRecordDecl::base_class_iterator 
             base = RD->bases_begin(),
             bend = RD->bases_end();
           base != bend;
           ++base) {
        Doc.addSubNode("Base");
        Doc.addAttribute("id", base->getType());
        AccessSpecifier as = base->getAccessSpecifierAsWritten();
        const char* as_name = "";
        switch(as) {
        case AS_none:      as_name = ""; break;
        case AS_public:    as_name = "public"; break;
        case AS_protected: as_name = "protected"; break;
        case AS_private:   as_name = "private"; break;
        }
        Doc.addAttributeOptional("access", as_name);
        Doc.addAttribute("is_virtual", base->isVirtual());
        Doc.toParent();
      }

      for (CXXRecordDecl::method_iterator i = RD->method_begin(),
             e = RD->method_end(); i != e; ++i) {
        Visit(*i);
        Doc.toParent();
      }

    }

  }

  void addSubNodes(EnumDecl* ED) {
    for (EnumDecl::enumerator_iterator i = ED->enumerator_begin(),
                                       e = ED->enumerator_end(); i != e; ++i) {
      Visit(*i);
      Doc.toParent();
    }
  }

  void addSubNodes(EnumConstantDecl* ECD) {
    if (ECD->getInitExpr())
      Doc.PrintStmt(ECD->getInitExpr());
  }

  void addSubNodes(FieldDecl* FdD)  {
    if (FdD->isBitField())
      Doc.PrintStmt(FdD->getBitWidth());
  }

  void addSubNodes(VarDecl* V) {
    if (V->getInit())
      Doc.PrintStmt(V->getInit());
  }

  void addSubNodes(ParmVarDecl* argDecl) {
    if (argDecl->getDefaultArg())
      Doc.PrintStmt(argDecl->getDefaultArg());
  }

  void addSubNodes(NamespaceDecl* ns) {

    for (DeclContext::decl_iterator 
           d    = ns->decls_begin(), 
           dend = ns->decls_end();
         d != dend;
         ++d) {
      Visit(*d);
      Doc.toParent();
    }
  }

  void addSpecialAttribute(const char* pName, EnumDecl* ED) {
    const QualType& enumType = ED->getIntegerType();
    if (!enumType.isNull())
      Doc.addAttribute(pName, enumType);
  }

  void addIdAttribute(LinkageSpecDecl* ED) {
    Doc.addAttribute("id", ED);
  }

  void addIdAttribute(NamedDecl* ND) {
    Doc.addAttribute("id", ND);
  }

public:
  DeclPrinter(DocumentXML& doc) : Doc(doc) {}

#define NODE_XML( CLASS, NAME )          \
  void Visit##CLASS(CLASS* T)            \
  {                                      \
    Doc.addSubNode(NAME);

#define ID_ATTRIBUTE_XML                  addIdAttribute(T);
#define ATTRIBUTE_XML( FN, NAME )         Doc.addAttribute(NAME, T->FN);
#define ATTRIBUTE_OPT_XML( FN, NAME )     Doc.addAttributeOptional(NAME, T->FN);
#define ATTRIBUTE_FILE_LOCATION_XML       Doc.addLocation(T->getLocation());
#define ATTRIBUTE_SPECIAL_XML( FN, NAME ) addSpecialAttribute(NAME, T);

#define ATTRIBUTE_ENUM_XML( FN, NAME )  \
  {                                     \
    const char* pAttributeName = NAME;  \
    const bool optional = false;             \
    switch (T->FN) {                    \
    default: assert(0 && "unknown enum value");

#define ATTRIBUTE_ENUM_OPT_XML( FN, NAME )  \
  {                                     \
    const char* pAttributeName = NAME;  \
    const bool optional = true;              \
    switch (T->FN) {                    \
    default: assert(0 && "unknown enum value");

#define ENUM_XML( VALUE, NAME )         case VALUE: if ((!optional) || NAME[0]) Doc.addAttribute(pAttributeName, NAME); break;
#define END_ENUM_XML                    } }
#define END_NODE_XML                    }

#define SUB_NODE_XML( CLASS )           addSubNodes(T);
#define SUB_NODE_SEQUENCE_XML( CLASS )  addSubNodes(T);
#define SUB_NODE_OPT_XML( CLASS )       addSubNodes(T);

#define SUB_NODE_FN_BODY_XML            addFunctionBody(T);

#include "clang/Frontend/DeclXML.def"
};


//---------------------------------------------------------
void DocumentXML::writeDeclToXML(Decl *D) {
  DeclPrinter(*this).Visit(D);
  toParent();
}

//---------------------------------------------------------
} // NS clang