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

#include <coral/core/ElementDefinition.h>

#include <list>

#include <coral/core/Element.h>
#include <coral/core/CorePrimitives.h>
#include <coral/core/config.h>
#include <coral/core/Project.h>
#include <sstream>
#include <Python.h>

#include <coral/lang/SMD01/smd2lang.h>
#include <coral/lang/SMD01/SMD01.h>
#include <coral/core/io/io.h>
#include <coral/core/xmlns_conversion_table.h>
#include <coral/lib/debug.h>


#include <algorithm>

NAMESPACE_BEGIN(coral);

typedef std::list<Package*> all_packages_t;
static all_packages_t all_packages;
INIT_MUTEX(mutex_languages);

bool crossesPresentationLayer(const AbstractElementDefinition* e1, const AbstractElementDefinition* e2) {
	bool p1 = e1->isPresentation();
	bool p2 = e2->isPresentation();
	return (!p1 && p2) | (p1 && !p2);
}


coral::autounref<coral::Element> Meta2ModelBridge::getCorrespondingElement() {
#if 0
	if (AbstractElementDefinition* ed
	    = dynamic_cast<AbstractElementDefinition*>(this)) {
		std::cerr << "getCorrespondingElement: " << ed->getName() << std::endl;
	}
#endif
	coral_assert(m_correspondingElement.get());
	m_correspondingElement->ref();
	return coral::autounref<coral::Element>(m_correspondingElement.get());
}

/*
 * AbstractElementDefinition
 */

AbstractElementDefinition::AbstractElementDefinition(Package* _package, ref_counted* type,
						     const char* name) : Meta2ModelBridge(), m_name(name) {
	this->package = _package;
	this->isAbstractED = false;
	if (this->package)
		this->package->addElement(this);
}

AbstractElementDefinition::~AbstractElementDefinition() throw () {
	dlog << DEBUGLEVEL_DEBUG << "Deleting type " <<  this->getName() << std::endl;
}

coral::Element* AbstractElementDefinition::newElement() {
	Atom* a = newAtom();
	Element* e = throwing_cast<Element*>(a);
	return e;
}

coral::autounref<coral::AbstractElementDefinition>
AbstractElementDefinition::getAbstractElementDefinitionFromSMD(const Element* e) {
	coral_assert(e);
	const Slot& s_name = e->__getSlotByName__("name");
	if (s_name.__len__() != 1) throw std::exception();
	const ElementPrimitive* prim
		= throwing_cast<ElementPrimitive*>(s_name.__getitem__(0));
	const unicodestring& realname(prim->__str__());

	e = e->__parent__();
	const AbstractElementDefinition* ed = e->__type__();
	const std::string& edname = ed->getName();
	if (edname == "Package" || edname == "Language") {
		const Slot& s_xmlns = e->__getSlotByName__("URInamespace");
		if (s_xmlns.__len__() != 1) throw std::exception();
		const ElementPrimitive* prim
			= throwing_cast<ElementPrimitive*>
			(s_xmlns.__getitem__(0));
		const unicodestring& u(prim->__str__());
		autounref<Package> pkg(
			Package::__getLanguageByXMLNS__(u.toUTF8()));
		coral_assert(pkg.get());
		Package* cur = pkg.get();
		autounref<AbstractElementDefinition> real_ed
			= cur->getElement(realname.toUTF8().c_str());
		if (real_ed.get()) return real_ed;
	}
	dlog << DEBUGLEVEL_ERROR
	     << "Corresponding AbstractElementDefinition of SMD Element not found!"
	     << " (" << e->__str__() << " given)"
	     << std::endl;
	coral_assert(0);
	throw std::exception();
	
}

void AbstractElementDefinition::__addOperation__(PyOperation* op) {
	this->operations.push_back(op);
	op->ref();
}

PyOperation* AbstractElementDefinition::getOperation(const std::string& name) {	
	for (operations_type::iterator i = operations.begin();
	     i != operations.end();
	     ++i) {
		if ((*i)->getFuncName() == name) {
			return *i;
		}
	}
	return NULL;
}
PyObject* AbstractElementDefinition::bindOperation_single(
	const std::string& name) {
	PyOperation* p = getOperation(name);
	if (p) return p->getFunc();
	return Py_None;
}

PyObject* AbstractElementDefinition::bindOperation(const std::string& name) {
	PyObject* ret = bindOperation_single(name);
	if (ret == Py_None) {
		for (topologic_all_superclasses_type::iterator i
			     = this->all_superclasses.begin();
		     i != this->all_superclasses.end();
		     ++i) {
			AbstractElementDefinition* super = *i;
			ret = super->bindOperation_single(name);
			if (ret != Py_None)
				break;
		}
	}
	Py_INCREF(ret);
	return ret;
}

PyObject* AbstractElementDefinition::__bindOperation__(const std::string& name,
					       PyObject* self) {
	PyObject* func = this->bindOperation(name);
	/*
	 * They call us in vain
	 */
	if (func == Py_None) {
		PyErr_Format(PyExc_AttributeError,
			     "Element object has no"
			     " attribute '%.400s'",
			     name.c_str());
		return NULL;
	}
	PyObject* _o = reinterpret_cast<PyObject*>(self->ob_type);
	PyObject* bound =
		PyMethod_New(func, self, _o);
	Py_DECREF(func);
	return bound;
}

void AbstractElementDefinition::__removeOperation__(PyObject* o) {
	coral_assert(o);
	coral_assert(o != Py_None);
	for (operations_type::iterator i = operations.begin();
	     i != operations.end();
	     ++i) {
		if ((*i)->getFunc() == o) {
			operations.erase(i);
			return;
		}
	}
}

bool AbstractElementDefinition::isSubclass(const AbstractElementDefinition* e) const throw () {
	if (e == this) return true;
	topologic_all_superclasses_type::const_iterator i
		= std::find(all_superclasses.begin(), all_superclasses.end(), e);
	return i != all_superclasses.end();
}

// slow
void AbstractElementDefinition::recalculateForMyselfAndSubclasses(
	std::set<AbstractElementDefinition*>& visited) {

	if (visited.find(this) != visited.end()) return;
	visited.insert(this);

	this->recalculateAllSuperclasses();

	const AbstractElementDefinition::subclasses_type& subs
		= this->getSubclasses();
	for (AbstractElementDefinition::subclasses_type::const_iterator i =
		     subs.begin();
	     i != subs.end();
	     ++i) {
		AbstractElementDefinition* ed = *i;
		ed->recalculateForMyselfAndSubclasses(visited);
	}
}

void AbstractElementDefinition::addSuperclass(AbstractElementDefinition* m) {
	coral_assert(m != this);
	if (m->isSubclass(this)) {
		dlog << DEBUGLEVEL_ERROR
		     << "ERROR These are already connected: "
		     << this->getName() << " " << m->getName()
		     << std::endl;
		coral_assert(0);
	}
	if (this->isSubclass(m)) {
		// OK, enables multiple inheritance
		;
	}

	if ((std::find(superclasses.begin(),
		       superclasses.end(),
		       m) != superclasses.end())
	    ||
	    (std::find(m->subclasses.begin(),
		       m->subclasses.end(),
		       this) != m->subclasses.end())) {
		dlog << DEBUGLEVEL_ERROR
		     << "ERROR Adding "
		     << this->__language__()->getName() << ":" << this->getName()
		     << "->"
		     << m->__language__()->getName() << ":" << m->getName()
		     << " twice?" << std::endl;
		coral_assert(0);
	}

	bool ok = true;
	if (dynamic_cast<const ElementDefinition*>(this)) {
		if (!dynamic_cast<const ElementDefinition*>(m)) {
			ok = false;
		}
	} else if (dynamic_cast<const EnumerationDefinition*>(this)) {
		if (!dynamic_cast<const EnumerationDefinition*>(m)) {
			ok = false;
		}
	} else if (dynamic_cast<const DatatypeDefinition*>(this)) {
		if (!dynamic_cast<const DatatypeDefinition*>(m)) {
			ok = false;
		}
	} else {
		dlog << DEBUGLEVEL_ERROR
		     << "ERROR Weird addSuperclass from "
		     << getName() << " to "
		     << m->getName() << std::endl;
		coral_assert(0);
	}
	if (!ok) {
		dlog << DEBUGLEVEL_ERROR
		     << "ERROR Adding a superclass to "
		     << getName() << " which is not an ElementDefinition!"
		     << std::endl
		     << "      Coral does not support this at the moment, so we're going"
		     << std::endl
		     << "      to fail!"
		     << std::endl;
	}


	this->superclasses.push_back(m);
	m->subclasses.push_back(this);
	/*
	 * Slow
	 */
	if (ElementDefinition* t = dynamic_cast<ElementDefinition*>(this)) {
		t->__propagateProperties(dynamic_cast<ElementDefinition*>(m));
	}
	/*
	 * Slow
	 */
	std::set<AbstractElementDefinition*> visited;
	this->recalculateForMyselfAndSubclasses(visited);
}

/*
 * e => set of x
 * means that everything in x must occur in result before e
 * can occur
 */
typedef std::multimap<const AbstractElementDefinition*,
		      const AbstractElementDefinition*> t_preferences;
	
// usage count for a particular ed
typedef std::map<const AbstractElementDefinition*, int> t_refs;
// which eds we already have visited
typedef std::set<const AbstractElementDefinition*> t_visited;
	
void recalculateAllSuperclassesHelper(const AbstractElementDefinition* self,
				      t_visited& visited,
				      t_preferences& pref,
				      t_refs& refs) {
	if (visited.find(self) != visited.end()) return;
	visited.insert(self);
	const AbstractElementDefinition* previous = self;
	const AbstractElementDefinition::superclasses_type& supers
		= self->getSuperclasses();
	for (AbstractElementDefinition::superclasses_type::const_iterator si
		     = supers.begin();
	     si != supers.end();
	     ++si) {
		const AbstractElementDefinition* e = *si;
		pref.insert(std::make_pair(e, previous));
		++refs[previous];
		previous = e;
		recalculateAllSuperclassesHelper(e,
						 visited,
						 pref,
						 refs);
	}
}

// slow
void AbstractElementDefinition::recalculateAllSuperclasses() {
	t_preferences preferences;
	t_refs refs;
	// and I couldn't figure out how to do it without visited.
	t_visited visited;
	recalculateAllSuperclassesHelper(this,
					 visited,
					 preferences,
					 refs);

#if 0
	if (this->getName() == "Classifier") {
		dlog << "CALC FOR " << this->getName() << std::endl;
		const AbstractElementDefinition::superclasses_type& supers
			= this->getSuperclasses();
		for (AbstractElementDefinition::superclasses_type::const_iterator si
			     = supers.begin();
		     si != supers.end();
		     ++si) {
			const AbstractElementDefinition* e = *si;
			dlog << "super " << e->getName() << std::endl;
		}
		dlog << "PREFS" << std::endl;
		for (t_preferences::iterator i = preferences.begin();
		     i != preferences.end(); ++i) {
			dlog << (*i).first->getName()
			     << " ---> "
			     << (*i).second->getName()
			     << std::endl;
		}
		dlog << "END" << std::endl;
	}
#endif
	int orig_size = preferences.size();

	topologic_all_superclasses_type result;
	while (preferences.size() > 0) {
		--orig_size;
		if (orig_size < -5) {
			const Language* L = this->__language__();
			dlog << DEBUGLEVEL_CRITICAL
			     << "ERROR Recalculating superclasses topology for " << L->getName() << "." << L->getVersion() << "." << L->getRevision() << ":" <<this->getName() << std::endl;
#if 0
			throw std::exception();
#else
			sleep(10);
			return;
#endif

		}
		for (t_preferences::iterator i = preferences.begin();
		     i != preferences.end(); ) {
			const AbstractElementDefinition* e = (*i).first;
			if (refs[e] != 0) {
				++i;
				continue;
			}
			result.push_front(const_cast<AbstractElementDefinition*>(e));
			refs.erase(e);
			for (;;) {
				const AbstractElementDefinition* target
					= (*i).second;
				--refs[target];
				coral_assert(refs[target] >= 0);
				preferences.erase(i++);
				if (i == preferences.end()) break;
				if ((*i).first != e) break;
			}
			coral_assert(preferences.find(e) == preferences.end());
			continue;
		}
	}
	all_superclasses.swap(result);
			    
}


bool AbstractElementDefinition::isPresentation() const throw () {
	return false;
}

Language* AbstractElementDefinition::__language__() const throw () {
	Package* p = this->__package__();
	return p->__language__();
}

bool AbstractElementDefinition::isRootDefinition() const throw () {
  if (!this->__language__()->getRoot())
    return false;
  else
    return this->isSubclass(this->__language__()->getRoot());
}


namespace {
	void inheritanceTree_helper(coral::Set* set, coral::AbstractElementDefinition* e) {
		if (set->includes(e)) return;
		if (!e->isAbstract()) {
			set->insert(e);
		}
		const coral::AbstractElementDefinition::subclasses_type& subs = e->getSubclasses();
		for (coral::AbstractElementDefinition::subclasses_type::const_iterator i = subs.begin();
		     i != subs.end();
		     ++i) {
			inheritanceTree_helper(set, *i);
		}
	}
}

autounref<coral::Set> inheritanceTree(coral::AbstractElementDefinition* e) {
	autounref<coral::Set> o(new coral::Set());
	inheritanceTree_helper(o.get(), e);
	return o;
}
	

/*
 * Package
 */

Package::Package(Package* _package, const std::string& _name) :
	AbstractElementDefinition(_package, NULL, _name.c_str()),
	elements()
{
	LockMutex l(mutex_languages);
		
#if 0
	for (all_packages_t::const_iterator i
		     = all_packages.begin();
	     i != all_packages.end();
	     ++i) {
		Package* L = *i;
		// BOGUS test
		if (P->getName() == model_name
		    && L->getVersion() == version
		    && L->getRevision() == revision) {
			dlog << DEBUGLEVEL_ERROR
			     << "ERROR Two languages "
			     << model_name << "."
			     << version << "."
			     << revision
			     << "; this is most likely an error!"
			     << " (continuing anyway)"
			     << std::endl;
		}
	}
#endif
	all_packages.push_front(this);
}

Package::~Package() throw () {
	dlog << DEBUGLEVEL_DEBUG << "DEBUG Deleting Package " << this->getName() << std::endl;
	LockMutex l(mutex_languages);
	all_packages.remove(this);
}

Atom* Package::newAtom() {
	coral_assert(0);
	return NULL;
}

std::string Package::__str__() const {
	std::string a;
	a += "Package ";
	a += getName();
	a += " (namespace ";
	a += getXMLNS();
	a += ")";
	return a;
}

void Package::setXMLNS(const std::string& newxmlns) {
	this->xmlns = newxmlns;
}

Language* Package::__language__() const throw () {
	const Package* p = this;
	const Language* l;
	for (;;) {
		l = dynamic_cast<const Language*>(p);
		if (l) break;
		p = p->__package__();
		if (!p) return NULL;
	}
	return const_cast<Language*>(l);
}

autounref<AbstractElementDefinition>
Package::getElement(const char* name) const throw (std::exception) {
	coral_assert(name);
	for (elements_type::const_iterator i = elements.begin();
	     i != elements.end();
	     ++i) {
		if (strcmp((*i)->getName().c_str(), name) == 0) {
			(*i)->ref();
			return autounref<AbstractElementDefinition>(*i);
		}
	}
	for (elements_type::const_iterator i = imported_elements.begin();
	     i != imported_elements.end();
	     ++i) {
		Package* p = dynamic_cast<Package*>(*i);
		if (p) {
			coral_assert(p != this);
			try {
				autounref<AbstractElementDefinition> e(p->getElement(name));
				coral_assert(e.get());
				return e;
			} catch (...) {
				;
			}
		}
		if (strcmp((*i)->getName().c_str(), name) == 0) {
			(*i)->ref();
			return autounref<AbstractElementDefinition>(*i);
		}
	}
	std::ostringstream s;
	s << "No such metaclass "
	  << name
	  << " in " << getName() << "!";
	throw coral_error(s.str());
}

void Package::addElement(AbstractElementDefinition* mm) {
	coral_assert(mm);
	coral_assert(mm != this);
	coral_assert(std::find(elements.begin(), elements.end(), mm) == elements.end());
	elements.push_back(mm);
	mm->ref();
}

void Package::removeElement(AbstractElementDefinition* mm) {
	coral_assert(mm);
	coral_assert(mm != this);
	coral::autounref<ref_counted> t(mm);
	elements_type::iterator i =
		std::find(elements.begin(),
			  elements.end(),
			  mm);
	if (i != elements.end()) {
		elements.remove(mm);
		return;
	}
		
	elements_type::iterator j =
		std::find(imported_elements.begin(),
			  imported_elements.end(),
			  mm);
	if (j != imported_elements.end()) {
		imported_elements.remove(mm);
		return;
	}

	coral_assert(0);
}

void Package::importElement(AbstractElementDefinition* mm) {
	coral_assert(mm);
	coral_assert(mm != this);
	coral_assert(std::find(elements.begin(),
			       elements.end(),
			       mm) == elements.end());
	coral_assert(std::find(imported_elements.begin(),
			       imported_elements.end(),
			       mm) == imported_elements.end());
	imported_elements.push_back(mm);
	mm->ref();
}

/*
 * Language
 */

autounref<Language> Package::tryDynLoad(const std::string& filename) {
	/*
	 * Dynamically load new language from SMD model
	 * and convert it to a language.
	 */
	const char* c = filename.c_str();
	autounref<Project> metamodel_as_xmi(NULL);
	try {
		metamodel_as_xmi = coral::io::loadDocument(c);
	} catch (coral::load_error) {
		;
	}
		
	if (metamodel_as_xmi.get()) {
		metamodel_as_xmi->setUserProject(false);
		ref_counted* _e;
		_e = metamodel_as_xmi->contents()->__getitem__(0);
		Element* e = static_cast<Element*>(_e);
		return SMD01::smd2lang(e);
	}
	return autounref<Language>(NULL);
}

autounref<Language> Language::__getLanguage__(
	const std::string& name,
	int ver, int rev,
	bool allowdynamicloading)
	throw (coral::language_not_found_error, std::exception) {
#if 0
	dlog << DEBUGLEVEL_DEBUG << "DEBUG: __getLanguage__ looking for "
	     << name << " " << ver << " " << rev
	     << (allowdynamicloading ?
		 " (allowing dynamic loading)"
		 : " (not allowing dynamic loading)" )
	     << std::endl;
#endif
	autounref<Language> l;
	{
		LockMutex _l(mutex_languages);
		for (all_packages_t::const_iterator i
			     = all_packages.begin();
		     i != all_packages.end();
		     ++i) {
			Language* L = dynamic_cast<Language*>(*i);
			if (!L) continue;
			if ((L->getName() == name)
			    && (!ver || L->version == ver)
			    && (!rev || L->revision == rev)) {
				l = L;
				l->ref();
				break;
			}
		}
	}


	if (!l && allowdynamicloading) {
		int db_size = xmlns_db_size();
		std::string mmfilename;
		for (int i = 0; i < db_size; ++i) {
			std::string db_name = xmlns_db_metamodelname(i);
			if (name != db_name)
				continue;
			int db_version = xmlns_db_version(i);
			int db_revision = xmlns_db_revision(i);
			if (ver && db_version != ver)
				continue;
			if (rev && db_revision != rev)
				continue;
			mmfilename = xmlns_db_filename(i);
			break;
		}
		if (mmfilename.empty()) {
			std::ostringstream mm_as_xmi_name;
			mm_as_xmi_name << installation_path
				       << "/coral/lang/" << name << ver
				       << rev << "/def/" << name << ver
				       << rev << ".xmi";
			mmfilename = mm_as_xmi_name.str();
		}
		l = tryDynLoad(mmfilename);
	}
	if (!l) {
		std::ostringstream s;
		s << "Language " << name
		  << " version " << ver << "." << rev
		  << " could not be found.";
		throw coral::language_not_found_error(s.str());
	}
	return l;
}

autounref<Package> Package::__getLanguageByXMLNS__(
	const std::string& xmlns,
	bool allowdynload)
	throw (coral::language_not_found_error, std::exception) {
	/*
	 * This is so we always check
	 * the available languages, even though the conversion
	 * table wouldn't work.
	 */
	autounref<Language> tmp(NULL);
again:
	{
		LockMutex l(mutex_languages);
		for (all_packages_t::const_iterator i
			     = all_packages.begin();
		     i != all_packages.end();
		     ++i) {
			Package* L = *i;
			if (L->getXMLNS() == xmlns) {
				L->ref();
				return autounref<Package>(L);
			}
		}
	}
	if (allowdynload) {
		struct xmlns_conversion_entry t;
		t.xmlns = xmlns.c_str();
		bool success = getXMLNSRmap(&t);
		if (success && !t.smd_model_filename.empty()) {
			/*
			 * Subtle. @l is a Language, but we might care
			 * for the Package that is inside the Language.
			 * Therefore roundtrip again to getLanguage
			 */
			tmp = tryDynLoad(t.smd_model_filename);
			allowdynload = false;
			goto again;
		}
	}

	std::ostringstream s;
	s << "Language with namespace " << xmlns
	  << " could not be found";
	throw coral::language_not_found_error(s.str());
}


Language::Language(const std::string& model_name,
		   int _version,
		   int _revision) : Package(NULL, model_name) {
	this->version = _version;
	this->revision = _revision;
	this->root_ed = NULL;

}

Language::~Language() throw () {
	;
}


void Language::setRoot(ElementDefinition* e) throw () {
	this->root_ed = e;
}

ElementDefinition* Language::getRoot() const throw () {
	return this->root_ed;
}


/*
 * ElementDefinition
 */

ElementDefinition::ElementDefinition(Package* metamodel,
		 const char* name) :
	AbstractElementDefinition(metamodel, NULL, name) {
	MOFDatatype = false;
}

ElementDefinition::~ElementDefinition() throw () {
	;
}

void ElementDefinition::__propagateProperties(ElementDefinition* super) {
	if (!super) return;
	for (ElementDefinition::properties_type::const_iterator i
		     = super->properties.begin();
	     i != super->properties.end();
	     ++i) {
		this->addPropertyRecursive((*i).second);
	}
}

void ElementDefinition::addProperty(Property* f) {
	coral_assert(f);
	coral_assert(this->properties.find(f->getName())
		     == this->properties.end());
	if (!f->owner) {
		f->owner = this;
		Property* oppo = f->getOppositeProperty();
		if (oppo && oppo->isAnonymous()) {
			oppo->element_kind = this;
		}
	}
	ordered_properties.push_back(f);
	try {
		this->properties[f->getName()] = f;
	} catch (...) {
		ordered_properties.pop_back();
		throw;
	}
	f->ref();
}

void ElementDefinition::addPropertyRecursive(Property* f) {
	coral_assert(f);
	/* Only add if it doesn't exist */
	coral_assert(f->getName());
	if (!this->getPropertyByName(f->getName())) {
		this->addProperty(f);
	} else if (this->getPropertyByName(f->getName()) == f) {
		; // ok
	} else {
		dlog << DEBUGLEVEL_DEBUG << "SKIP " << this->getName() << ":" << f->getName() << " --- SOMETHING IS WRONG!" << std::endl;
		coral_assert(0);
	}
	for (AbstractElementDefinition::subclasses_type::const_iterator i =
		     subclasses.begin();
	     i != subclasses.end();
	     ++i) {
		ElementDefinition* e =
			dynamic_cast<ElementDefinition*>(*i);
		coral_assert(e);
		e->addPropertyRecursive(f);
	}
}

bool ElementDefinition::_isWellFormed(/*const*/ Element* e) const throw() {
	coral_assert(e->oclIsKindOf(this));
	/*
	 * Check property multiplicity constraints
	 */
	for (ElementDefinition::properties_type::const_iterator
		     i = properties.begin();
	     i != properties.end();
	     ++i) {
		Property* p = (*i).second;
		if (p->isAnonymous()) continue;
		Slot* s = e->__hasSlotByName__(p->getName());
		int lower = p->getMultLower();
		int upper = p->getMultUpper();
		if (!s) {
			if (lower != 0) {
				/*
				 * Special case since
				 * these are created
				 * lazily.
				 */
				if (p->isAtMostOne()
				    && p->getTarget()->isDatatypeDefinition())
					continue;
				dlog << DEBUGLEVEL_INFO << "WFR Multiplicity violation for " << p->getName() << " " << e->__str__() << " (no slot)" << std::endl;
				return false;
			}
			continue;
		}
		int l = s->size();
		if (l < lower || (upper != -1 && l > upper)) {
			dlog << DEBUGLEVEL_INFO << "WFR Multiplicity violation for " << p->getName() << " " << e->__str__() << " [" << lower << ", " << upper << "], but had " << l << "." << std::endl;
			return false;
		}
	}

	/*
	 * Check well-formed rules.
	 */
	for (operations_type::const_iterator i =
		     this->operations.begin();
	     i != this->operations.end();
	     ++i) {
		PyOperation* op = *i;
		coral_assert(op);
		if (!op->isWFR()) continue;
		PyObject* ret = NULL;
		try {
			dlog << DEBUGLEVEL_INFO
			     << "CALLING " << op->getFuncName() << std::endl;
			ret = op->__call__(e);
			if (PyErr_Occurred()) {
				dlog << DEBUGLEVEL_WARNING
				     << "WFR Error in Python code" << std::endl;
				PyErr_Print();
			}
		} catch (...) {
			ret = Py_None;
			Py_INCREF(ret);
		}
		if (ret == Py_True) {
			Py_DECREF(ret);
			continue;
		}

		Py_XDECREF(ret);
			
		dlog << DEBUGLEVEL_INFO << "The well-formed rule "
		     << this->getName() << ":" << op->getFuncName()
		     << " failed for element "
		     << e->__str__() << "." << std::endl;
		return false;
	}
	return true;
}

const ElementDefinition::properties_type*
ElementDefinition::getProperties()
	const throw () {
	return &this->properties;
}

const ElementDefinition::ordered_properties_type*
ElementDefinition::getPropertiesInOrder()
	const throw () {
	return &this->ordered_properties;
}

void ElementDefinition::__setCorrectPropertiesOrder(ordered_properties_type& t) {
	if (t.size() != ordered_properties.size()) {
		dlog << DEBUGLEVEL_DEBUG << "ELEMENT " << this->getName() << std::endl;
		dlog << DEBUGLEVEL_DEBUG << "NEW" << std::endl;
		for (std::vector<Property*>::const_iterator i = t.begin();
		     i != t.end();
		     ++i) {
			dlog << DEBUGLEVEL_DEBUG << (*i)->getName() << std::endl;
		}
		dlog << DEBUGLEVEL_DEBUG << "OLD" << std::endl;
		for (std::vector<Property*>::const_iterator i = ordered_properties.begin();
		     i != ordered_properties.end();
		     ++i) {
			dlog << DEBUGLEVEL_DEBUG << (*i)->getName() << std::endl;
		}
	}
	coral_assert(t.size() == ordered_properties.size());
	ordered_properties.swap(t);
}

Property* ElementDefinition::getPropertyByName(const char* name)
	const throw () {
	coral_assert(name);

	properties_type::const_iterator i =
		this->properties.find(name);
	if (i != this->properties.end()) {
		return (*i).second;
	}
	//std::cout << "Couldn't find feature " << name << std::endl;
	return NULL;
}

Property* ElementDefinition::getPropertyByOpposite(const ElementDefinition* oppo,
					 const char* opponame) {
	coral_assert(oppo);
	coral_assert(opponame);
	Property* p = oppo->getPropertyByName(opponame);
	if (!p) return NULL;
	p = p->getOppositeProperty();
	coral_assert(p);
	coral_assert(p->isAnonymous());
	return p;
}

Element* ElementDefinition::operator()(bool gen_element_attributes) {
	coral_assert(!isAbstractED);
	return new Element(this, gen_element_attributes);
}
Atom* ElementDefinition::newAtom() {
	return (*this)();
}

bool ElementDefinition::isPresentation() const throw () {
	return this->isSubclass(CORE::PresentationElement);
}

std::string ElementDefinition::__str__() const {
	std::string s;
	s += "ElementDefinition ";
	s += getName();
	return s;
}

/*
 * Property
 */

void Property::__init(const char* _key, AbstractElementDefinition* _element_kind,
		      const char* other_key,
		      int m_lower, int m_upper,
		      kind_t k,
		      const std::string& default_value
	) {
	owner = NULL;
	key = _key;
	element_kind = _element_kind;
	kind = k;
	mult_lower = m_lower;
	mult_upper = m_upper;
	other = NULL;
	m_default_value = default_value;
	read_only_slot = newSlot(NULL);

	if (!this->isAssociation() && !this->isComposition()) {
		dlog << DEBUGLEVEL_ERROR
		     << "ERROR Property " << this->getName()
		     << " is neither an association nor a composition!" << std::endl;
		coral_assert(0);
	}

	if (k.isAnonymous())
		return;

	if (!this->isBidirectional() && (other_key && other_key[0] != '\0')) {
		dlog << DEBUGLEVEL_ERROR
		     << "ERROR Property is not marked bidirectional but has an opposite!"
		     << " (setting it to bidirectional, continuing)"
		     << std::endl;
		this->kind |= kind_t::KIND_BIDI;
	}

	Property* o = this->__findOtherProperty(element_kind,
						other_key);
	if (o) {
		// CRL-444 throw if association has > 1 multiplicity and opposite
		// is a composite, i.e. we don't allow multiple parents in the
		// metamodel definition
		if ((!isComposition()
		     && (mult_upper > 1 || mult_upper == -1)
		     && o->isComposition())
		    || (!o->isComposition()
			&& (o->mult_upper > 1 || o->mult_upper == -1)
			&& isComposition())) {
			std::ostringstream s;
			s << "Either of "
			  << key << " <- " << element_kind->getName() << "." << other_key
			  << " can have multiple parents!"
			  << " (i.e multiplicity_upper > 1 for the association)"
			  << std::endl;
			throw std::runtime_error(s.str());
		}

		if (!(o->other == NULL || o->other == this)) {
			if (o->other) {
				dlog << DEBUGLEVEL_ERROR
				     << "ERROR o->other == " << o->other->getName()
				     << " (for element " << _element_kind->__str__() << ")" << std::endl
				     << " this == " << this->getName()
				     << " ought to be == " << other_key << " and " << key << std::endl
				     << " We still have weirdo overridden properties" << std::endl;
			}
		}
		this->other = o;
		o->other = this;
		bool b;

		b = this->isBag() ^ o->isBag();
		if (b) {
			std::ostringstream s;
			s << "ERROR differing bag/set for properties "
			  << key << " <- " << element_kind->getName() << "." << other_key
			  << std::endl;
			throw std::runtime_error(s.str());
		}

		b = this->isComposition() && o->isComposition();
		if (b) {
			std::ostringstream s;
			s << "ERROR both Properties are composites: "
			  << key << " <- " << element_kind->getName() << "." << other_key
			  << std::endl;
			throw std::runtime_error(s.str());
		}
	} else {
		if (other_key
		    && isBidirectional()
		    && !strcmp(_key, other_key)) {
			dlog << DEBUGLEVEL_INFO
			     << "INFO assuming property " << key
			     << " is \"doubled\"." << std::endl;
			this->other = this;
		} else {
			this->other = NULL;
		}
		if ((!other_key || (other_key[0] == '\0'))
		    && (!this->getTarget()->isDatatypeDefinition())) {
			__makeAnonymousProperty();
		}
	}

}

Property::~Property() throw () {
	dlog << DEBUGLEVEL_WARNING
	     << "Deleting property: " << this << " ("
	     << this->getName() << ")" <<std::endl;
	;
	delete read_only_slot;
}

Property::Property(const char* _key, AbstractElementDefinition* elem_kind,
		   const char* other_key,
		   int multiplicity_lower, int multiplicity_upper,
		   kind_t k,
		   const std::string& default_value)
{
	__init(_key, elem_kind, other_key, multiplicity_lower,
	       multiplicity_upper, k, default_value);
}

void Property::__makeAnonymousProperty() {
	ElementDefinition* e = dynamic_cast<ElementDefinition*>(this->getTarget());
	coral_assert(e);
	static int anon_runner = 0;
	std::ostringstream o;
	// d'Oh getOwner() isn't set at this point :-(
	o << "__coral_anon_"
	  << getName()
	  << "_"
	  << ++anon_runner;
	coral_assert(this->getOppositeProperty() == NULL);
	if (this->isBidirectional()) {
		dlog << DEBUGLEVEL_ERROR << "ERROR anon for "
		     << this->getName() << " -> "
		     << e->getName() << " not possible."
		     << std::endl;
	}
	coral_assert(!this->isBidirectional());
	/*
	 * Not bidirectional by definition.
	 */
	kind_t k(kind_t::KIND_ASSOCIATION
		 | kind_t::KIND_ANONYMOUS
		 | kind_t::KIND_NOSERIALIZE);
	if (this->isBag())
		k |= kind_t::KIND_BAG;
	int lower = 0;
	int upper = -1;
	if (this->isComposition()) {
		upper = 1;
	}
	autounref<Property> p(new Property(o.str().c_str(),
					   this->getOwner(),
					   this->getName(),
					   lower, upper,
					   k,
					   ""));
	this->other = p.get();
	p.get()->other = this;
	e->addPropertyRecursive(p.get());
}

Property* Property::__findOtherProperty(AbstractElementDefinition* other_ed,
					const char* other_key)
	const throw () {
	coral_assert(other_ed);
	if (other_ed->isDatatypeDefinition() || !other_key) return NULL;
	coral_assert(other_key);
	const ElementDefinition* ed
		= static_cast<const ElementDefinition*>(other_ed);
	return ed->getPropertyByName(other_key);
}

Slot* Property::newSlot(Element* e) throw (std::bad_alloc) {
	Slot* f;
	if (!this->isAtMostOne()) {
		coral::autounref<Collection>
			c(Collection::newCollection(this->isBag(),
						    this->isOrdered()));
		f = new SlotManyElements(e, this, c.get());
		c.release();
	} else {
		f = new SlotOneElement(e, this);
	}
	return f;
}

std::string Language::__str__() const {
	std::ostringstream a;
	a << "Language " << getName()
	  << " " << getVersion() << "." << getRevision() << " (namespace " << getXMLNS() << ")";
	return a.str();
}

NAMESPACE_END();

