#include "generator.h"

namespace {

  void write_occurrence(const aka::occurrence &occ, std::ostream &ostm) {
    ostm << occ.minOccurs_ << ", ";
    if (occ.maxOccurs_ == aka::unbounded)
      ostm << "aka::unbounded";
    else
      ostm << occ.maxOccurs_;
  }

  std::string tagname(const element_type &et) {
    if (et.is_group_)
      return std::string("\"&") + et.name_.local() + "\"";
    return std::string("\"") + et.name_.qualified() + "\"";
  }
  std::string tagname(const attribute_type &et) {
    return std::string("\"") + et.name_.qualified() + "\"";
  }

  std::string quote(const aka::qname &value) {
    return std::string("\"") + value.qualified() + "\"";
  }
  std::string quote(const std::string &value) {
    return std::string("\"") + value + "\"";
  }
}

std::string generator::cppname(const aka::qname &name) const {

  aka::qname tyname = registry_->rename_type(name);
  std::string localname = registry_->escape(tyname.local());
  if (tyname.is_qualified())
    return std::string("::") + tyname.prefix() + "::" + localname;
  if (registry_->is_builtin(name))
    return localname;
  else
    return std::string("::") + localname;
}

std::string generator::declname(const aka::qname &name) const {
  aka::qname tyname = registry_->rename_type(name);
  return registry_->escape(tyname.local());
}

std::string generator::leafdecl(const aka::qname &name) const {
  aka::qname tyname = registry_->rename_type(name);
  return registry_->escape(tyname.local()) + "_leaf";
}

std::string generator::leafname(const aka::qname &type) const {
  if (registry_->is_predefined(type)) {
    return registry_->get_predefined_leafname(type);
  }
  else {
    aka::qname tyname = registry_->rename_type(type);
    std::string localname = registry_->escape(tyname.local());
    if (tyname.is_qualified())
      return std::string("::") + tyname.prefix() + "::" + localname + "_leaf";
    return localname + "_leaf";
  }
}

std::string generator::membername(const aka::qname &name) const {
  return registry_->escape(name.local()) + "_";
}

void generator::begin_model_method(const aka::qname &name) {
  ximpl_ << "  void " << leafdecl(name) << "::model() {" << std::endl;
}

void generator::end_model_method() {
  ximpl_ << "  }" << std::endl
	 << std::endl;
}



void generator::generate(registry &reg) {
  registry_ = &reg;

  el_.open(std::string(dirpath_ + "element.h").c_str());
  xiso_.open(std::string(dirpath_ + "xiso.h").c_str());
  ximpl_.open(std::string(dirpath_ + "xiso.cpp").c_str());

  write_header(el_, "element.h");
  el_ << "#include <akaxiso/schema/schema_builtin.h>" << std::endl
      << std::endl;

  write_header(xiso_, "xiso.h");
  xiso_ << "#include " << quote("element.h") << std::endl
	<< "#include <akaxiso/content_model.h>" << std::endl
	<< std::endl;

  ximpl_ << "#include " << quote("xiso.h") << std::endl
	 << "#include <akaxiso/akaxiso.h>" << std::endl
	 << "#include <akaxiso/schema/schema_builtin_xiso.h>" << std::endl
	 << std::endl;

  try {
    qname_set resolved;
    reg.get_predefinedTypes(resolved);
    
    for (std::vector<aka::id_type>::const_iterator it = reg.ns_order_.begin();
	 it != reg.ns_order_.end(); ++it) {
      aka::id_type ns_id = *it;

      qname_set ns_toplevels;
      qname_set ns_simpleTypes;
      for (qname_set::const_iterator nit = reg.toplevels_.begin(); 
	   nit != reg.toplevels_.end(); ++nit) {
	if (nit->get_uri_id() != ns_id)
	  continue;
	if (registry_->is_predefined(*nit))
	  continue;

	class_def *def = registry_->classes_.get(*nit);
	if (def->id_ == simpletype_id)
	  ns_simpleTypes.insert(*nit);
	else
	  ns_toplevels.insert(*nit);
      }
      generate_one_ns(resolved, ns_id, ns_simpleTypes, ns_toplevels);
    }
  }
  catch ( ... ) {
    el_.close();
    xiso_.close();
    ximpl_.close();
    throw;
  }
  write_footer(el_, "element.h");
  write_footer(xiso_, "xiso.h");

  el_.close();
  xiso_.close();
  ximpl_.close();
}

void generator::write_leaf_decl(const class_def &def, const std::string &leaftmp) {
  xiso_ << "  struct " << leafdecl(def.name_) 
	<< " : aka::" << leaftmp << "< " << cppname(def.name_)
	<< ", " << leafdecl(def.name_) << "> {" << std::endl
	<< "    void model();" << std::endl
	<< "  };" << std::endl
	<< std::endl;
}


void generator::write_member_decls(const element_types &etypes) {

  if (!etypes.empty())
    el_ << "    /** members */" << std::endl;
  for (element_types::const_iterator eit = etypes.begin();
       eit != etypes.end(); ++eit) {
    if (eit->ptrmember_)
      el_ << "    aka::deep_ptr< " << cppname(eit->type_) << "> " << membername(eit->name_) 
	  << ";" << std::endl;
    else if (!eit->is_fixed_)
      el_ << "    " << cppname(eit->type_) << " " << membername(eit->name_) << ";" << std::endl;
  }
}

void generator::write_attribute_decls(const attribute_defs &attrs) {

  if (attrs.has_attributes())
    el_ << "    /** attributes */" << std::endl;
  for (attribute_types::const_iterator ait = attrs.attributes_.begin();
       ait != attrs.attributes_.end(); ++ait) {
    if (!ait->is_fixed_)
      el_ << "    " << cppname(ait->type_) << " " << membername(ait->name_) << ";" << std::endl;
  }
  if (attrs.has_anyAttribute_) {
    el_ << "    aka::any_attributes attributes_;" << std::endl;
  }
}

void generator::write_member_leaves(const aka::qname &name, const element_types &etypes) {

  if (etypes.empty())
    return;

  ximpl_ << "  /** element member definitions */" << std::endl;

  for (element_types::const_iterator eit = etypes.begin();
       eit != etypes.end(); ++eit) {
    if (eit->is_fixed_) {
      ximpl_ << "    fixed_member(" << tagname(*eit) << ", " 
	     << quote(eit->default_) << ", " << leafname(eit->type_) << ");";
      continue;
    }

    // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    if (eit->type_ == aka::qname("aka:any")) {
      ximpl_ << "    any(" << tagname(*eit) << ", &"
	     << cppname(name) << "::" << membername(eit->name_) << ");" << std::endl;
      continue;
    }
    if (eit->type_ == aka::qname("aka:any_array")) {
      ximpl_ << "    any(" << tagname(*eit) << ", &"
	     << cppname(name) << "::" << membername(eit->name_) << ", ";
      write_occurrence(eit->occ_, ximpl_);
      ximpl_ << ");" << std::endl;
      continue;
    }
    if (registry_->is_predefined(eit->type_)) {
      ximpl_ << "    member(" << tagname(*eit) << ", &"
	     << cppname(name) << "::" << membername(eit->name_) << ", "
	     << leafname(eit->type_) << "());" << std::endl;
      continue;
    }

    if (eit->ptrmember_) {
      ximpl_ << "    ptrmember(" << tagname(*eit) << ", &"
	     << cppname(name) << "::" << membername(eit->name_) << ", "
	     << leafname(eit->type_)
	     << "());" << std::endl;
      continue;
    }
    ximpl_ << "    member(" << tagname(*eit) << ", &"
	   << cppname(name) << "::" << membername(eit->name_) << ", "
	   << leafname(eit->type_) << "()";
    if (eit->is_array()) {
      ximpl_ << ", ";
      write_occurrence(eit->occ_, ximpl_);
    }
    ximpl_ << ");" << std::endl;
  }
}


void generator::write_item_leaves(const aka::qname &name, const element_types &etypes) {

  if (etypes.empty())
    return;

  ximpl_ << "  /** element member definitions */" << std::endl;

  for (element_types::const_iterator eit = etypes.begin();
       eit != etypes.end(); ++eit) {
    if (eit->is_fixed_) {
      ximpl_ << "    fixed_item(" << tagname(*eit) << ", " 
	     << quote(eit->default_) << ", " << leafname(eit->type_) << ");";
      continue;
    }

    if (registry_->is_any(eit->type_)) {
      ximpl_ << "    any(" << tagname(*eit);
      if (eit->is_array()) {
	ximpl_ << ", ";
	write_occurrence(eit->occ_, ximpl_);
      }
      ximpl_ << ");" << std::endl;
      continue;
    }

    if (registry_->is_predefined(eit->type_)) {
      ximpl_ << "    item(" << tagname(*eit) << ", "
	     << leafname(eit->type_) << "());" << std::endl;
      continue;
    }

    const class_def *vdef = registry_->classes_.get(eit->type_);
    ximpl_ << "    item(" << tagname(*eit) << ", "
	   << leafname(eit->type_) << "()";
    if (vdef->id_ == array_id) {
      ximpl_ << ", ";
      write_occurrence(eit->occ_, ximpl_);
    }
    ximpl_ << ");" << std::endl;
  }
}

void generator::write_attribute_leaves(const aka::qname &name, const attribute_defs &attrs) {

  if (!attrs.has_attributes())
    return;
  
  ximpl_ << "    /** attribute member definition. */" << std::endl;

  for (attribute_types::const_iterator ait = attrs.attributes_.begin();
       ait != attrs.attributes_.end(); ++ait) {
    if (ait->is_fixed_) {
      ximpl_ << "    fixed_attribute(" << tagname(*ait) << ", " 
	     << quote(ait->default_) << ", "
	     << leafname(ait->type_) << "())";
      if (ait->required_)
	ximpl_ << ".required(true)";
      ximpl_ << ";";
      continue;
    }
    ximpl_ << "    attribute(" << tagname(*ait) << ", &" 
	   << cppname(name) << "::" << membername(ait->name_) << ", "
	   << leafname(ait->type_) << "())";
    if (!ait->default_.empty()) {
      ximpl_ << ".set_default(" << quote(ait->default_) << ")";
      assert(!ait->required_);
    }
    if (ait->required_)
      ximpl_ << ".required(true)";

    ximpl_ << ";" << std::endl;
  }
  if (attrs.has_anyAttribute_)
    ximpl_ << "    any_attribute(&" << cppname(name) << "::attributes_);" << std::endl;
}


void generator::generate_sequence(const class_def &def, const qname_set &resolved) {
  write_ptrmember_decl(def.etypes_, resolved);
  el_ << "  struct " << declname(def.name_.local()) << " {" << std::endl;
  write_member_decls(def.etypes_);
  write_attribute_decls(def.adefs_);
  el_ << "  };" << std::endl
      << std::endl;

  write_leaf_decl(def, "sequence");

  begin_model_method(def.name_);
  write_member_leaves(def.name_, def.etypes_);
  write_attribute_leaves(def.name_, def.adefs_);
  end_model_method();
}


void generator::generate_all(const class_def &def, const qname_set &resolved) {
  write_ptrmember_decl(def.etypes_, resolved);
  el_ << "  struct " << declname(def.name_.local()) << " {" << std::endl;
  write_member_decls(def.etypes_);
  write_attribute_decls(def.adefs_);
  el_ << "  };" << std::endl
      << std::endl;

  write_leaf_decl(def, "all");

  begin_model_method(def.name_);
  write_member_leaves(def.name_, def.etypes_);
  write_attribute_leaves(def.name_, def.adefs_);
  end_model_method();
}


void generator::generate_choice(const class_def &def, const qname_set &resolved) {
  // element.h
  std::ostringstream forwarded;

//   for (element_types::const_iterator eit = def.etypes_.begin();
//        eit != def.etypes_.end(); ++eit) {
//     if (resolved.find(eit->type_) == resolved.end())
//       forwarded << "  struct " << eit->type_ << ";" << std::endl;
//   }

//   if (!forwarded.rdbuf()->str().empty()) {
//     el_ << "  /** Forwarded decl. */" << std::endl;
//     el_ << forwarded.rdbuf()->str() << std::endl;
//   }


  el_ << "  typedef std::list<aka::item> " << declname(def.name_) << ';' << std::endl;

  write_leaf_decl(def, "sequential_choice");

  begin_model_method(def.name_);
  write_item_leaves(def.name_, def.etypes_);
  end_model_method();
}


void generator::generate_simplecontent(const class_def &def) {

  el_ << "  struct " << declname(def.name_.local()) << " {" << std::endl;
  el_ << "    " << cppname(def.value_type_) << " value_;" << std::endl; 
  write_attribute_decls(def.adefs_);
  el_ << "  };" << std::endl
      << std::endl;

  write_leaf_decl(def, "simplecontent");

  begin_model_method(def.name_);

  ximpl_ << "    value(&" << cppname(def.name_) << "::value_);" << std::endl;
  // write default !!!!!

  write_attribute_leaves(def.name_, def.adefs_);
  end_model_method();
  

}



void generator::generate_array(const class_def &def, const qname_set &resolved) {
  if (resolved.find(def.value_type_) == resolved.end()) {
    el_ << "  /** forwarded declartion */" << std::endl
	<< "  struct " << def.value_type_.local() << ";" << std::endl;
  }
  el_ << "  typedef std::list< " << cppname(def.value_type_) << "> " 
      << declname(def.name_) << ";" << std::endl
      << std::endl;

  // Forwareded declaration of value leaf type.
  if (xisodecl_generated_.find(def.value_type_) == xisodecl_generated_.end()) {
    xiso_ << "  struct " << declname(def.value_type_) << ";" << std::endl;
  }

  xiso_ << "  typedef aka::sequential_array< " << cppname(def.name_)
	<< ", " << leafname(def.value_type_) << "> " 
	<< leafdecl(def.name_) << ";" << std::endl
	<< std::endl;
}

void generator::generate_simpletype(const class_def &def) {
  el_ << "  typedef " << cppname(def.value_type_) << " " 
      << declname(def.name_.local()) << ";" << std::endl
      << std::endl;

  if (!registry_->is_predefined(def.name_)) {
    xiso_ << "  typedef xiso::leaf< " << cppname(def.value_type_) << "> " 
	  << leafdecl(def.name_) << ";" << std::endl; 
  }
}


std::string generator::escape_filename(const std::string &fn) {
  std::string escaped;
  for (std::string::size_type pos = 0; pos < fn.size(); ++pos) {
    std::string::value_type ch = fn[pos];
    if (ch == '.')
      ch = '_';
    escaped += ch;
  }
  return escaped;
}


void generator::write_header(std::ostream &ostm, const std::string &basename) {

  std::string escaped = escape_filename(basename);

  ostm << "/* -*- c++ -*- */" << std::endl
       << "#ifndef " << escaped << "__" << std::endl
       << "#define " << escaped << "__" << std::endl
       << std::endl;
}


void generator::write_footer(std::ostream &ostm, const std::string &basename) {

  std::string escaped = escape_filename(basename);
  ostm << "#endif // #ifndef " << escaped << "__" << std::endl;
}


void generator::generate_one_ns(qname_set &resolved, aka::id_type ns_id,
				qname_set &simpleTypes, qname_set &toplevels) {

  if (ns_id != aka::empty_token) { // no-namespace
    el_ << "namespace " << aka::get_prefix(ns_id) << " {" << std::endl
	<< std::endl;
    xiso_ << "namespace " << aka::get_prefix(ns_id) << " {" << std::endl
	  << std::endl;
    ximpl_ << "namespace " << aka::get_prefix(ns_id) << " {" << std::endl
	   << std::endl;
  }    

  el_ << "  void instantiate_xiso();" << std::endl
      << std::endl;

  if (!simpleTypes.empty())
    el_ << "  /** simpleType definitions */" << std::endl
	<< std::endl;

  while (!simpleTypes.empty()) {
    qname_set::iterator it = simpleTypes.begin();
    for (; it != simpleTypes.end(); ++it) {
      if (resolved.find(*it) != resolved.end()) {
	simpleTypes.erase(it);
	break;
      }
      if (generate_type(resolved, *it, toplevels)) {
	resolved.insert(*it);
	simpleTypes.erase(it);
	break;
      }
    }
    if (it == simpleTypes.end())
      throw fatal_error();
  }  

  if (!toplevels.empty())
    el_ << "  /** complexType definitions */" << std::endl
	<< std::endl;

  while (!toplevels.empty()) {
    qname_set::iterator it = toplevels.begin();
    for (; it != toplevels.end(); ++it) {
      if (resolved.find(*it) != resolved.end()) {
	toplevels.erase(it); // already resolved.
	break;
      }
//       std::cerr << "Resolving " << *it << "...";
      if (generate_type(resolved, *it, toplevels)) {
// 	std::cerr << "done." << std::endl;
	toplevels.erase(it);
	break;
      }
    }
    if (it == toplevels.end())
      throw fatal_error();
  }

  // write implementation of instantiate_xiso() to xiso.cpp
  generate_instantiate_xiso(ns_id);

  if (ns_id != aka::empty_token) {
    el_ << std::endl
	<< "}" << std::endl;
    xiso_ << std::endl
	  << "}" << std::endl;
    ximpl_ << std::endl
	   << "}" << std::endl;
  }

  el_ << std::endl;
  ximpl_ << std::endl;
  generate_serialize_methods(ns_id);
  el_ << std::endl;
}


bool generator::generate_type(qname_set &resolved, const aka::qname &to_resolve, 
			      qname_set &toplevels) {

  qname_set resolving(resolved);

  const_class_defs dependency;
  qname_set forwarded;

  if (!soft_resolve(resolving, to_resolve, dependency, forwarded))
    return false;

  const class_def &def = *registry_->classes_.get(to_resolve);
  dependency.push_back(&def);

  for (const_class_defs::iterator it = dependency.begin(); it != dependency.end(); ++it) {
    const class_def *def = *it;
    if (resolved.find(def->name_) != resolved.end())
      continue;

//     logstm_ << def->name_ << " resolved." << std::endl;
      
    switch (def->id_) {
    case sequence_id:
      generate_sequence(*def, resolved);
      break;
    case choice_id: {
      generate_choice(*def, resolved);
      break;
    }
    case all_id:
      generate_all(*def, resolved);
      break;
    case array_id:
      generate_array(*def, resolved);
      break;
    case simplecontent_id:
      generate_simplecontent(*def);
      break;
    case simpletype_id: {
      generate_simpletype(*def);
      break;
    }
    default:
      assert(!"Must not reach here.");
      break;
    }
    resolved.insert(def->name_);
  }

  // generation of forwarded classes. 
  for (qname_set::iterator fit = forwarded.begin(); fit != forwarded.end(); ++fit) {
    if (resolved.find(*fit) == resolved.end())
      toplevels.insert(*fit);
  }
  return true;
}


bool generator::soft_resolve(qname_set &resolving, const aka::qname &name, 
			     const_class_defs &dependency, qname_set &forwarded) {

  if (resolving.find(name) != resolving.end()) // already resolved.
    return true;

  const_class_defs resolved_classes;
  qname_set resolved_here(resolving);
  const class_def *def = registry_->classes_.get(name);
  
  qname_set processing;
  if (hard_resolve(resolved_here, name, resolved_classes, processing)) {
    dependency.insert(dependency.end(), resolved_classes.begin(), resolved_classes.end());
    dependency.push_back(def);
    resolving = resolved_here;
    resolving.insert(name);
    return true;
  }
  
  if (def->id_ == choice_id) {
    dependency.push_back(def);
    for (element_types::const_iterator it = def->etypes_.begin(); 
	 it != def->etypes_.end(); ++it)
      forwarded.insert(it->type_);
    return true;
  }

  if (def->id_ == array_id) {
    dependency.push_back(def);
    forwarded.insert(def->value_type_);
    return true;
  }

  qname_set forwarded_here;

  for (element_types::const_iterator eit = def->etypes_.begin();
       eit != def->etypes_.end(); ++eit) {
    const element_type& childtype = *eit;
    if (soft_resolve(resolved_here, childtype.type_, resolved_classes, forwarded_here))
      continue;
    return false;
  }
  
  for (attribute_types::const_iterator ait = def->adefs_.attributes_.begin();
       ait != def->adefs_.attributes_.end(); ++ait) {
    if (hard_resolve(resolving, ait->type_, resolved_classes, processing))
      continue;
    return false;
  }
  
  resolving = resolved_here;
  dependency.insert(dependency.end(), resolved_classes.begin(), resolved_classes.end());
  for (qname_set::iterator it = forwarded_here.begin(); it != forwarded_here.end(); ++it)
    forwarded.insert(*it);
  
  dependency.push_back(def);
  resolving.insert(name);
  
  return true;
}


bool generator::hard_resolve(qname_set &resolving, const aka::qname &name, 
			     const_class_defs &dependency, qname_set &processing) {
  
  if (resolving.find(name) != resolving.end())
    return true;
  
  if (processing.find(name) != processing.end())
    return false;
  
  processing.insert(name);
  
  const_class_defs resolved_classes;
  qname_set resolved_here(resolving);
  const class_def *def = registry_->classes_.get(name);
 
  if (def->id_ == array_id) {
    if (resolved_here.find(def->value_type_) == resolved_here.end())
      if (!hard_resolve(resolved_here, def->value_type_, dependency, processing))
	return false;
  }
  else {
    for (element_types::const_iterator eit = def->etypes_.begin();
	 eit != def->etypes_.end(); ++eit) {
      if (resolved_here.find(eit->type_) == resolved_here.end())
	if (!hard_resolve(resolved_here, eit->type_, dependency, processing))
	return false;
    }
  }

  for (attribute_types::const_iterator ait = def->adefs_.attributes_.begin();
       ait != def->adefs_.attributes_.end(); ++ait) {
    if ((resolved_here.find(ait->type_) == resolved_here.end()) &&
	!hard_resolve(resolved_here, ait->type_, dependency, processing))
      return false;
  }

  resolving = resolved_here;
  resolving.insert(name);

  dependency.insert(dependency.end(), resolved_classes.begin(), resolved_classes.end());
  const class_def *child = registry_->classes_.get(name);
  dependency.push_back(child);
  return true;
}


void generator::generate_serialize_methods(const aka::id_type ns_id) {

  typedef std::map<aka::qname, qname_set> element_map;
  element_map emap;

  for (name_resolver::iterator it = registry_->elements_.begin();
       it != registry_->elements_.end(); ++it) {
    if (it->first.get_uri_id() == ns_id)
      emap[it->second].insert(it->first);
  }    
  
  for (element_map::iterator eit = emap.begin(); eit != emap.end(); ++eit) {
    qname_set &docs = eit->second;
    if (docs.size() == 1) {
      const aka::qname &type = eit->first;
      const aka::qname &tagname = *eit->second.begin();
      el_ << "/** serialization of " << quote(tagname) << ". */" << std::endl
	  << "void serialize(const " << cppname(type) 
	  << " &root, std::ostream &ostm);" << std::endl;
      
      ximpl_ << "void serialize(const " << cppname(type) 
	     << "& root, std::ostream &ostm) {" << std::endl
	     << "  aka::xml_serializer ser;" << std::endl
	     << "  ser.serialize(root, " << quote(tagname) << ", ostm);" << std::endl
	     << "}" << std::endl
	     << std::endl;
    }
    else {
      const aka::qname &type = eit->first;
      const qname_set &tagnames = eit->second;
      el_ << "/** serialization of ";
      
      qname_set::const_iterator tit = tagnames.begin();
      el_ << quote(*tit);
      for (; tit != tagnames.end(); ++tit) {
	el_ << ", " << quote(*tit);
      }
      el_ << ". */" << std::endl
	  << "void serialize(const " << cppname(type)
	  << "&root, const std::string &tagname, std::ostream &ostm);" << std::endl;

      ximpl_ << "void serialize(const " << cppname(type) 
	     << "& root, const std::string &tagname, std::ostream &ostm) {" << std::endl
	     << "  aka::xml_serializer ser;" << std::endl
	     << "  ser.serialize(root, tagname, ostm);" << std::endl
	     << "}" << std::endl
	     << std::endl;
    }
  }
}


void generator::generate_instantiate_xiso(const aka::id_type ns_id) {
  
  ximpl_ << "  void instantiate_xiso() {" << std::endl;
  if ((ns_id != aka::empty_token) && (ns_id != aka::xml_ns_token))
    ximpl_ << "    aka::xmlns(" << quote(aka::get_prefix(ns_id))
	   << ", " << quote(aka::get_namespace_uri(ns_id)) << ");" << std::endl;
  
  for (name_resolver::iterator it = registry_->elements_.begin();
       it != registry_->elements_.end(); ++it) {
    const aka::qname &name = it->first;
    if (name.get_uri_id() == ns_id)
      ximpl_ << "    aka::doctype(" << quote(it->first) << ", "
	     << leafname(it->second) << "());" << std::endl;
  }
  
  ximpl_ << "  }" << std::endl;
}

void generator::write_ptrmember_decl(const element_types &etypes, const qname_set &resolved) {
  for (element_types::const_iterator it = etypes.begin(); it != etypes.end(); ++it) {
    if (it->ptrmember_) {
      if (resolved.find(it->type_) == resolved.end())
	el_ << "    struct " << it->type_ << ";" << std::endl;
    }
  }
}
