// This file is part of the Coral project.
// Please read the copyright notice in the LICENSE file.

#include "xmi_reader_common.h"

#include <iostream>

#include <coral/core/CorePrimitives.h>
#include <coral/core/util/TransactionManager.h>
#include "compression_support.h"
#include <coral/core/config.h>
#include "xmi_reader_1.h"
#include "xmi_reader_2.h"
#include <coral/lib/regexp.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <coral/core/Project.h>

#include <coral/core/util/check_bidi_slots.h>

#include "libxml2internal.h"

NAMESPACE_BEGIN(coral);
NAMESPACE_BEGIN(io);


double retrieve_double(const std::string& s) {
	double d;
	// This can be slow
	std::istringstream t(s);
	t.imbue(coral::clocale);
	t >> d;
	if (!t.eof()) {
		if (t.fail()) {
			const char* c = s.c_str();
			if (strcasecmp("inf", c) == 0 || strcasecmp("infinity", c) == 0) {
				d = std::numeric_limits<double>::infinity();
				return d;
			}
			if (strcasecmp("-inf", c) == 0 || strcasecmp("-infinity", c) == 0) {
				d = -std::numeric_limits<double>::infinity();
				return d;
			}
			if (strcasecmp("nan", c) == 0) {
				d = std::numeric_limits<double>::quiet_NaN();
				return d; 
			}
			dlog << DEBUGLEVEL_ERROR
			     << "ERROR Double " << s << " outside limits!?" << std::endl;
			throw std::runtime_error("Double outside limits!?");
		}
		dlog << DEBUGLEVEL_WARNING
		     << "WARNING Extra information after double in \"" << s << "\" (ignored, continuing)" << std::endl;
	}
	coral_assert(!t.fail());
	return d;
}

std::ostream& operator<<(std::ostream& out, const _ref& r) {
	int n = r.number();
	const char* idtype = "<Unknown id type>";
	switch (n) {
	case 1:
		idtype = "xmi uuid";
		break;
	case 2:
		idtype = "xmi id";
		break;
	case 3:
		idtype = "xmi label";
		break;
	default:
		break;
	}
	out << idtype << " value " << r.id;
	return out;
}


xmistate::xmistate() {
	start = false;
	metamodel = NULL;
	rate_limited_ignore_property = 10;
	rate_limited_ignore_unknown_elems = 10;
	need_postprocess = true;
	reader = NULL;
	storage = NULL;
	was_compressed = false;
}

xmistate::~xmistate() throw () {
	if (reader) {
		xmlFreeTextReader(reader);
	}
	if (storage) {
		storage->unref();
	}
}

int xmistate::loadIt() {
	coral_assert(0);
	return 1;
}


const _ref& xmistate::addID(const _ref* id, coral::Element* elem) {
	coral_assert(!id->id.empty());
	coral::autounref<const _ref> _id(id);
	std::pair<all_ids_type::iterator, bool> p =
		all_ids.insert(id);
	if (p.second) {
		_id.release();
	}
	const _ref& r = **(p.first);
	if (!elem) return r;

	// BUG xpathref
	coral::Project::old_loaded_ref_info_t& oldinfo(storage->getOldInfo());
	coral::Project::old_loaded_ref_info_t::iterator findr = oldinfo.find(&r);
	if (findr != oldinfo.end()) {
		dlog << DEBUGLEVEL_CRITICAL
		     << "CRITICAL The input stream includes multiple elements with same ID!" << std::endl
		     << "This can happen e.g. if a file shares one element between several parents, "
		     << std::endl
		     << "or if the saving routine has accidentally used the same ID twice."
		     << std::endl
		     << "The ID in question is " << r << "." << std::endl;
		// BUG Hmm, the fix routine can be XMI 2-specific and fail on XMI 1...
		handleBadID();
		throw handled();
	}

	oldinfo[&r] = elem;
	elem->ref();
	r.ref();

	return r;
}


/*
 * Helper that prints that we ignored a property.
 * Rate-limited to reduce clutter.
 */
void xmistate::ignore_property(const char* a_name,
			       const std::string& type) throw () {
	if (rate_limited_ignore_property < 0)
		return;
	if (!--rate_limited_ignore_property) {
		try {
			dlog << DEBUGLEVEL_INFO
			     << "INFO Rate-limiting ignored properties."
			     << std::endl;
		} catch (...) {
			;
		}
		return;
	}

	try {
		dlog << DEBUGLEVEL_WARNING
		     << "WARNING Couldn't find property "
		     << a_name << " in type "
		     << type
		     << " (ignoring, continuing loading)"
		     << std::endl;
	} catch (...) {
		;
	}
}

/*
 * Helper that prints that we ignored an unknown element.
 * Rate-limited to reduce clutter.
 */
void xmistate::ignore_unknown_element(const char* a_name,
				      const coral::Package* P) throw () {
	coral_assert(a_name);
	coral_assert(P);
	if (rate_limited_ignore_unknown_elems < 0)
		return;
	if (!--rate_limited_ignore_unknown_elems) {
		try {
			dlog << DEBUGLEVEL_INFO
			     << "INFO Rate-limiting ignored unknown elements."
			     << std::endl;
		} catch (...) {
			;
		}
		return;
	}

	try {
		dlog << DEBUGLEVEL_WARNING
		     << "WARNING"
#if LIBXML_VERSION > 20507
		     << " Line "
		     << xmlGetLineNo(xmlTextReaderCurrentNode(reader))
#endif
		     << " Couldn't find element "
		     << a_name
		     << " from Package "
		     << P->getName();
		if (const Language* L = dynamic_cast<const Language*>(P)) {
			dlog << " " << L->getVersion() << "."
			     << L->getRevision();
		}
		dlog << std::endl
		     << " (with namespace " << P->getXMLNS() << ")"
		     << " (ignoring, continuing loading)"
		     << std::endl;
	} catch (...) {
		;
	}
}

int xmistate::addAtom(coral::Element* e, const coral::Property* p,
		      const std::string& val_utf8) {

	coral_assert(e);
	coral_assert(p);
	coral::AbstractElementDefinition* target = p->getTarget();
	coral_assert(target);
	if (!target->isDatatypeDefinition()) {
		dlog << DEBUGLEVEL_ERROR
		     << "ERROR while processing XMI.field, "
		     << target->getName() << " is not a primitive type."
		     << std::endl;
		return 1;
	}
	/*
	 * We can't easily shortcircuit if the default value is nonempty.
	 * E.g. if the default value is "true" we can't shortcircuit all the
	 * "false" values...
	 */
	bool can_shortcircuit = p->isMultSingle() && p->getDefaultValue().empty();

	if (val_utf8.empty()) {
		if (can_shortcircuit) return 0;
		coral::Atom* atom = target->newAtom();
		coral::autounref<coral::Atom> _p(atom);
		Slot& s = e->__getSlotByName__(p->getName());
		s.insert(atom, -1, -1, true);
		return 0;
	}

	coral::Atom* atom = target->newAtom();
	coral::autounref<coral::Atom> _p(atom);

	/*
	 * Could have a helper function here, we have some code duplication
	 */
	if (dynamic_cast<coral::ElementPyObject*>(atom)) {
		/*
		 * We don't support this.
		 */
		coral_assert(0);
	} else if (coral::ElementEnumeration* a = dynamic_cast<coral::ElementEnumeration*>(atom)) {
		a->__setEValueByName__(val_utf8.c_str());
		if (can_shortcircuit && a->__getEValue__() == 0) return 0;
	} else if (coral::ElementString* a = dynamic_cast<coral::ElementString*>(atom)) {
		a->__setString__(unicodestring(val_utf8, true));
	} else if (coral::ElementInteger* a = dynamic_cast<coral::ElementInteger*>(atom)) {
	     // This can be slow...
	     int i;
	     std::istringstream thestream(val_utf8);
	     thestream.imbue(coral::clocale);
	     thestream >> i;
	     if (!thestream.eof()) {
		  if (thestream.fail()) {
		       dlog << DEBUGLEVEL_ERROR
			    << "ERROR Integer " << val_utf8 << " outside limits!?" << std::endl;
		       return 1;
		  }
		  dlog << DEBUGLEVEL_WARNING
		       << "WARNING Extra information after integer in \"" << val_utf8 << "\" (ignored, continuing)" << std::endl;
	     }
	     coral_assert(!thestream.fail());
	     if (can_shortcircuit && i == 0) return 0;
	     a->__setIValue__(static_cast<int>(i));
	} else if (coral::ElementDouble* a = dynamic_cast<coral::ElementDouble*>(atom)) {
		coral_double d;
		try {
			d = retrieve_double(val_utf8);
		} catch (std::runtime_error& e) {
			return 1;
		}
		if (can_shortcircuit && d == 0.0f) return 0;
		a->__setDValue__(d);
	} else {
		dlog << DEBUGLEVEL_ERROR
		     << "ERROR Can't load primitive value "
		     << val_utf8 << "." << std::endl;
		return 1;
	}

	Slot& s = e->__getSlotByName__(p->getName());
	s.insert(atom, -1, -1, true);
	return 0;
}

/*
 * Set the slot of a primitive type.
 * Not done lazily, because code looks crap...
 */
int xmistate::fixDatatype(coral::Element* new_element,
			  const coral::AbstractElementDefinition* target,
			  const Property* p,
			  const std::string& a_value) {
	coral_assert(new_element);
	return addAtom(new_element,
		       p,
		       a_value);
}

/*
 * Get the parent of all elements.
 * One would like to think it's the first element in the XMI
 * file, but UML tools do this differently.
 * Returns a parent element with ref.
 */
coral::Element* findParent(Collection* roots) {
	if (!roots) return NULL;
	coral_assert(roots->size() > 0);

	coral::Element* parent = NULL;
	int parent_hierarchy_size = 0;

	switch (roots->size()) {
	case 0: {
		break;
	}
	case 1: {
		parent = dynamic_cast<coral::Element*>(roots->__getitem__(0));
		break;
	}
	default: {
		coral::ref_counted* _parent;
		coral::Iterator m(roots);
		while ((_parent = m.next()) != NULL) {
			coral::Element* candidate
				= dynamic_cast<coral::Element*>(_parent);
			if (!candidate) continue;
			while (1) {
				coral::Element* p = candidate->__parent__();
				if (!p) break;
				candidate = p;
			}
			autounref<Set> o(candidate->__getAllParts__());
			int size = o.get()->size();
			if (size > parent_hierarchy_size) {
				parent = candidate;
				parent_hierarchy_size = size;
			}
		}
		break;
	}
	}
	if (!parent) {
		dlog << DEBUGLEVEL_ERROR
		     << "ERROR no suitable root element found!"
		     << std::endl;
		return NULL;
	}
	coral_assert(parent);

	parent->ref();
	return parent;
}

/*
 * We got an unknown element element_name.
 * we either return an error, or ignore it
 * and its children.
 * BUG For now, we ignore it... Should this be configurable?
 */
int xmistate::handleUnknownElement(const char* element_name) {
	int default_err = 0;

	if (xmlTextReaderIsEmptyElement(reader))
		return default_err;
	int ret;
	int n_unclosed_starts = 1;
	for (; (ret = xmlTextReaderRead(reader) == 1); ) {
		switch (xmlTextReaderNodeType(reader)) {
		case XML_ELEMENT_NODE:
			if (!xmlTextReaderIsEmptyElement(reader)) {
				++n_unclosed_starts;
			}
			break;
		case XML_ELEMENT_DECL:
			--n_unclosed_starts;
			if (n_unclosed_starts == 0) {
				return 0;
			}
			break;
		default:
			break;
		}
	}
	if (ret != 1) {
		dlog << DEBUGLEVEL_ERROR
		     << "ERROR when ignoring unknown element." << std::endl;
		return 1;
	}
	dlog << DEBUGLEVEL_ERROR
	     << "ERROR end of file when ignoring unknown element?"
	     << std::endl;
	return 1;
}





NAMESPACE_BEGIN();
int process_no_xmi_version(xmistate** result, xmlTextReaderPtr reader) {
	switch (xmlTextReaderNodeType(reader)) {
	case XML_ELEMENT_NODE: {
		wXmlTextReaderPrefix wprefix(reader);
		const xmlChar* prefix = wprefix.get();
		/*
		 * Ultra-simple XMI 1.2 / XMI 2.0 detection.
		 *
		 */
		if (prefix) {
			//dlog << DEBUGLEVEL_DEBUG << "Detected XMI 2.0 stream." << std::endl;
			*result = new _state2();
		} else {
			//dlog << DEBUGLEVEL_DEBUG << "Detected XMI 1.x stream." << std::endl;
			*result = new _state1();
		}
	}
	default:
		;
	}
	return 0;
}

xmistate* __read_start(const char* filename_or_buffer,
			      bool is_buffer) {
	xmlTextReaderPtr reader;
	int ret;

	if (is_buffer) {
		dlog << DEBUGLEVEL_DEBUG
		     << "DEBUG Going to read memory at address "
		     << static_cast<const void*>(filename_or_buffer)
		     << "." << std::endl;
	} else {
		dlog << DEBUGLEVEL_DEBUG
		     << "DEBUG Going to read " << filename_or_buffer
		     << "." << std::endl;
	}
	bool was_compressed = false;

	if (!is_buffer) {
#if 0
		std::string filename = filename_or_buffer;
#else
		CompressionSupport zuml(filename_or_buffer);
		std::string filename = zuml.getLoadableFilename();
		if (zuml.wasCompressed()) {
			dlog << DEBUGLEVEL_INFO
			     << "INFO Detected compressed file."
			     << std::endl;
			was_compressed = true;
		}
#endif
		reader = xmlNewTextReaderFilename(filename.c_str());
		if (!reader) {
			dlog << DEBUGLEVEL_ERROR
			     << "ERROR Couldn't initialise the XML reader!"
			     << std::endl
			     << "ERROR Perhaps there is no file "
			     << filename_or_buffer << "?"
			     << std::endl;
		}
	} else {
		reader = xmlReaderForMemory(filename_or_buffer,
					    strlen(filename_or_buffer),
					    "",
					    "UTF-8",
					    0);
	}

	if (!reader) {
		return NULL;
	}

// BUG I wonder when it was first introduced
#if LIBXML_VERSION > 20507
	xmlThrDefLineNumbersDefaultValue(1);
#endif
	xmistate* result = NULL;
	for (; (ret = xmlTextReaderRead(reader) == 1); ) {
		try {
			ret = process_no_xmi_version(&result, reader);
		} catch (handled& h) {
			/* ok, these should be ignored. */
			ret = 0;
		} catch (std::exception& e) {
			dlog << DEBUGLEVEL_ERROR
			     << "ERROR Caught an exception: " << e.what() << std::endl;
			ret = 1; 
		}
		if (ret) break;
		if (result) {
			result->was_compressed = was_compressed;
			result->reader = reader;
			break;
		}
	}
	if (!result) {
		xmlFreeTextReader(reader);
	}
	return result;
}

xmistate* load_file_no_postprocess(loadstate_per_invocation& loadstate,
				   const char* filename_or_buffer,
				   coral::Language* language,
				   bool is_buffer) {

	all_states_t& all_states(loadstate.all_states);

	bool did_create;
	const char* filename = filename_or_buffer;
	if (is_buffer) {
		filename = "";
	}
	coral::autounref<coral::Project> _storage
		= coral::Project::getProjectByFilename(filename,
						       "",
						       &did_create);
	if (is_buffer) {
		coral_assert(did_create);
	}
	Project* storage = _storage.get();
	coral_assert(storage);

	if (loadstate.fixed_ref_info.insert(storage).second == true) {
		/*
		 * The model can have changed between loading
		 * (when the ref info was created) and now.
		 * So, tell the storage to go through the whole
		 * model and re-create the UUID info.
		 */
		storage->fixLoadedRef();
	}

	if (!did_create) {
		all_states_t::iterator x = all_states.find(storage);
		if (x != all_states.end()) {
			coral_assert((*x).second->storage == storage);
			return (*x).second;
		}
	}

	xmistate* result = NULL;
	if (!did_create
	    && storage->contents()->size() > 0) {
		// placeholder xmistate
		result = new _state1();
		result->need_postprocess = false;
	} else {
		result = __read_start(filename_or_buffer, is_buffer);
	}
	if (!result)
		return NULL;

	std::auto_ptr<xmistate> _result(result);
	all_states[storage] = result;
	result->metamodel = language;
	result->storage = storage;
	_storage.release();
	if (storage->contents()->size() == 0) {
		int ret = result->loadIt();
		if (ret) {
			dlog << DEBUGLEVEL_ERROR
			     << "ERROR erasing " << filename
			     << " from internal all_states table" << std::endl;
			all_states.erase(storage);
			return NULL;
		} else {
			dlog << DEBUGLEVEL_INFO
			     << "INFO " << filename << " loaded."
			     << std::endl;
		}
	}
	_result.release();

	coral_assert(result->storage);
	return result;
}

coral::Element* load_filename(loadstate_per_invocation& loadstate,
			      const std::string& filename,
			      const _ref& id) {
	xmistate* x = load_file_no_postprocess(loadstate,
					       filename.c_str(),
					       NULL,
					       false);
	if (!x) {
		dlog << DEBUGLEVEL_DEBUG
		     << "DEBUG No xmistate returned???" << std::endl;
		return NULL;
	}
 	dlog << DEBUGLEVEL_DEBUG
	     << "DEBUG Looking for " << id.id
	     << " (" << id.number() << ")" << std::endl;

	Element* e = x->storage->findRef(&id);
	if (e) {
		return e;
	}
	dlog << DEBUGLEVEL_DEBUG
	     << "DEBUG Not found." << std::endl;
	return NULL;
}

/*
 * Helper to load a new XMI document to satisfy references.
 */
coral::Element* matchFromOtherState(loadstate_per_invocation& loadstate,
				    Project* storage,
				    const _ref& id) {

	static const std::string path_to_install_dir(coral::installation_path);

	/*
	 * First we try the filename, if one exists.
	 * This way the UUID shouldn't have to be anything special
	 * like AAU:coral:xxxx.
	 */
	if (storage && !storage->getFilename().empty()) {
		std::string filename = storage->getFilename();
		coral::Element* e;
		if ((e = load_filename(loadstate, filename, id)))
			return e;
		else
			;
	}

	// D'Oh, unnamed projects (e.g. drag-and-drop) come here. A bit clumsy
	if (loadstate.fixed_ref_info.insert(storage).second == true) {
		/*
		 * The model can have changed between loading
		 * (when the ref info was created) and now.
		 * So, tell the storage to go through the whole
		 * model and re-create the UUID info.
		 */
		storage->fixLoadedRef();
		Element* e = storage->findRef(&id);
		if (e) {
			return e;
		}
	}




	/*
	 * Otherwise we try using our "magic" UUIDs.
	 */
	if (id.id.find("AAU:coral::") != 0) return NULL;

	static RE r("AAU:coral::([a-zA-Z]+)([0-9])([0-9])::",
		    REG_EXTENDED|REG_NEWLINE);
	regmatch_t match[4];
	const char* s = id.id.c_str();
	int err = r.executeRE(s, 4, match, 0);
	if (err) return NULL;
	const int i_lang = 1;
	const int i_ver = 2;
	const int i_rev = 3;
	if (match[i_lang].rm_so == -1
	    || match[i_ver].rm_so == -1
	    || match[i_rev].rm_so == -1) {
		return NULL;
	}
	//const char* rest = id.id.c_str() + match[0].rm_eo - match[0].rm_so;
	std::string slang(&s[match[i_lang].rm_so],
			 match[i_lang].rm_eo - match[i_lang].rm_so);
	std::string sver(&s[match[i_ver].rm_so],
			 match[i_ver].rm_eo - match[i_ver].rm_so);
	std::string srev(&s[match[i_rev].rm_so],
			 match[i_rev].rm_eo - match[i_rev].rm_so);
	//int ver = atoi(sver.c_str());
	//int rev = atoi(sver.c_str());
	std::string name = slang + sver + srev + ".xmi";

	std::string newlang = path_to_install_dir + "/" + name;
	coral::Element* e;
	if ((e = load_filename(loadstate, newlang, id)))
		;
	else if ((e = load_filename(loadstate, name, id)))
		;
	else
		return NULL;

	return e;
}

NAMESPACE_END();

/*
 * Postprocessing sets the slots. This has to be done
 * at a late stage due to forwards (or even out-of-band)
 * references in the XMI stream.
 */
int xmistate::postProcess(loadstate_per_invocation& loadstate) {
	int err = 0;
	for (xmistate::slots_type::iterator f = slots.begin();
	     f != slots.end();
	     ++f) {
		coral::Slot* s = f->first;
		coral_assert(s);
		std::vector<remote_id_t>& ids = f->second;
		for (std::vector<remote_id_t>::iterator i = ids.begin();
		     i != ids.end();
		     ++i) {
			coral::Project* targetstorage = (*i).first;
			const _ref* id = (*i).second;
			coral_assert(id);
			coral_assert(targetstorage);

			coral::Element* target_elem = NULL;

			target_elem = targetstorage->findRef(id);
			if (!target_elem) {
				target_elem
					= matchFromOtherState(loadstate,
							      targetstorage,
							      *id);
			}
			if (!target_elem) {
				dlog << DEBUGLEVEL_WARNING
				     << "WARNING Malformed XMI file "
				     << targetstorage->getFilename()
				     << ", couldn't find " << (*id).id << "."
				     << std::endl;
				err = 1;
				continue;
			}

			try {
				const coral::Property* poppo = s->getProperty()->getOppositeProperty();
				bool unidirectional = true;
				if (poppo && poppo->isUnserializable())
					unidirectional = false;
				s->insert(target_elem, -1, -1, unidirectional);
				coral::Element* owner = s->getElement();
				if (poppo && poppo->isComposition()) {
					/* Can't be root if there's
					 * a composition above */
					owner->__setProject__(NULL);
				}
			} catch (std::exception& e) {
				dlog << DEBUGLEVEL_ERROR
				     << "ERROR Couldn't add "
				     << target_elem->__str__()
				     << " (UUID "
				     << target_elem->__XMIid__()
				     << ", id "
				     << id->id << ")" << std::endl
				     << " into slot "
				     << s->getProperty()->getName()
				     << " in postprocessing: "
				     << e.what() << std::endl;
				return 1;
			}
		}
	}

	if (err) return err;
	
	/*
	 * Project
	 */
	coral_assert(storage);
	for (Iterator m(storage->contents()); ref_counted* r = m.next(); ) {
		coral::Element* a_root
			= dynamic_cast<coral::Element*>(r);
		if (!a_root) continue;
		// Roots shouldn't have a parent...
		if (a_root->__parent__()) {
			a_root->__setProject__(NULL);
			continue;
		}
		coral_assert(!a_root->__parent__());
		coral_assert(Project::getProject(a_root) == storage);
	}
	for (Iterator m(storage->contents()); ref_counted* r = m.next(); ) {
		coral::Element* a_root
			= dynamic_cast<coral::Element*>(r);
		if (!a_root) continue;
		coral_assert(!a_root->__parent__());
		coral_assert(Project::getProject(a_root) == storage);
	}
	return 0;
}

coral::autounref<Project> io_load_document(const char* filename_or_buffer,
					   coral::Language* language,
					   bool is_buffer) {
	loadstate_per_invocation loadstate;
	loadstate.fixed_ref_info.insert(NULL);

	coral::Transaction::PauseTransaction p(true);

	xmistate* state =
		load_file_no_postprocess(loadstate,
					 filename_or_buffer,
					 language,
					 is_buffer);
	if (!state) {
		// BUG push this further down into load_file_no_postprocess...
		throw load_error("Load error");
	}

	for (;;) {
		all_states_t::const_iterator i;
		for (i = loadstate.all_states.begin();
		     i != loadstate.all_states.end();
		     ++i) {
			if ((*(*i).second).need_postprocess == true)
				break;
		}
		if (i == loadstate.all_states.end()) break;

		(*(*i).second).need_postprocess = false;
		if ((*i).second->postProcess(loadstate)) {
			dlog << DEBUGLEVEL_ERROR
			     << "ERROR postprocessing of xmi:idrefs, xmi:uuidrefs and/or hrefs failed!"
			     << std::endl;
			throw load_error("Error in postprocessing xmi:idrefs, xmi:uuidrefs and/or hrefs" );
		}
	}
	state->storage->ref();
	autounref<Project> result(state->storage);


       /*
        * Annoying.
        * Our XMI loader can make bad bidirectional connects if our XMI file
        * is wrong. This fixes it using check_bidi_refs.
        */
       std::vector<coral::Element*> roots;
       for (all_states_t::const_iterator i = loadstate.all_states.begin();
	    i != loadstate.all_states.end();
	    ++i) {
               Project* st = (*i).second->storage;
	       Collection* o = st->contents();
	       Iterator ocl(o);
	       ref_counted* t;
	       while ((t = ocl.next()) != NULL) {
		       Element* e = dynamic_cast<Element*>(t);
		       coral_assert(e);
		       roots.push_back(e);
	       }
       }
       std::for_each(roots.begin(),
		     roots.end(),
		     std::ptr_fun(coral::util::check_bidi_slots));





	dlog << DEBUGLEVEL_DEBUG
	     << "DEBUG io_load_document: Going to return " << state->storage;
	if (!is_buffer) {
		dlog << DEBUGLEVEL_DEBUG
		     << " for file " << filename_or_buffer;
	}
	dlog << DEBUGLEVEL_DEBUG
	     << std::endl;

	/*
	 * Mark all compressed files as non-writable by removing the
	 * Project's filename.
	 */
	for (all_states_t::const_iterator i
		     = loadstate.all_states.begin();
	     i != loadstate.all_states.end();
	     ++i) {
		xmistate* x = (*i).second;
		if (x->was_compressed) {
			Project* s = (*i).first;
			s->setFilename("");
		}
		
	}

	return result;
}

/* Dummy helper routine for deprecated loadModel */
coral::Element* io_load_file(const char *filename,
			     coral::Language* language) {
	autounref<Project> s(io_load_document(filename, language));
	Project* st = s.release();
	return findParent(st->contents());
}


NAMESPACE_END();
NAMESPACE_END();
