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

#include <libxml/xmlreader.h>
#include <libxml/tree.h>

#include <iostream>

#include "xmi_reader_1.h"
#include <coral/lib/regexp.h>

#include <coral/core/ElementDefinition.h>
#include <coral/core/Element.h>
#include <coral/core/Collection.h>
#include <coral/lib/autovector.h>
#include <coral/core/CorePrimitives.h>
#include <coral/lang/XMI/XMI.h>
#include <coral/lib/assert.h>
#include <coral/core/Project.h>

#include "xmi_reader_common.h"

#include "libxml2internal.h"

NAMESPACE_BEGIN(coral);
NAMESPACE_BEGIN(io);

_state1::_state1() : xmistate() {
	reading_slots = true;
}
_state1::~_state1() throw () {
	;
}

bool _state1::switchF() throw () {
	bool _old = !reading_slots;
	bool _new = _old;
	if (xmlTextReaderIsEmptyElement(reader))
		_new = !_new;
	reading_slots = _new;
	return _old;
}

void _state1::handleBadID() {
	coral_assert(!parent_slots.empty());
	parent_slots.pop_back();
	handleUnknownElement("<bogus>");
}

NAMESPACE_BEGIN();

RE href_regexp("(.*)\\|descendent\\(.*,.*,(.*),(.*)\\)",
	       REG_EXTENDED|REG_NEWLINE);

int __fixIdref_counted(_state1& state,
	       const _ref* a_value) {
	coral_assert(!state.parent_slots.empty());
	coral::Slot* pf = state.parent_slots.back();
	coral_assert(pf);
	const _ref& id = state.addID(a_value, NULL);
	state.slots[pf].push_back(xmistate::remote_id_t(state.storage, &id));
	return 0;
}

int parse_href(_state1& state,
	       const char* a_value) {
	const int n_matches = 5;
	regmatch_t match[n_matches];
	/* update these indices according to the RE. 0 is for the
	   whole string. */
	const int i_file = 1;
	const int i_attr = 2;
	const int i_value = 3;
	int err = href_regexp.executeRE(a_value, n_matches, match, 0);
	if (err) {
		dlog << DEBUGLEVEL_ERROR
		     << "ERROR Couldn't parse href argument: "
		     << a_value
		     << "." << std::endl;
		return 1;
	}
	if (match[i_file].rm_so == -1
	    || match[i_attr].rm_so == -1
	    || match[i_value].rm_so == -1) {
		dlog << DEBUGLEVEL_ERROR
		     << "ERROR Couldn't match the href argument: "
		     << a_value 
		     << "." << std::endl;
		return 1;
	}

	std::string filename(&a_value[match[i_file].rm_so],
			     match[i_file].rm_eo - match[i_file].rm_so);
	std::string attr(&a_value[match[i_attr].rm_so],
			 match[i_attr].rm_eo - match[i_attr].rm_so);
	std::string value(&a_value[match[i_value].rm_so],
			  match[i_value].rm_eo - match[i_value].rm_so);

	coral_assert(!state.parent_slots.empty());
	coral::Slot* pf = state.parent_slots.back();
	coral_assert(pf);

	autounref<Project> _labelstorage;
	Project* labelstorage = NULL;
	if (filename.empty()) {
		labelstorage = state.storage;
		labelstorage->ref();
		_labelstorage = labelstorage;
	} else {
		std::string relative_path = state.storage->getFilename();
		std::string::size_type pos = relative_path.find_last_of('/');
		if (pos != std::string::npos) {
			relative_path.erase(pos);
		} else {
			relative_path = "";
		}
		_labelstorage = Project::getProjectByFilename(filename,
							      relative_path);
		labelstorage = _labelstorage.get();
	}
	coral_assert(labelstorage);
	state.unref_these.push_back(labelstorage);
	_labelstorage.release();

	if (!strcmp(attr.c_str(), "xmi.uuid")) {
		const _ref& id
			= state.addID(new _uuidref(value.c_str()), NULL);
		state.slots[pf].push_back(xmistate::remote_id_t(labelstorage,
								&id));
	} else if (!strcmp(attr.c_str(), "xmi.label")) {
		const _ref& id
			= state.addID(new _labelref(value.c_str()), NULL);
		state.slots[pf].push_back(xmistate::remote_id_t(labelstorage,
								&id));
	} else {
		dlog << DEBUGLEVEL_ERROR
		     << "ERROR Sorry, Coral can't handle the following href: "
		     << std::endl
		     << a_value 
		     << "." << std::endl;
		return 1;
	}
	return 0;
}

/*
 * Helper for checking the various references in
 * an XMI element .
 */
int fixIdref_counted(_state1& state, const char* element_name) {
	while (xmlTextReaderMoveToNextAttribute(state.reader)) {
		wXmlTextReaderName wa_name(state.reader);
		wXmlTextReaderValue wa_value(state.reader);
		const char* a_name = reinterpret_cast<const char*>(wa_name.get());
		const char* a_value = reinterpret_cast<const char*>(wa_value.get());
		if (!a_value) continue;
		/*
		 * For some reason Rational XDE creates stuff like
		 * <UML:Dependency.client>
		 *   <UML:ModelElement xmi.idref=""/>
		 * </UML:Dependency.client>
		 *
		 * We'll just have to ignore that element with the
		 * a_value[0] == '\0' check
		 */
		if (!strcmp(a_name, "xmi.idref")) {
			if (a_value[0] == '\0') return 0;
			return __fixIdref_counted(state, new _idref(a_value));
		} else if (!strcmp(a_name, "href")) {
			if (a_value[0] == '\0') return 0;
			return parse_href(state, a_value);
		} else if (!strcmp(a_name, "xmi.uuidref")) {
			if (a_value[0] == '\0') return 0;
			return __fixIdref_counted(state, new _uuidref(a_value));
		} else if (!strcmp(a_name, "xmi.label")) {
			if (a_value[0] == '\0') return 0;
			return __fixIdref_counted(state, new _labelref(a_value));
		} else if (!strcmp(a_name, "xmi.id")) {
			dlog << DEBUGLEVEL_WARNING
			     << "WARNING"
#if LIBXML_VERSION > 20507
			     << " Line "
			     << xmlGetLineNo(xmlTextReaderCurrentNode(state.reader))
#endif
			     << " Expecting an association, but we seem to receive a composition (xmi.id)." << std::endl
			     << "       (ignoring, continuing loading)" << std::endl;
		} else {
			dlog << DEBUGLEVEL_WARNING
			     << "WARNING"
#if LIBXML_VERSION > 20507
			     << " Line "
			     << xmlGetLineNo(xmlTextReaderCurrentNode(state.reader))
#endif
			     << " Expecting an association, but we are receiving an element's slots!"
			     << std::endl;
			state.ignore_property(a_name, element_name);
			continue;
		}
	}
	dlog << DEBUGLEVEL_WARNING
	     << "WARNING Couldn't get reference to a "
	     << element_name << " element!" << std::endl
	     << "        Coral expected an association, not a composition." << std::endl;
	int err = state.handleUnknownElement(element_name);
	if (err) return err;
	state.switchF();
	throw handled();
}

/*
 * Fix "object attributes", i.e. attributes which reference
 * some other element instead of being of primitive type.
 */
int fixObjAttr(_state1& state,
	       coral::Element* new_element,
	       const char* a_name,
	       const char* a_value) {

	// Ignore missing idrefs
	coral_assert(a_value);
	if (a_value[0] == 0) return 0;

	try {
		coral::Slot& s = new_element->__getSlotByName__(a_name);
		const _ref& astr = state.addID(new _idref(a_value), NULL);
		Project::old_loaded_ref_info_t::const_iterator i
			= state.storage->getOldInfo().find(&astr);
		if (i == state.storage->getOldInfo().end()) {
			state.slots[&s].push_back(xmistate::remote_id_t(state.storage, &astr));
		} else {
			coral_assert((*i).second);
			// unidirectional, but we have anonymous properties
			// => false
			s.insert((*i).second, -1, -1, false);
		}
	} catch (bad_slot& exc) {
		dlog << DEBUGLEVEL_ERROR
		     << "ERROR No " << a_name << " for "
		     << new_element->__str__() << std::endl;
		return 1;
	}
	return 0;
}

void dropOld(_state1& state, coral::Atom* new_element) {
	if (!state.parent_slots.empty()) {
		coral::Slot* pf = state.parent_slots.back();
		pf->drop(new_element);
	} else {
		state.unref_these.pop_back();
		new_element->unref();
	}
}

/*
 * Support reading values such as
 * <XMI.field>value</XMI.field>
 * Some models use it, and it's useful
 * in writing multi-valued slots of primitive
 * type.
 * (although the latter isn't defined in XMI 1.x? Then how are
 * we supposed to do it???)
 */
int fixXMIField(_state1& state) {

	if (state.parent_slots.empty()) {
		return 1;
	}
	coral::Slot* s = state.parent_slots.back();
	coral_assert(s);

	/*
	 * Read the actual XMI.field value
	 */
	int ret = xmlTextReaderRead(state.reader);
	if (ret != 1) {
		dlog << DEBUGLEVEL_ERROR
		     << "ERROR Reading <XMI.field> value."
		     << std::endl;
		return 1;
	}

	w wa_value;
	const char* a_value;
	bool xmi_field_ending = true;
	if (xmlTextReaderIsEmptyElement(state.reader)) {
		dlog << DEBUGLEVEL_INFO
		     << "INFO XMI.field is empty " << std::endl;
		a_value = "";
		xmi_field_ending = false;
	} else {
		wXmlTextReaderValue d(state.reader);
		wa_value = d; // slicing ok
		a_value = reinterpret_cast<const char*>(wa_value.get());
		if (!a_value) {
			a_value = "";
		}
	}
	std::string val_utf8(a_value); // UTF-8
	//dlog << DEBUGLEVEL_DEBUG << "Read " << val << std::endl;

	if (xmi_field_ending) {
		// Skip </XMI.field>
		state.reading_slots = true;
		ret = xmlTextReaderRead(state.reader);
		if (ret != 1) {
			dlog << DEBUGLEVEL_ERROR
			     << "ERROR Reading ending element </XMI.field>."
			     << std::endl;
			return 1;
		}
	}
#if 0
	wXmlTextReaderValue x(state.reader);
	if (strcmp((const char*) x.get(), "XMI.field")) {
		dlog << DEBUGLEVEL_ERROR
		     << "ERROR Ending element </XMI.field> does not exist."
		     << std::endl;
		return 1;
	}
#endif

	return state.addAtom(s->getElement(),
			     s->getProperty(),
			     val_utf8);
}

/*
 * Creation of a new element. This is further complicated
 * by the need to recognize special "XMI." elements,
 * and to sometimes simply drop the new element if we suddenly
 * find a xmi.idref. (no, it shouldn't happen in proper XMI
 * files...)
 * Also we take care of root elements, attributes,
 * all references.
 */
int createElem(_state1& state,
	       const char* element_name) {

	if (!strcmp("XMI.field", element_name)) {
		return fixXMIField(state);
	}

	coral::ElementDefinition* ed = NULL;
	/*
	 * Hack for UML 1.3. At least ArgoUML needs this.
	 */
	if (0)
		;
	else if (!strncmp("Foundation.Core.", element_name, 16))
		element_name += 16;
	else if (!strncmp("Foundation.Data_Types.", element_name, 22))
		element_name += 22;
	else if (!strncmp("Foundation.Extension_Mechanisms.", element_name, 32))
		element_name += 32;
	else if (!strncmp("Behavioral_Elements.Use_Cases.", element_name, 30))
		element_name += 30;
	else if (!strncmp("Behavioral_Elements.Collaborations.", element_name, 35))
		element_name += 35;
	else if (!strncmp("Behavioral_Elements.Common_Behavior.", element_name, 36))
		element_name += 36;
	else if (!strncmp("Behavioral_Elements.State_Machines.", element_name, 35))
		element_name += 35;
	else if (!strncmp("Behavioral_Elements.Activity_Graphs.", element_name, 36))
		element_name += 36;
	else if (!strncmp("Model_Management.", element_name, 17)) {
		element_name += 17;
	}

	/*
	 * Hack to recognize the XMI.extension element
	 */
	coral::Package* L = NULL;
	if (!strncmp("XMI.", element_name, 4)) {
		element_name += 4;
		if (!strcmp(element_name, "extension"))
			element_name = "Extension";
		try {
			autounref<AbstractElementDefinition> _ed
				= XMI::language->getElement(element_name);
			ed = dynamic_cast<coral::ElementDefinition*>(_ed.get());
		} catch (...) {
			;
		}
		L = XMI::language;
	} else {
		coral_assert(state.metamodel);
		try {
			autounref<AbstractElementDefinition> _ed
				= state.metamodel->getElement(element_name);
			ed = dynamic_cast<coral::ElementDefinition*>(_ed.get());
		} catch (...) {
			;
		}
		L = state.metamodel;
	}
	if (!ed) {
		state.ignore_unknown_element(element_name, L);
		int err = state.handleUnknownElement(element_name);
		if (err) return err;
		state.switchF();
		throw handled();
	}
	/*
	 * Fix xmi.idref
	 */
	if (!state.parent_slots.empty()
	    && state.parent_slots.back()->getProperty()->isAssociation()) {
		return fixIdref_counted(state, element_name);
	}
	/*
	 * Create a new element, populate it with attributes.
	 */
	coral::Element* new_element = (*ed)(false);
	{
		coral::autounref<coral::Element> _t(new_element);
		if (!state.parent_slots.empty()) {
			coral::Slot* pf = state.parent_slots.back();
			/*
			 * bidirectional, since there shouldn't be an object
			 * attribute back to the parent element in a
			 * composition relation.
			 * But even if there is, it won't matter: check
			 * SlotOneElement::insert()
			 */
			pf->insert(new_element, -1, -1, false);
		} else {
			/*
			 * This unreffing needs to be done at some later time.
			 * Clearly, 99% of elements will run in the above code,
			 * only root elements will be here.
			 */
			state.unref_these.push_back(new_element);
			new_element->ref();
			/*
			 * Every new_element is a root candidate, not only
			 * elements of the root_ed type. This is done
			 * so that model fragments can be transported.
			 */
			new_element->__setProject__(state.storage);
		}
	}

	/*
	 * For <UML:Class .... /> elements
	 */
	if (!xmlTextReaderIsEmptyElement(state.reader)) {
		//dlog << DEBUGLEVEL_DEBUG << "Pushed E " << new_element->__type__()->getName() << std::endl;
		state.parent_elems.push_back(new_element);
	}

	while (xmlTextReaderMoveToNextAttribute(state.reader)) {
		wXmlTextReaderName wa_name(state.reader);
		const char* a_name = reinterpret_cast<const char*>(wa_name.get());
		coral_assert(a_name);
		wXmlTextReaderValue wa_value(state.reader);
		const char* _a_value = reinterpret_cast<const char*>(wa_value.get());
		if (!_a_value) continue; // for xmlns

		if (!strcmp(a_name, "xmi.id")) {
			state.addID(new _idref(_a_value), new_element);
			continue;
		} else if (!strcmp(a_name, "xmi.uuid")) {
			new_element->__setXMIid__(std::string(_a_value));
			/*
			 * We save the UUIDs as well, if somebody has
			 * an xmi.uuidref.
			 * (xmi.idref uses xmi.id)
			 */
			state.addID(new _uuidref(_a_value), new_element);
			continue;
		} else if (!strcmp(a_name, "xmi.label")) {
			/* Gather this for future XLinks... */
			state.addID(new _labelref(_a_value), new_element);
			continue;
		} else if (!strcmp(a_name, "href")) {
			return parse_href(state, _a_value);
		} else if (!strcmp(a_name, "xmi.uuidref")) {
			/* Allow in any XMI version... What harm can it do? */
			dropOld(state, new_element);
			return __fixIdref_counted(state, new _uuidref(_a_value));
		} else if (!strcmp(a_name, "xmi.idref")) {
			/*
			 * xmi.idrefs shouldn't occur here,
			 * but they do anyway. Yawn.
			 * We'll just drop the current element
			 * we created in vain.
			 */
			dropOld(state, new_element);
			return __fixIdref_counted(state, new _idref(_a_value));
		}

		/*
		 * Other attributes
		 */
		if (ed == XMI::Extension) {
			if (!strcmp(a_name, "xmi.extender"))
				a_name = "extender";
			else if (!strcmp(a_name, "xmi.extenderID"))
				a_name = "extenderID";
		}
		coral::Property* p = ed->getPropertyByName(a_name);
		if (!p) {
			state.ignore_property(a_name, ed->getName());
			continue;
		}
		const coral::AbstractElementDefinition* target
			= p->getTarget();
		coral_assert(target);
		int err;
		if (target->isDatatypeDefinition()) {
			err = state.fixDatatype(new_element,
						target,
						p,
						_a_value);
		} else {
			err = fixObjAttr(state,
					 new_element,
					 a_name,
					 _a_value);
		}
		if (err) return err;
	}
	return 0;
}

/*
 * Reads the metamodel if necessary. User can override this.
 */
int inspectLanguage(_state1& state) {
	/* User already defined a language? */
	if (state.metamodel) return 0;

	std::string mmname;
	std::string version;
	while (xmlTextReaderMoveToNextAttribute(state.reader)) {
		wXmlTextReaderName wa_name(state.reader);
		wXmlTextReaderValue wa_value(state.reader);
		const char* a_name = reinterpret_cast<const char*>(wa_name.get());
		const char* a_value = reinterpret_cast<const char*>(wa_value.get());
		coral_assert(a_name);
		if (!a_value) continue; // for xmlns
		if (!strcmp("xmi.name", a_name)) {
			mmname = a_value;
			continue;
		}
		if (!strcmp("xmi.version", a_name)) {
			version = a_value;
			continue;
		}
	}
	if (mmname.size() == 0 || version.size() == 0) {
		dlog << DEBUGLEVEL_ERROR
		     << "ERROR Invalid metamodel specified!"
		     << std::endl;
		return 1;
	}
	int ver = 0;
	int rev = 0;
	if (sscanf(version.c_str(), "%d.%d", &ver, &rev) != 2) {
		dlog << DEBUGLEVEL_ERROR
		     << "ERROR Invalid version string for"
		     << " metamodel specified!"
		     << std::endl;
		return 1;
	}
	coral::Language* metamodel;
	autounref<Language> _l
		= coral::Language::__getLanguage__(mmname, ver, rev, true);
	metamodel =_l.get();

	if (!metamodel) {
		dlog << DEBUGLEVEL_ERROR
		     << "ERROR Couldn't find language " << mmname
		     << " version " << version << "!" << std::endl;
		return 1;
	}
	state.metamodel = metamodel;
	_l.release();
	return 0;
}

int wait_for_xmi_field_begin(_state1& state) {
	do {
		int ret = xmlTextReaderRead(state.reader);
		if (ret != 1) return 1;
		int type = xmlTextReaderNodeType(state.reader);
		if (type == XML_ELEMENT_NODE)
			break;
		if (type == XML_ELEMENT_DECL) {
			throw handled_no_read();
		}
	} while (1);
	wXmlTextReaderLocalName welemname(state.reader);
	const char* elemname = 
		reinterpret_cast<const char*>(welemname.get());
	if (strcmp(elemname, "XMI.field")) return 1;
	return 0;
}

int wait_for_xmi_field_end(_state1& state) {
	do {
		int ret = xmlTextReaderRead(state.reader);
		if (ret != 1) return 1;
		if (xmlTextReaderNodeType(state.reader) == XML_ELEMENT_DECL)
			break;
	} while (1);
	wXmlTextReaderLocalName welemname(state.reader);
	const char* elemname = 
		reinterpret_cast<const char*>(welemname.get());
	if (strcmp(elemname, "XMI.field")) return 1;
	return 0;
}

int fill_one_mof_datatype(_state1& state, coral::ElementDefinition* ec) {
	coral::Slot* s = state.parent_slots.back();
	coral::Element* e = (*ec)(false);
	coral::autounref<ref_counted> _e(e);
	// really unidirectional, but we have anonymous properties
	// => false
	s->insert(e, -1, -1, false);
	const coral::ElementDefinition::ordered_properties_type& props
		= *(ec->getPropertiesInOrder());
	for (coral::ElementDefinition::ordered_properties_type::const_iterator i
		     = props.begin();
	     i != props.end();
	     ++i) {
		const Property* p = *i;
		if (p->isAnonymous()) continue;
		int err = wait_for_xmi_field_begin(state);
		if (err) {
			dlog << DEBUGLEVEL_ERROR
			     << "ERROR Expected <XMI.field>!" << std::endl;
			return err;
		}
		Slot* slot;
		try {
			coral::Slot& sslot = e->__getSlotByName__(p->getName());
			slot = &sslot;
		} catch (bad_slot& exc) {
			return 1;
		}
		state.parent_slots.push_back(slot);
		err = fixXMIField(state);
		state.parent_slots.pop_back();
		if (err) return err;
	}
	return 0;
}

int fixMOFDatatypes(_state1& state) {
	coral::Slot* s = state.parent_slots.back();
	coral::Property* p = s->getProperty();
	coral::AbstractElementDefinition* ed = p->getTarget();
	coral::ElementDefinition* ec = dynamic_cast<coral::ElementDefinition*>(ed);
	if (!ec) return 0;
	if (!ec->isMOFDatatype()) return 0;

	if (p->isAtMostOne()) {
		int err = fill_one_mof_datatype(state, ec);
		state.reading_slots = true;
		return err;
	}

	int already_read_one_mof_datatype = -1;
	for (;;) {
		try {
			wait_for_xmi_field_begin(state);
		} catch (handled_no_read&) {
			state.reading_slots = true;
			throw;
		} catch (...) {
			throw;
		}
		/*
		 * Ignore the latter two XMIDI.Points
		 */
		already_read_one_mof_datatype =
			(already_read_one_mof_datatype + 1) % 3;
		if (already_read_one_mof_datatype != 0) {
			state.handleUnknownElement("XMI.field");
			continue;
		}

		int err = fill_one_mof_datatype(state, ec);
		if (err) return err;
		err = wait_for_xmi_field_end(state);
		if (err) return err;
	}
}

int fixSlot(_state1& state, const char* propertyname) {
	if (state.parent_elems.empty()) {
		dlog << DEBUGLEVEL_ERROR
		     << "ERROR Trying to create a slot " << propertyname
		     << " but there is no parent element." << std::endl;
		return 1;
	}

	coral::Element* parent = state.parent_elems.back();
	const char* feature = strchr(propertyname, '.');

	/*
	 * Hack for UML 1.3. At least ArgoUML needs this.
	 */
	bool hack_argouml = false;
	if (!strncmp("Foundation.Core.", propertyname, 16)) {
		hack_argouml = true;
	}
	else if (!strncmp("Foundation.Extension_Mechanisms.", propertyname, 32)) {
		hack_argouml = true;
	}
	else if (!strncmp("Foundation.Data_Types.", propertyname, 22)) {
		hack_argouml = true;
	}
	else if (!strncmp("Behavioral_Elements.State_Machines.", propertyname, 35)) {
		hack_argouml = true;
	}
	else if (!strncmp("Behavioral_Elements.Collaborations.", propertyname, 35)) {
		hack_argouml = true;
	}
	else if (!strncmp("Behavioral_Elements.Common_Behavior.", propertyname, 36)) {
		hack_argouml = true;
	}
	else if (!strncmp("Behavioral_Elements.Activity_Graphs.", propertyname, 36)) {
		hack_argouml = true;
	}
	else if (!strncmp("Behavioral_Elements.Use_Cases.", propertyname, 30)) {
		hack_argouml = true;
	}

	if (hack_argouml) {
		// Skips Foundation.xxx.yyy.@propertyname crap
		feature = strchr(feature+1, '.');
		if (!feature) goto error;
		feature = strchr(feature+1, '.');
		if (!feature) goto error;
	}


	if (!feature) {
		goto error;
	}

	++feature;
	try {
		Slot& s = parent->__getSlotByName__(feature);
		/* This is for "<UML:ModelElement.name/>" */
		if (xmlTextReaderIsEmptyElement(state.reader)) {
			/* And this is for <UML:ModelElement.visibility xmi.value="public"/>*/
			const xmlChar* xmivalue;
			if (!(xmivalue = xmlTextReaderGetAttribute(state.reader, reinterpret_cast<const xmlChar*>("xmi.value"))))
				return 0;
			const char* a_value = reinterpret_cast<const char*>(xmivalue);
			/*
			 * Hack for ArgoUML
			 */
			if (!strcmp("branch", a_value)
			    && (!strcmp("Behavioral_Elements.State_Machines.Pseudostate.kind", propertyname))) {
				a_value = "choice";
			}

			return state.addAtom(parent,
					     parent->__type__()->getPropertyByName(feature),
					     std::string(a_value));
		}
		state.parent_slots.push_back(&s);
		int err = fixMOFDatatypes(state);
		return err;
	} catch (bad_slot& exc) {
		; // fall-thru
	}
 error:
	dlog << DEBUGLEVEL_WARNING
	     << "WARNING"
#if LIBXML_VERSION > 20507
	     << " Line "
	     << xmlGetLineNo(xmlTextReaderCurrentNode(state.reader))
#endif
	     << " Couldn't find property \""
	     << propertyname << "\"";
	if (feature) {
		dlog << " (" << feature << ")";
	}
	dlog << " in "
	     << parent->__type__()->getName() << std::endl
	     << "        with UUID "
	     << parent->__XMIid__()
	     << " (ignoring)."
	     << std::endl;
	int err = state.handleUnknownElement(propertyname);
	if (err) return err;
	state.switchF();
	throw handled();
}

int fixOutsideXMIextension(_state1& state,const char* name) {
	if (strcasecmp(name, "XMI.extension"))
		return 0;

	/*
	 * Don't use switchF();
	 * I'm unsure if it has side-effects.
	 */
	int err = fixSlot(state, "XMI.xmi_extension");
	if (err) {
		dlog << DEBUGLEVEL_WARNING
		     << "WARNING This means you'll lose all XMI extensions"
		     << " if you save your model now."
		     << std::endl;
		return err;
	}
	state.reading_slots = !state.reading_slots;
	return 0;
}

int fixInsideXMIextension(_state1& state) {
	if (state.parent_elems.empty())
		return 0;
	coral::Element* parent = state.parent_elems.back();
	if (parent->__type__() != XMI::Extension)
		return 0;

	if (!state.parent_slots.empty()) {
		/*
		 * Don't add the XMI.xmi_contents
		 * slot twice
		 */
		coral::Slot* s;
		s = state.parent_slots.back();
		if (s->getElement() == parent) {
			return 0;
		}
	}

	int err = fixSlot(state, "XMI.xmi_contents");
	if (err) return err;
	/*
	 * BUG At this point we should have a different routine.
	 * For our extensions, it should create the appropriate
	 * model elements (we only use model elements).
	 * For other's extensions, it should create XMI/XML elements
	 * which contain attributes and children, and keep xmi.id:s
	 * and xmi.idrefs straight. This is because we really can't
	 * tell what it will contain. libxml:s routines could perhaps
	 * be used for this?
	 * Good fun.
	 * 
	 */
	state.reading_slots = !state.reading_slots;
	return 0;
}

int processNode(_state1& state) {
	switch (xmlTextReaderNodeType(state.reader)) {
	/*
	 * Start XML element
	 */
	case XML_ELEMENT_NODE: {
		wXmlTextReaderName wname(state.reader);
		const char* name =
			(const char*) wname.get();
		if (!name) break;
		if (!state.start) {
			if (!strcasecmp(name, "XMI.content")) {
				state.start = true;
			} else if (!strcasecmp(name, "XMI.metamodel")) {
				return inspectLanguage(state);
			}
			// Skip XMI.header etc...
			break;
		}
		/*
		 * At this point, we should have a metamodel
		 */
		if (!state.metamodel) {
			wXmlTextReaderPrefix wa_prefix(state.reader);
			const xmlChar* _a_prefix = wa_prefix.get();
			wXmlTextReaderLookupNamespace w_ns(state.reader, _a_prefix);
			const char* ns =
				reinterpret_cast<const char*>(w_ns.get());

			autounref<Package> L(
				Package::__getLanguageByXMLNS__(ns)
				);
			state.metamodel = L.get();
			L.release();
			if (!ns) {
				dlog << DEBUGLEVEL_ERROR
				     << "ERROR No metamodel defined, although"
				     << " we are receiving elements!"
				     << std::endl;
				return 1;
			}
		}
		/*
		 * Hack! Ignore XMI.extension_s_
		 */
		if (!strcasecmp(name, "XMI.extensions")) {
			state.handleUnknownElement(name);
			return 0;
		}

		/*
		 * Hack! XMI.extension comes in and destroys
		 * everything for us...
		 * This is when we just are getting inside
		 * the XMI.extension.
		 */
		int err;
		err = fixInsideXMIextension(state);
		if (err) return err;
		/*
		 * Hack! And the case of entering XMI.extension
		 * from the outside
		 */
		err = fixOutsideXMIextension(state, name);
		if (err) return err;
		/*
		 * Assume an Element or a Slot starts here
		 */
		if (state.switchF()) {
			//dlog << DEBUGLEVEL_DEBUG << "Fixing " << name << "." << std::endl;
			return fixSlot(state, name);
		} else {
			wXmlTextReaderLocalName welemname(state.reader);
			const char* elemname = 
				(const char*) welemname.get();
			return createElem(state, elemname);
		}
		break;
	}
	/*
	 * End XML Element
	 */
	case XML_ELEMENT_DECL: {
		if (!state.start) break;
		wXmlTextReaderName wname(state.reader);
		const char* name =
			(const char*) wname.get();
		/*
		 * Hack! XMI.extension comes in and destroys
		 * everything for us...
		 */
		if (!strcasecmp(name, "XMI.extension")) {
			// XMI.xmi_contents
			if (!state.parent_slots.empty())
				state.parent_slots.pop_back();
			// XMI.extension
			if (!state.parent_elems.empty())
				state.parent_elems.pop_back();
			// and the AnyElement.xmi_extension
			// will be dropped below
		}

		if (state.switchF()) {
			if (!state.parent_elems.empty()) {
				//dlog << DEBUGLEVEL_DEBUG << "popped E " << state.parent_elems.back()->__type__()->getName()  << "." << std::endl;
				state.parent_elems.pop_back();
			}
		} else {
			if (!state.parent_slots.empty()) {
				//dlog << DEBUGLEVEL_DEBUG << "popped S " << state.parent_slots.back()->getProperty()->getName()  << "." << std::endl;
				state.parent_slots.pop_back();
			}
		}
	}
	case XML_TEXT_NODE: {
		/*
		 * This is for the following cases:
		 * <UML:ModelElement.name>MyName</UML:ModelElement.name>
		 * i.e. primitive attributes not specified as attributes
		 * but as text nodes inside an XML element.
		 */
		wXmlTextReaderValue wvalue(state.reader);
		const char* value =
			(const char*) wvalue.get();
		if (!value) break;
		if (!state.parent_slots.empty() && !state.parent_elems.empty()) {
			coral::Slot* s = state.parent_slots.back();
			coral::Property* p = s->getProperty();
			coral::AbstractElementDefinition* target
				= p->getTarget();
			if (target->isDatatypeDefinition()) {
				state.fixDatatype(state.parent_elems.back(),
						  target,
						  p,
						  value);
			}
		} else {
			/*
			dlog << DEBUGLEVEL_DEBUG << "Skipping text \"" << value
				  << "\"" << std::endl;
			*/
			;

		}
	}
	case XML_COMMENT_NODE: return 0;
	default: {
		wXmlTextReaderName wname(state.reader);
		wXmlTextReaderValue wvalue(state.reader);
		dlog << DEBUGLEVEL_WARNING
		     << "WARNING Unknown type "
		     << xmlTextReaderNodeType(state.reader)
		     << " with name " << wname.get()
		     << std::endl
		     << " and value "
		     << wvalue.get()
		     << std::endl
		     << " (ignoring, continuing loading)"
		     << std::endl;
		break;
	}
	case XML_DTD_NODE: return 0;
	}
	return 0;
}

NAMESPACE_END();

int _state1::loadIt() {
	coral_assert(reader);
	coral_assert(storage);
	int ret = 0;

	storage->setXMIFormat("1.2");

	/*
	 * Because process_no_xmi_vesion has already been active,
	 * we have to skip the first xmlTextReaderRead()
	 */
	bool skip = true;
	for (;;) {
		if (!skip) {
			ret = xmlTextReaderRead(reader);
			if (ret != 1) break;
		}
		skip = false;
		try {
			ret = processNode(*this);
		} catch (handled& h) {
			/* ok, these should be ignored. */
			ret = 0;
		} catch (handled_no_read& h) {
			ret = 0;
			skip = true;
		} catch (...) {
			dlog << DEBUGLEVEL_ERROR
			     << "ERROR Caught an exception. " << std::endl;
			ret = 1; 
		}
		if (ret) { break; }
	}

	if (ret) {
		dlog << DEBUGLEVEL_ERROR
		     << "ERROR An error occured during the XML parsing!"
		     << std::endl
	// BUG I wonder when it was first introduced
#if LIBXML_VERSION > 20507
		     << "      Occurred on line "
		     << xmlGetLineNo(xmlTextReaderCurrentNode(reader))
#else
		     << "      Couldn't determine line number, please upgrade libxml2"
#endif
		     << "." << std::endl;
		return 1;
	}

	return 0;
}

NAMESPACE_END();
NAMESPACE_END();
