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

#include <coral/core/Element.h>
#include <coral/core/Collection.h>
#include "ElementIterator.h"
#include <coral/core/CorePrimitives.h>

#include <coral/lib/assert.h>
#include <cstdio>

#include <iostream>
#include <memory>
#include <list>

#include <coral/core/util/TransactionManager.h>
#include <coral/core/Project.h>
#include <coral/core/util/uElement.h>

#include <coral/lib/debugstream.h>

#include <coral/lib/basicmath.h>

#include <Python.h>

#include <unistd.h>

PyObject* getElementPyObject(const coral::Element* ptr);

NAMESPACE_BEGIN(coral);
INIT_MUTEX(mutex_modelelements);

all_modelelements_type all_modelelements;

std::size_t all_modelelements_size() {
	return all_modelelements.size();
}

void invalidatePythonSingleSlot(Element* e, const char* slotname) {
	PyObject* obj = getElementPyObject(e);
	if (!obj || obj == Py_None) return;
	PyObject** dict = _PyObject_GetDictPtr(obj);
	if (!dict || !*dict || *dict == Py_None) return;
	PyObject* slotname_key = PyString_FromString(slotname);
	coral_assert(slotname_key); // BUG
	int err = PyDict_DelItem(*dict, slotname_key);
	if (err) {
		PyErr_Clear();
	}
		
}


/*
 * Atom
 */

Atom::Atom(AbstractElementDefinition* type) : ref_counted(), m_type(type) {
	;
}

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

autounref<Set> Atom::__asSet__() throw (std::exception) {
	autounref<Set> m(new Set());
	m->add(this);
	return m;
}

/*
 * Element
 */

Element::Element(ElementDefinition* type,
		 bool generate_element_attributes)
	: Atom(type), List(), m_uuid() {

	coral_assert(type != NULL);

	if ((Transaction::theTM) && (Transaction::theTM->internalIsActive())) {
		Transaction::ElementNewCommand* x;
		MUST_WORK(x = new Transaction::ElementNewCommand(this));
		Transaction::theTM->partialModifyCmd(x);
	}


	if (generate_element_attributes) {
		/* NOTE! BUG! We may need to clean up transactions
		 * if anything throws. */
		const ElementDefinition::properties_type*
			mmproperties =
			this->__type__()->getProperties();
		for (ElementDefinition::properties_type::const_iterator
			     i = mmproperties->begin();
		     i != mmproperties->end();
		     ++i) {
			Property* mm = (*i).second;
			if (!mm->isComposition())
				continue;
			if (!mm->isMultSingle())
				continue;
			/* Primitives are created automatically
			 * with default values. */
			AbstractElementDefinition* md
				= mm->getTarget();
			if (md->isDatatypeDefinition())
				continue;
			if (md->isAbstract())
				continue;
			Slot& mf = this->createSlot(mm);
			ElementDefinition* me =
				static_cast<ElementDefinition*>(md);
			/* Recursively create its attributes */
			Element* child = (*me)(true);
			coral::autounref<ref_counted> r(child);
			mf.insert(child);
		}
	}

	LockMutex l(mutex_modelelements);
	all_modelelements.add(this);
}

Element::~Element() throw () {
	coral_assert(this->get_ref_count() == 0);

	Project::setProject(this, NULL);

	LockMutex l(mutex_modelelements);
	this->drop();
}

bool Element::__switchType__(ElementDefinition* type) {
	/*
	 * BUG! TRANSACTIONS!
	 */
	coral_assert(type);
	/* Same type? */
	if (type == this->__type__()) return true;
	for (slots_type::iterator i = this->slots.begin();
	     i != slots.end();
		) {
		slots_type::iterator old = i;
		++i;
		Property* mmf = (*old).second->getProperty();
		coral_assert(mmf);
		if (mmf == type->getPropertyByName(mmf->getName()))
			continue;
		invalidatePythonSingleSlot(this, mmf->getName());
		delete (*old).second;
		this->slots.erase(old);
	}
	m_type = type;
	return true;
}

bool Element::isWellFormed() /* const */ {
	bool ok = this->__type__()->_isWellFormed(this);
	if (!ok) return ok;

	const AbstractElementDefinition::topologic_all_superclasses_type& 
		all_supers = __type__()->getAllSuperclasses();

	for (AbstractElementDefinition::topologic_all_superclasses_type::const_iterator i =
		     all_supers.begin();
	     i != all_supers.end();
	     ++i) {
		const AbstractElementDefinition* edsuper = *i;
		const ElementDefinition* super
			= dynamic_cast<const ElementDefinition*>(edsuper);
		if (!super) continue;
		if (!super->_isWellFormed(this)) return false;
	}
	return true;
}

bool Element::isWellFormedRecursive() /* const */ {
	autounref<Set> o(__getAllParts__());
	ref_counted* r;
	Iterator i(o.get());
	while ((r = i.next()) != NULL) {
		Element* e = dynamic_cast<Element*>(r);
		if (!e) continue;
		bool ok = e->isWellFormed();
		if (!ok) return false;
	}
	return true;
}


Slot* Element::__parentSlot__() const throw () {
	for (slots_type::const_iterator i = this->slots.begin();
	     i != slots.end();
	     ++i) {
		Slot* s = (*i).second;
		coral_assert(s);
		// Parent can only be exactly one element.
		if (s->size() != 1) continue;
		Property* p = s->getProperty();
		coral_assert(p);
		if (!p->isAssociation())
			continue;
		if (!p->isAtMostOne())
			continue;
		if (p->getTarget()->isDatatypeDefinition())
			continue;
		Property* other = p->getOppositeProperty();
		coral_assert(other);
		if (!other->isComposition())
			continue;
		return s;
	}
	return NULL;
}

Element* Element::__parent__() const throw () {
	Slot* s = __parentSlot__();
	if (!s) return NULL;
	return static_cast<Element*>((*s)[0]);
}

Element* Element::__getRoot__() const throw (coral::coral_error) {
	const coral::Element* p = this;
	std::set<const Element*> old_children;
	(void) old_children.insert(this);
	for (;;) {
		const coral::Element* _p = p->__parent__();
		if (!_p) break;
		p = _p;
		if (old_children.insert(p).second) {
			continue;
		}
		std::ostringstream s;
		s << "Circular ownership detected!"
		  << std::endl
		  << "Unordered list of elements follows:"
		  << std::endl;
		for (std::set<const Element*>::const_iterator i = old_children.begin();
		     i != old_children.end();
		     ++i) {
			s << "Element " << (*i)
			  << " " << (*i)->__XMIid__()
			  << std::endl;
		}
		dlog << DEBUGLEVEL_ERROR
		     << "Circular ownership detected!"
		     << std::endl;
		throw coral_error(s.str());
	}
	return const_cast<Element*>(p);
}

coral::autounref<coral::Set> Element::__getAllParts__() {
	coral::autounref<Set> m(new Set());
	AtomIterator i(this->DFSIterator());
	Atom* a;
	while ((a = i.next()) != NULL) {
		if (a->__type__()->isDatatypeDefinition()) continue;
		if (m->includes(a)) {
			dlog << DEBUGLEVEL_DEBUG << "ERROR IN ATOMITERATOR!"
			     << "Already have " << a << std::endl;
			if (Element* e = dynamic_cast<Element*>(a)) {
				dlog << DEBUGLEVEL_DEBUG << "UUID is " << e->__XMIid__() << std::endl;
			}
			i.discardCurrentBranch();
			continue;
		}
		m->insert(a);
	}
	return m;
}

AtomIterator Element::DFSIterator() {
	return AtomIterator(this, AtomIterator::TYPE_DFS);
}

bool Element::__isPart__(const Atom* obj, bool recurse) const throw () {
	if (obj->__type__()->isDatatypeDefinition()) {
		return this == obj;
	} else {
		const Element* o = static_cast<const Element*>(obj);
		return this->__isPart__(o, recurse);
	}
}


bool Element::__isPart__(const Element* obj, bool recurse)
	const throw () {
	if (!obj) return false;
	if (obj == this) return true;
	for (slots_type::const_iterator i = obj->slots.begin();
	     i != obj->slots.end();
	     ++i) {
		Slot* mf = (*i).second;
		Property* mmf = mf->getProperty();
		if (!mmf->isComposition()) continue;

		Iterator m(mf);
		const ref_counted* parent;
		while ((parent = m.next()) != NULL) {
			if (parent == this) return true;
			if (!recurse)
				continue;
			if (!dynamic_cast<const Atom*>(parent))
				continue;
			if (this->__isPart__(static_cast<const Atom*>(parent)))
				return true;
		}
	}
	return false;
}

bool Element::__isConnectedTo__(const Element* obj) const throw () {
	return this->__isPart__(obj)
		|| obj->__isPart__(this);
}

Project* Element::__getProject__() const throw () {
	return coral::Project::getProject(this);
}

void Element::__setProject__(Project* s) throw () {
	if ((Transaction::theTM) && (Transaction::theTM->internalIsActive())) {
		Project* old = __getProject__();
		if (old == s) return;
		Transaction::ProjectChangeCommand* x;
		MUST_WORK(x =
			  new Transaction::ProjectChangeCommand(this, old));
		Transaction::theTM->partialModifyCmd(x);
	}
	MUST_WORK(coral::Project::setProject(this, s));
}

Project* Element::__getRootsProject__() const throw (coral::coral_error) {
	return this->__getRoot__()->__getProject__();
}

void Element::__setXMIid__(const uuid_t& s) {
	if ((Transaction::theTM) && (Transaction::theTM->internalIsActive())) {
		Transaction::ElementUUIDCommand* x;
		MUST_WORK(x = new Transaction::ElementUUIDCommand(this, this->__XMIid__()));
		Transaction::theTM->partialModifyCmd(x);
	}
	MUST_WORK(m_uuid.__setXMIid__(s));
}

Slot& Element::createSlot(Property* mm)
	throw (coral::bad_slot, std::exception) {
	coral_assert(mm);
	std::auto_ptr<Slot> mf(mm->newSlot(this));
	this->slots[mm] = mf.get();
	return *mf.release();
}

Slot* Element::__hasSlotByName__(const char* name) const throw () {
	const Property* p = this->__type__()->getPropertyByName(name);
	slots_type::const_iterator i = this->slots.find(p);
	if (i != this->slots.end()) {
		return (*i).second;
	}
	return NULL;
}

const Slot& Element::__getSlotRO__(const char* name) const throw (bad_slot) {
	Slot* s = __hasSlotByName__(name);
	if (s) return *s;
	Property* p = this->__type__()->getPropertyByName(name);
	if (!p) {
		std::string s("Bad slot ");
		s += name;
		throw coral::bad_slot(s);
	}
	return *p->getROSlot();
}

Slot& Element::__getSlotByName__(const char* name)
	throw (coral::bad_slot, std::exception) {
	Property* p = this->__type__()->getPropertyByName(name);
	if (!p) {
		std::string s("Bad slot ");
		s += name;
		throw coral::bad_slot(s);
	}
	slots_type::const_iterator i = this->slots.find(p);
	if (i != this->slots.end()) {
		return *(*i).second;
	}
	/* Not yet created? */

	return createSlot(p);
}
Slot& Element::__getSlotByOppositeName__(const coral::ElementDefinition* ed,
					 const char* fname)
	throw (coral::bad_slot, std::exception) {
	coral_assert(ed);
	coral_assert(fname);
	ElementDefinition* target = dynamic_cast<ElementDefinition*>(this->__type__());
	coral_assert(target);
	Property* p = target->getPropertyByOpposite(ed, fname);
	coral_assert(p);
	return __getSlotByName__(p->getName());
}

void Element::__getSlotByNameInsert__(const char* name, PyObject* o)
	throw (std::invalid_argument, std::exception)
{
	coral_assert(o);
	Slot& mf = this->__getSlotByName__(name);
	const Property* p = mf.getProperty();
	if (!p->isAtMostOne()) {
		char buf[128];
		snprintf(buf, 128, "invalid_argument: slot %s can contain many values", p->getName());
		MAY_FAIL(dlog << DEBUGLEVEL_DEBUG << buf << std::endl);
		throw std::invalid_argument(std::string(buf));
	}
	if (!p->getTarget()->isDatatypeDefinition()) {
	  std::string s("invalid_argument when setting slot ");
	  s += p->getName();
	  s += ". Are you sure you gave me an element as the argument?";
	  throw std::invalid_argument(s);
	}

	Atom* _old_atom = mf.__getitem__(0);
	/*
	 * Note! old_atom can be null if multiplicity is not 1..1!
	 */
	ElementPrimitive* old_atom
		= dynamic_cast<ElementPrimitive*>(_old_atom);

	mf.helper_insert(old_atom, o);
}


void Element::__getSlotByNameInsertElement__(const char* name,
					     Element* o)
	throw (std::invalid_argument, std::exception) {
	Slot& mf = this->__getSlotByName__(name);
	const Property* p = mf.getProperty();
	if (!p->isAtMostOne()) {
		char buf[128];
		snprintf(buf, 128, "invalid_argument: slot %s can contain many values", p->getName());
		MAY_FAIL(dlog << DEBUGLEVEL_DEBUG << buf << std::endl);
		throw std::invalid_argument(std::string(buf));
	}
	if (p->getTarget()->isDatatypeDefinition()) {
		coral_assert(o == NULL);
		this->__getSlotByNameInsert__(name, Py_None);
		return;
	}
	mf.insert(o);
}

Atom* Element::__getSlotOneElement__(const char* fname) const throw () {
	if (!fname) return NULL;	
	Slot* mf = this->__hasSlotByName__(fname);
	Property* me = this->__type__()->getPropertyByName(fname);
	if (!me)
		dlog << DEBUGLEVEL_DEBUG << fname << " not found in "
		     << this->__str__() << std::endl;
	coral_assert(me);
	if (!me->isAtMostOne()) return NULL;
	if (me->getTarget()->isDatatypeDefinition()) {
		Slot& smf = (const_cast<coral::Element*>(this))->__getSlotByName__(fname);
		return smf.__getitem__(0);
	} else {
		if (!mf) return NULL;
	}
	/* Can't throw */
	return mf->__getitem__(0);
}


unicodestring Element::__str__() const {
	std::ostringstream s;
	s << "Element ";
	const ElementString* es = coral::util::Element__getName(this);
	if (es) {
		s << es->__str__().toUTF8();
	} else {
		s << "<unknown>";
	}
	s << " ("
	  << static_cast<const void*>(this)
	  << ", "
	  << __type__()->getName()
	  << ")";
	return unicodestring(s.str(), true);
}

void Element::__resetAttrs__() {
	temp_ref t(this);
	for (slots_type::iterator i = this->slots.begin();
	     i != this->slots.end();
	     ++i) {
		(*i).second->resetSlot();
	}
	__setProject__(NULL);
}

void Element::__detachUsFromEverybodyElse__(const std::set<Element*>&
					    all_elems_participating) {
	temp_ref tmp(this);
	for (slots_type::iterator i = this->slots.begin();
	     i != this->slots.end();
	     ++i) {
		Slot* s = (*i).second;
		Property* p = s->getProperty();

		// Assuming children are in all_elems_participating
		if (p->isComposition()) continue;

		if (p->getTarget()->isDatatypeDefinition())
			continue;
		/*
		 * BUG.
		 * Urk. bidi properties to the same element.
		 * I'm unsure what guarantees all the various
		 * STL constructions have when dropping and iterating.
		 * Note how Iterator is special in that it
		 * we can drop the _current_ element, but not
		 * anything else.
		 */
		Iterator o(s);
		ref_counted* t;
		while ((t = o.next())) {
			Element* target = static_cast<Element*>(t);
			if (all_elems_participating.find(target)
			    == all_elems_participating.end()) {
				s->drop(target);
			}
		}
	}
	__setProject__(NULL);
}


/*
 * Slot
 */

Slot::Slot(Element* e, Property* f) {
	coral_assert(f);
	this->mmproperty = f;
	/* NO ref() here! Slots do not count into ref usage.
	 * And besides, readonly-slots don't have an element... */
	this->element = e;
}

Slot::~Slot() throw () {
#if 0
	std::cout << "Deleting slot " << this << std::endl;
#endif
}

Iterator* Slot::__iter__() {
	return new Iterator(this);
}

Collection* Slot::getCollection() const throw () {
	return NULL;
}

bool Slot::TRANSACTION() throw () {
	if (Transaction::theTM
	    && Transaction::theTM->internalIsActive()
	    && ! Transaction::theTM->already_modified(element, getProperty())
		) {
		Transaction::ElementChangeSlotCommand* x;
		MUST_WORK(x = new Transaction::ElementChangeSlotCommand(this->element, this));
		Transaction::theTM->partialModifyCmd(x);
		return true;
	}
	return false;
}



std::size_t Slot::__len__() const throw () {
	return 0;
}

/* We default to just aborting if somebody calls us wrongly. */
Atom* Slot::__getitem__(int index) const throw (std::out_of_range) {
	coral_assert(this->element != NULL);
	try {
		dlog << DEBUGLEVEL_DEBUG << this->element->__str__() << std::endl;
		dlog << DEBUGLEVEL_DEBUG << this->getProperty()->getName()
		     << std::endl;
	} catch (...) {
		;
	}
	throw std::out_of_range("__getitem__ on this object not allowed");
}

void Slot::__setitem__(int index, Atom*)
	throw (std::out_of_range, std::exception) {
	coral_assert(this->element != NULL);
	try {
		dlog << DEBUGLEVEL_DEBUG << this->element->__str__() << std::endl;
		dlog << DEBUGLEVEL_DEBUG << this->getProperty()->getName() << std::endl;
	} catch (...) {
		;
	}
	throw std::out_of_range("__setitem__ on this object not allowed");
}


void Slot::insert(Atom* e, int index, int other_index,
		  bool unidirectional) throw (std::exception) {
	coral_assert(this->element != NULL);
	coral_assert(e);
	throw std::exception();
}

int Slot::drop(Atom* e, bool unidirectional) throw (std::exception) {
	coral_assert(this->element != NULL);
	coral_assert(e);
	throw std::exception();
}

Atom* Slot::remove_keepref(int index, bool unidirectional) {
	coral_assert(0);
	return NULL;
}

void Slot::insert_py(PyObject* e, int index, int other_index,
		  bool unidirectional) throw (std::exception) {
	helper_insert(NULL, e, index, other_index, unidirectional);
}

void Slot::helper_insert(ElementPrimitive* old_atom,
			 PyObject* o, int index, int other_index,
			 bool unidirectional) throw (std::exception) {
	coral_assert(o);
	const Property* p = getProperty();

	AbstractElementDefinition* md = p->getTarget();

	// Use something else instead of this..
	if (!md->isDatatypeDefinition()) {
		char buf[128];
		snprintf(buf, 128, "invalid_argument: slot %s cannot contain primitive values", p->getName());
		MAY_FAIL(dlog << DEBUGLEVEL_DEBUG << buf << std::endl);
		throw std::invalid_argument(std::string(buf));
	}

	Atom* _new_atom = md->newAtom();
	ElementPrimitive* new_atom
		= dynamic_cast<ElementPrimitive*>(_new_atom);
	coral::autounref<ref_counted> _t(new_atom);

	{
		Transaction::PauseTransaction pause_tr(true);

		if (md == CORE::coralPyObj) {
			new_atom->__setPyValue__(o);
		} else if (PyInt_Check(o)) {
			long l = PyInt_AsLong(o);
			if (md->isEnumerationDefinition()) {
				if (old_atom && old_atom->__getEValue__() == l)
					return;
				new_atom->__setEValue__(l);
			} else if (md->isSubclass(CORE::UnlimitedInteger)) {
				if (old_atom && old_atom->__getIValue__() == l)
					return;
				new_atom->__setIValue__(l);
			} else if (md->isSubclass(CORE::UnlimitedReal)) {
				if (old_atom && old_atom->__getDValue__() == static_cast<float>(l))
					return;
				new_atom->__setDValue__(l);
			} else {
				coral_assert(0);
			}
		} else if (PyFloat_Check(o)) {
			double d = PyFloat_AsDouble(o);
			if (md->isSubclass(CORE::UnlimitedReal)) {
				if (old_atom && old_atom->__getDValue__() == d)
					return;
				new_atom->__setDValue__(d);
			} else {
				coral_assert(0);
			}
		} else if (PyString_Check(o)) {
			const char* val = PyString_AsString(o);
			const unicodestring uval(val, false);
			if (md->isEnumerationDefinition()) {
				if (old_atom && old_atom->__getEValueByName__() == uval)
					return;
				new_atom->__setEValueByName__(val);
			} else if (md->isSubclass(CORE::String)) {
				if (old_atom && old_atom->__getString__() == uval)
					return;
				new_atom->__setString__(uval);
			} else {
				coral_assert(0);
			}
		} else if (PyUnicode_Check(o)) {
			const coral::unicodestring u(o);
			if (md->isEnumerationDefinition()) {
				new_atom->__setEValueByName__(u.toUTF8().c_str());
			} else if (md->isSubclass(CORE::String)) {
				if (old_atom && old_atom->__getString__() == u)
					return;
				new_atom->__setString__(u);
			} else {
				coral_assert(0);
			}
		} else {
			/*
			 * We have to unpause the processing, since the
			 * upcoming getSlotByNameInsert needs to create
			 * a command, in md->newAtom() at the beginning of this
			 * function.
			 */
			pause_tr.setPause(false);
			/*
			 * We come here if we assign a slot
			 * to another slot, e.g.
			 * mymodel.name = myclass.name
			 */
			PyObject* x = NULL;
			if (md->isSubclass(CORE::UnlimitedInteger)) {
				x = PyNumber_Long(o);
			} else if (md->isSubclass(CORE::UnlimitedReal)) {
				x = PyNumber_Float(o);
			} else if (md->isEnumerationDefinition()) {
				x = PyNumber_Long(o);
			} else if (md->isSubclass(CORE::String)) {
				x = PyObject_Unicode(o);
			} else {
				coral_assert(0);
			}
			if (x) {
				helper_insert(old_atom,
					      x,
					      index,
					      other_index,
					      unidirectional);
				return;
			} else {
				coral_assert(0);
			}
		}
	}
	/* The old one will be pushed away */
	insert(new_atom, index, other_index, unidirectional);
	
}



SlotManyElements::SlotManyElements(Element* e, Property* f,
				   Collection* m)
	: Slot(e, f), collection(m) {
	coral_assert(!f->isAtMostOne());
	//coral_assert(f->getTarget()->isDatatypeDefinition() == false);
	coral_assert(this->collection);
	/*
	 * No ref on this->collection, we have it already.
	 */
}

SlotManyElements::~SlotManyElements() throw () {
	this->collection->unref();
}

void SlotManyElements::swapSlotContents(Slot* newmf) throw () {
	coral_assert(newmf);
	SlotManyElements* s = dynamic_cast<SlotManyElements*>(newmf);
	coral_assert(s);
	swapvalues<Collection*>(this->collection, s->collection);
}

Collection* SlotManyElements::getCollection() const throw () {
	return this->collection;
}

std::size_t SlotManyElements::__len__() const throw () {
	return this->collection->__len__();
}

/* hack, this shouldn't be possible. It's a Set, not a sequence. */
Atom* SlotManyElements::__getitem__(int index)
	const throw (std::out_of_range) {
	coral_assert(this->element != NULL);
	ref_counted* _child = this->collection->__getitem__(index);
	Atom* child = dynamic_cast<Atom*>(_child);
	coral_assert(child);
	return child;
}

void SlotManyElements::__setitem__(int index, Atom* e)
	throw (std::out_of_range, std::exception) {
	coral_assert(e);
	coral_assert(this->element != NULL);

	ref_counted* _old = this->collection->__getitem__(index);
	Atom* old = dynamic_cast<Atom*>(_old);
	coral_assert(old);
	if (old == e) {
		/*
		 * We don't need to modify anything
		 */
		return;
	}

	/*
	 * Of correct type? If not, metamodel constraints
	 * are violated => throw
	 */
	if (!e->__type__()->isSubclass(this->getProperty()->getTarget())) {
		dlog << DEBUGLEVEL_DEBUG << "WRONG TYPE " << e->__type__()->getName()
		     << " should be " << this->getProperty()->getTarget()->getName() << std::endl;
		dlog << DEBUGLEVEL_DEBUG << "Property name is " << this->getProperty()->getName() << std::endl;
		throw std::exception();
	}

	temp_ref _t(this->element);

	Property* oppo = mmproperty->getOppositeProperty();
	bool did_transaction = false;
	if (oppo) {
		coral_assert(!old->__type__()->isDatatypeDefinition());
		Element* olde = static_cast<Element*>(old);
		Slot& old_f = olde->__getSlotByName__(oppo->getName());
			
		did_transaction = TRANSACTION();

#ifndef NDEBUG
		size_t s = old_f.size();
		coral_assert(s > 0);
#endif
		old_f.drop(this->element, true);
		coral_assert(old_f.size() == s - 1);
	} else {
		did_transaction = TRANSACTION();
	}
		
	if (!this->collection->__setitem__(index, e)) {
		if (did_transaction) {
			Transaction::theTM->__forget_previous_Cmd();
		}
		/* BUG... what to do here? */
		dlog << DEBUGLEVEL_DEBUG << "BUG/error encountered. You're setting something into a set, even"
		     << std::endl
		     << "though the target already has its one, unique connection back."
		     << std::endl
		     << "We haven't decided what this semantically means, so...."
		     << std::endl;
		coral_assert(0);
		return;
	}
	if (oppo) {
		//Transaction::ForgetPreviousCommand forget;
		//if (!did_transaction) forget.release();
		coral_assert(!e->__type__()->isDatatypeDefinition());
		Element* ee = static_cast<Element*>(e);
		Slot& e_f = ee->__getSlotByName__(oppo->getName());
		try {
			e_f.insert(this->element, -1, -1, true);
		} catch (...) {
			/*
			 * BUG we should fix this->collection
			 * to contain the old value :(
			 */
			//forget.release();
			coral_assert(0);
			throw;
		}
		//forget.release();
	}
}


void SlotManyElements::insert(Atom* e, int index, int other_index, bool unidirectional) throw (std::exception) {
	coral_assert(this->element != NULL);
	coral_assert(e);
	//coral_assert(e != this->element);
	/*
	 * Of correct type? If not, metamodel constraints
	 * are violated => throw
	 */
	if (!e->__type__()->isSubclass(this->getProperty()->getTarget())) {
		dlog << DEBUGLEVEL_DEBUG
		     << "WRONG TYPE " << e->__type__()->getName()
		     << " should be "
		     << this->getProperty()->getTarget()->getName()
		     << std::endl
		     << "Property name is "
		     << this->getProperty()->getName()
		     << std::endl;
		throw std::exception();
	}

	bool did_transaction = TRANSACTION();

	Transaction::ForgetPreviousCommand forget;
	if (!did_transaction) forget.release();

	/* ok to throw */
	bool did_insert = this->collection->insert(e, index);
	/* Already there, we can quit. */
	if (did_insert == false) {
		return;
	}

	Property* oppo = mmproperty->getOppositeProperty();
	if (unidirectional
	    || !oppo) {
		forget.release();
		return;
	}
	
	coral_assert(oppo);
	coral_assert(!e->__type__()->isDatatypeDefinition());
	Element* ee = static_cast<Element*>(e);
	Slot& e_f = ee->__getSlotByName__(oppo->getName());

	/* ok to throw */
	try {
		e_f.insert(this->element, other_index, -1, true);
	} catch (...) {
		MAY_FAIL(dlog
			 << DEBUGLEVEL_WARNING
			 << "WARNING! SlotManyElements.insert failed! " << e << std::endl);
		if (this->collection->isOrdered()) {
			this->collection->drop_index(index);
		}  else {
			this->collection->drop(e);
		}
		throw;
	}
	forget.release();

}

int SlotManyElements::drop(Atom* e, bool unidirectional) throw () {
	coral_assert(this->element != NULL);
	coral_assert(e);
	/* so we don't lose it. */
	temp_ref t(e);

	TRANSACTION();

	int index = this->collection->drop(e);
	if (unidirectional)
		return index;

	Property* oppo = mmproperty->getOppositeProperty();
	if (!oppo)
		return index;

	coral_assert(!e->__type__()->isDatatypeDefinition());
	Element* ee = static_cast<Element*>(e);
	Slot& e_f = ee->__getSlotByName__(oppo->getName());
	e_f.drop(this->element, true);

	return index;
}

Atom* SlotManyElements::__remove_continued(Atom* other,
					   bool unidirectional)
	throw () {
	coral_assert(other);
	if (!unidirectional) {
		/*
		 * Because we hope that the user has a ref on
		 * "this->element". On the other hand, it might be
		 * that the user does not have to have a ref()
		 * on this->element... We're not touching anything
		 * in this after the following method call.
		 */
		Property* oppo = mmproperty->getOppositeProperty();
		if (oppo) {
			coral_assert(!other->__type__()->isDatatypeDefinition());
			Element* oother = static_cast<Element*>(other);
			Slot& other_f = oother->__getSlotByName__(oppo->getName());
			other_f.drop(this->element, true);
		}
	}
	/* NOTE! No other->unref() here, because caller might use it!
	   See also drop().*/
	return other;
}

Atom* SlotManyElements::remove_keepref(int index, bool unidirectional)
	throw () {
	coral_assert(this->element != NULL);
	TRANSACTION();
	ref_counted* _other = this->collection->remove_keepref(index);
	Atom* other = dynamic_cast<Atom*>(_other);
	coral_assert(other);
	return this->__remove_continued(other, unidirectional);
}

std::auto_ptr<Slot> SlotManyElements::clone() {
	coral_assert(this->element != NULL);
	/* NOT under transaction control. */
	coral::autounref<Collection> c(this->collection->clone());
	std::auto_ptr<Slot>
		r(new SlotManyElements(this->element,
				       this->mmproperty,
				       c.get()));
	c.release();
	return r;
}

void SlotManyElements::resetSlot() {
	coral_assert(this->element != NULL);
	/*
	 * NOTE! It's especially not correct to call
	 * collection->reset() since the refcount to this would not
	 * be decremented, and the elements in the collection must
	 * also be updated.
	 *
	 * BUG
	 * Also, note that this code should work _good_ on several
	 * kinds of Collections: notably, sets and sequences.
	 * It ought to not waste transactions...
	 *
	 * Also, hash_sets have an annoying property:
	 * If you always operate on .begin(), you'll
	 * use more and more time to _reach_ begin().
	 * Thus, you must go one at a time.
	 */

	/* No transaction necessary? */
	if (this->collection->isEmpty()) return;
		
	TRANSACTION();

	Iterator i(this->collection);
	ref_counted* _child;
	while ((_child = i.next()) != NULL) {
		Atom* child = dynamic_cast<Atom*>(_child);
		coral_assert(child);
		this->drop(child);
	}
	coral_assert(this->collection->isEmpty());
}

unicodestring SlotManyElements::__str__() const {
	return this->collection->__str__();
}

/*
 * SlotOneElement
 */

SlotOneElement::SlotOneElement(Element* e, Property* f) : Slot(e, f) {
	coral_assert(f->isAtMostOne());
	this->value = NULL;
	if (f->getMultLower() == 1 && f->getMultUpper() == 1) {
		// BUG ed is not defined for anon props yet
		// must fix Property.__init__ constructor to take
		// owner argument also.

		/*
		 * Primitive types of multiplicity 1..1 exist by default...
		 */
		AbstractElementDefinition* ed = f->getTarget();
		/*
		 * Set this->value to the Property.defaultValue
		 * BUG Currently we only do this for SlotOneElements, not
		 * BUG for SlotManyElements. Should we?
		 */
		if (ed && ed->isDatatypeDefinition()) {
			this->value = f->getTarget()->newAtom();
			const std::string& dv = f->getDefaultValue();
			if (dv.empty()) return;
			if (ElementEnumeration* ee
			    = dynamic_cast<ElementEnumeration*>(this->value)) {
				ee->__setEValueByName__(dv.c_str());
			} else if (ElementInteger* ei
				   = dynamic_cast<ElementInteger*>(this->value)) {
				long int i = strtol(dv.c_str(), NULL, 10);
				if ((i <= INT_MIN)
				    || (i >= INT_MAX)) {
					;
				} else {
					ei->__setIValue__(i);
				}
			} else if (ElementDouble* edd
				   = dynamic_cast<ElementDouble*>(this->value)) {
				coral_double d;
				int err = sscanf(dv.c_str(),
						 "%lf", &d);
				if (err != 1) {
					;
				} else {
					edd->__setDValue__(d);
				}
			} else if (ElementString* es
				   = dynamic_cast<ElementString*>(this->value)) {
			  es->__setString__(coral::unicodestring(dv, true));
			} else {
				coral_assert(0);
			}
		}
	}
}

SlotOneElement::~SlotOneElement() throw () {
	if (this->value) {
		this->value->unref();
	}
}

void SlotOneElement::swapSlotContents(Slot* newmf) throw () {
	coral_assert(newmf);
	SlotOneElement* s = dynamic_cast<SlotOneElement*>(newmf);
	coral_assert(s);
	swapvalues<Atom*>(this->value, s->value);
	invalidatePythonSingleSlot(this->element, getProperty()->getName());
}


std::auto_ptr<Slot> SlotOneElement::clone() {
	coral_assert(this->element != NULL);
	/* NOT under transaction control. */
	SlotOneElement* r
		= new SlotOneElement(this->element, this->mmproperty);
	r->value = this->value;
	if (r->value) r->value->ref();
	return std::auto_ptr<Slot>(r);
}

void SlotOneElement::resetSlot() {
	coral_assert(this->element != NULL);
	invalidatePythonSingleSlot(this->element, getProperty()->getName());
	if (this->value) {
		ElementPrimitive* ep 
			= dynamic_cast<ElementPrimitive*>(this->value);
		/*
		 * If we have a primitive element
		 * with a lower and upper multiplicity of one,
		 * we just reset its value, not remove it
		 * from the Slot.
		 */
		if (ep) {
			Property* p = getProperty();
			if ((p->getMultLower() == 1) && p->getMultUpper() == 1) {
				ep->reset();
				return;
			}
				
		}
		this->drop_index(0);
	}
}

void SlotOneElement::insert(Atom* e, int index, int other_index,
			    bool unidirectional)
	throw (std::exception) {
	coral_assert(this->element != NULL);
	coral_assert(index == -1);
	/*
	 * SMD Properties that point to self seem to
	 * need the below commented out...
	 */
	//coral_assert(e != this->element);

	/*
	 * Same element? No change. This is important, due to
	 * Sets at the other end.
	 */
	if (e == this->value) return;

	/*
	 * Of correct type? If not, metamodel constraints
	 * are violated => throw
	 */
	if (e && !e->__type__()->isSubclass(this->getProperty()->getTarget())) {
	  std::ostringstream s;
	  s << "WRONG TYPE " << e->__type__()->getName()
	    << " should be "
	    << this->getProperty()->getTarget()->getName()
	    << " (Property name is "
	    << this->getProperty()->getName()
	    << ")";
	  throw std::runtime_error(s.str());
	}


	const Property* oppo
		= mmproperty->getOppositeProperty();

	/*
	 * Additional check if we try to share
	 * elements between two parents.
	 * Steal it.
	 */
	if (oppo && oppo->isComposition()) {
		Slot* cur_parent_slot = element->__parentSlot__();
		if (cur_parent_slot) {
			const Element* curparent
				= dynamic_cast<Element*>((*cur_parent_slot)[0]);
			coral_assert(curparent);
			const Element* other
				= dynamic_cast<const Element*>(e);
			if (curparent
			    && other
			    && curparent != other
			    && curparent != this->value) {
				cur_parent_slot->drop_index(0);
#if 0
				dlog << DEBUGLEVEL_CRITICAL
				     << "CRITICAL Achtung! "
				     << "You're sharing an element between two parents!"
				     << std::endl
				     << "This is *probably* a mistake, so we'll throw an exception just to be sure"
				     << std::endl
				     << "Slot: " << mmproperty->getOwner()->getName() << ":" << mmproperty->getName()
				     << std::endl
				     << "Parent 1: "
				     << curparent->__XMIid__()
				     << std::endl
				     << "Parent 2: "
				     << other->__XMIid__()
				     << std::endl
				     << "Workarounds: First remove the old parent, then add the new."
				     << std::endl;
				throw std::exception();
#endif
			}
		}
	}

	TRANSACTION();

	invalidatePythonSingleSlot(this->element, getProperty()->getName());

	/*
	 * Make sure our current element doesn't vanish from
	 * under us when we drop().
	 */
	temp_ref t(this->element);

	/*
	 * Since we can only hold one element, the old element
	 * must be removed.
	 */
	if (this->value) {
		if (oppo) {
			/* remove value2this link */
			coral_assert(!this->value->__type__()->isDatatypeDefinition());
			Element* v = static_cast<Element*>(this->value);
			Slot& old_f = v->__getSlotByName__(oppo->getName());
			old_f.drop(this->element, true);
		}
		if (oppo != getProperty()) {
			this->value->unref();
		}
	}
	this->value = e;
	if (e)
		e->ref();
		
	if (unidirectional
	    || !e
	    || !oppo)
		return;

	try {
		coral_assert(!e->__type__()->isDatatypeDefinition());
		Element* ee = static_cast<Element*>(e);
		Slot& e_f = ee->__getSlotByName__(oppo->getName());
		e_f.insert(this->element, other_index, -1, true);
	} catch (...) {
		/* Little sane left to do... */
		MAY_FAIL(dlog
			 << DEBUGLEVEL_WARNING
			 << "WARNING! SlotOneElement::insert failed! " << e << std::endl);
		this->value->unref();
		this->value = NULL;
	}
}

int SlotOneElement::drop(Atom* e, bool unidirectional) throw () {
	coral_assert(this->element != NULL);
	coral_assert(e);
	if (!this->value) return 0; /* I wonder if this is ok... */
	coral_assert(e == this->value);
	TRANSACTION();
	this->remove_keepref(0, unidirectional)->unref();
	return 0;
}

std::size_t SlotOneElement::__len__() const throw () {
	return this->value == NULL ? 0 : 1;
}

Atom* SlotOneElement::__getitem__(int index)
	const throw (std::out_of_range) {
	if (index != 0) {
		throw std::out_of_range("");
	}
	return this->value;
}

Atom* SlotOneElement::remove_keepref(int index, bool unidirectional) {
	coral_assert(this->element != NULL);
	coral_assert(index == 0);
	coral_assert(this->value);
	TRANSACTION();
	invalidatePythonSingleSlot(this->element, getProperty()->getName());
	temp_ref t(this->element);
	if (!unidirectional) {
		Property* oppo = mmproperty->getOppositeProperty();
		if (oppo && oppo != mmproperty) {
			coral_assert(!this->value->__type__()->isDatatypeDefinition());
			Element* v = static_cast<Element*>(this->value);
			v->__getSlotByName__(oppo->getName()).drop(this->element, true);
		}
	}
	Atom* e = this->value;
	this->value = NULL;
	return e;
}

unicodestring SlotOneElement::__str__() const {
	char buf[1024];
	if (this->value)
		snprintf(buf, 1024, "%p",
			 static_cast<const void*>(this->value));
	else
		snprintf(buf, 1024, "<None>");
	return unicodestring(buf, true);
}


	
/*
 * Primitive elements
 */

ElementPrimitive::ElementPrimitive(DatatypeDefinition* e) : Atom(e) {
}
ElementPrimitive::~ElementPrimitive() throw () {
}

void ElementPrimitive::TRANSACTION() throw () {
	if ((Transaction::theTM) && (Transaction::theTM->internalIsActive())) {
		Transaction::ChangeElementPrimitiveCommand* x;
		MUST_WORK(x = new Transaction::ChangeElementPrimitiveCommand(this));
		Transaction::theTM->partialModifyCmd(x);
	}
}

bool ElementPrimitive::operator==(const PyObject* p) const throw () {
	// This only works for coral Booleans
	if (this->__type__() != CORE::Boolean) {
		return false;
	}

	// And only for Python's True/False objects.
	std::size_t b;
	if (p == Py_True) {
		b = 1;
	} else if (p == Py_False) {
		b = 0;
	} else {
		return false;
	}

	std::size_t l(__len__());
	return l == b;
}


int Atom::__getEValue__() const throw () {
	coral_assert(0);
	return -999;
}

unicodestring Atom::__getEValueByName__() {
	static unicodestring a("", true);
	coral_assert(0);
	return a;
}

void Atom::__setEValue__(int newValue) {
	coral_assert(0);
}

PyObject* Atom::__getPyValue__() const throw () {
	coral_assert(0);
	return NULL;
}

void Atom::__setPyValue__(PyObject*) throw () {
	coral_assert(0);
}

void Atom::__setEValueByName__(const char* newValue) {
	coral_assert(0);
}

int Atom::__getIValue__() const throw () {
	coral_assert(0);
	return -999;
}

int Atom::__int__() const throw () {
	coral_assert(0);
	return -999;
}

double Atom::__float__() const throw () {
	coral_assert(0);
	return -999;
}

void Atom::__setIValue__(int newValue) {
	coral_assert(0);
}

double Atom::__getDValue__() const throw () {
	coral_assert(0);
	return -999.999f;
}

void Atom::__setDValue__(double newValue) throw () {
	coral_assert(0);
}

void Atom::__setString__(const unicodestring& newValue) {
	coral_assert(0);
}

const unicodestring& Atom::__getString__() const throw () {
	static unicodestring a("<ERROR: coral::Atom::__getString__ called>", true);
	coral_assert(0);
	return a;
}

/*
 * Python object wrapper.
 */

ElementPyObject::ElementPyObject(DatatypeDefinition* e) : ElementPrimitive(e) {
	coral_assert(e->isSubclass(CORE::coralPyObj));
	this->obj = Py_None;
	Py_INCREF(Py_None);
}

ElementPyObject::~ElementPyObject() throw () {
	Py_DECREF(this->obj);
}

void ElementPyObject::reset() {
	__setPyValue__(Py_None);
}

PyObject* ElementPyObject::__getPyValue__() const throw () {
	Py_INCREF(this->obj);
	return this->obj;
}

void ElementPyObject::__setPyValue__(PyObject* newObj) throw () {
	coral_assert(newObj);
	if (this->obj != newObj) {
		TRANSACTION();
		Py_INCREF(newObj);
		Py_DECREF(this->obj);
		this->obj = newObj;
	}
}

std::size_t ElementPyObject::__len__() const throw () {
	return this->obj == Py_None ? 0 : 1;
}

ElementPyObject* ElementPyObject::clone() const {
	/* NOT under transaction control. */
	ElementPyObject* r = NULL;
	try {
		r = new ElementPyObject(this->__type__());
	} catch (...) {
		return NULL;
	}
	r->obj = this->obj;
	Py_INCREF(r->obj);
	return r;
}

void ElementPyObject::swapWithAtom(ElementPrimitive* _a) throw () {
	ElementPyObject* a = dynamic_cast<ElementPyObject*>(_a);
	coral_assert(a);
	PyObject* old = this->obj;
	this->obj = a->obj;
	a->obj = old;
}

unicodestring ElementPyObject::__str__() const {
	char buf[1024];
	snprintf(buf, 1024, "%p", reinterpret_cast<void*>(this->obj));
	return unicodestring(buf, true);
}

bool ElementPyObject::operator==(const ElementPrimitive& other)
	const throw () {
	if (other.__type__() != this->__type__()) return false;
	const ElementPyObject& o =
		static_cast<const ElementPyObject&>(other);
	return this->obj == o.obj;
}

bool ElementPyObject::operator<(const ElementPrimitive& other)
	const throw () {
	if (other.__type__() != this->__type__()) return false;
	const ElementPyObject& o =
		static_cast<const ElementPyObject&>(other);
	return this->obj < o.obj;
}

bool ElementPyObject::operator<=(const ElementPrimitive& other)
	const throw () {
	if (other.__type__() != this->__type__()) return false;
	const ElementPyObject& o =
		static_cast<const ElementPyObject&>(other);
	return this->obj <= o.obj;
}


/*
 * ElementEnumeration
 */

ElementEnumeration::ElementEnumeration(EnumerationDefinition* e)
	: ElementPrimitive(e) {
	this->enumValue = 0;
}

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

void ElementEnumeration::reset() {
	__setEValue__(0);
}


unicodestring ElementEnumeration::__getEValueByName__() {
	return unicodestring(this->__type__()->getDescription(this->enumValue), true);
}

bool ElementEnumeration::operator==(const ElementPrimitive& other)
	const throw () {
	if (other.__type__() != this->__type__()) return false;
	const ElementEnumeration& o =
		static_cast<const ElementEnumeration&>(other);
	return this->enumValue == o.enumValue;
}

bool ElementEnumeration::operator<(const ElementPrimitive& other)
	const throw () {
	if (other.__type__() != this->__type__()) return false;
	const ElementEnumeration& o =
		static_cast<const ElementEnumeration&>(other);
	return this->enumValue < o.enumValue;
}

bool ElementEnumeration::operator<=(const ElementPrimitive& other)
	const throw () {
	if (other.__type__() != this->__type__()) return false;
	const ElementEnumeration& o =
		static_cast<const ElementEnumeration&>(other);
	return this->enumValue <= o.enumValue;
}

ElementEnumeration* ElementEnumeration::clone() const {
	/* NOT under transaction control. */
	ElementEnumeration* r = NULL;
	try {
		r = new ElementEnumeration(this->__type__());
	} catch (...) {
		return NULL;
	}
	r->enumValue = this->enumValue;
	return r;
}

void ElementEnumeration::swapWithAtom(ElementPrimitive* _a) throw () {
	ElementEnumeration* a = dynamic_cast<ElementEnumeration*>(_a);
	coral_assert(a);
	int old = this->enumValue;
	this->enumValue = a->enumValue;
	a->enumValue = old;
}



void ElementEnumeration::__setEValueByName__(const char* newValue) {
	int t = this->__type__()->getIndexByDescription(newValue);
	if (t == -999) {
		dlog << DEBUGLEVEL_ERROR
		     << "Wrong enumeration value " << newValue << std::endl
		     << "Possibilities are:" << std::endl;
		for (int i = 0; i < __type__()->size(); ++i) {
			dlog << "- "
			     << __type__()->getDescription(i) << std::endl;
		}
		dlog << "(done)" << std::endl;
	}
	coral_assert(t != -999);
	if (this->enumValue != t) {
		TRANSACTION();
		this->enumValue = t;
	}
}

void ElementEnumeration::__setEValue__(int newValue) {
	coral_assert((newValue >= 0) && (newValue < this->__type__()->size()));
	coral_assert(newValue != -999);
	if (this->enumValue != newValue) {
		TRANSACTION();
		this->enumValue = newValue;
	}
}

unicodestring ElementEnumeration::__str__() const {
	return unicodestring(this->__type__()->getDescription(this->enumValue), true);
}

int ElementEnumeration::__int__() const throw () {
	return this->enumValue;
}

std::size_t ElementEnumeration::__len__() const throw () {
	if (this->__type__() == CORE::Boolean) {
		return this->enumValue == 1;
	}
	return 1;
}

/*
 * ElementInteger
 */


ElementInteger::ElementInteger(DatatypeDefinition* e) : ElementPrimitive(e) {
	coral_assert(e->isSubclass(CORE::UnlimitedInteger));
	this->ivalue = 0;
}

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

void ElementInteger::reset() {
	__setIValue__(0);
}

int ElementInteger::__int__() const throw () {
	return this->ivalue;
}

double ElementInteger::__double__() const throw () {
	return double(this->ivalue);
}

void ElementInteger::__setIValue__(int newValue) throw () {
	if (this->ivalue != newValue) {
		TRANSACTION();
		this->ivalue = newValue;
	}
}

std::size_t ElementInteger::__len__() const throw () {
	return this->ivalue == 0 ? 0 : 1;
}

ElementInteger* ElementInteger::clone() const {
	/* NOT under transaction control. */
	ElementInteger* r = NULL;
	try {
		r = new ElementInteger(this->__type__());
	} catch (...) {
		return NULL;
	}
	r->ivalue = this->ivalue;
	return r;
}

void ElementInteger::swapWithAtom(ElementPrimitive* _a) throw () {
	ElementInteger* a = dynamic_cast<ElementInteger*>(_a);
	coral_assert(a);
	int old = this->ivalue;
	this->ivalue = a->ivalue;
	a->ivalue = old;
}

unicodestring ElementInteger::__str__() const {
     // Note that we can't use snprintf etc
     // because of locale
     std::ostringstream s;
     s.imbue(coral::clocale);
     s.precision(256); // arbitrary huge precision
     s << ivalue;
     return unicodestring(s.str(), true);
}

bool ElementInteger::operator==(const ElementPrimitive& other)
	const throw () {
	const ElementInteger* o =
		dynamic_cast<const ElementInteger*>(&other);
	if (!o) return false;
	return this->ivalue == o->ivalue;
}

bool ElementInteger::operator<(const ElementPrimitive& other)
	const throw () {
	if (other.__type__() != this->__type__()) return false;
	const ElementInteger& o =
		static_cast<const ElementInteger&>(other);
	return this->ivalue < o.ivalue;
}

bool ElementInteger::operator<=(const ElementPrimitive& other)
	const throw () {
	if (other.__type__() != this->__type__()) return false;
	const ElementInteger& o =
		static_cast<const ElementInteger&>(other);
	return this->ivalue <= o.ivalue;
}


/*
 * ElementDouble
 */

ElementDouble::ElementDouble(DatatypeDefinition* e) : ElementPrimitive(e) {
	coral_assert(e->isSubclass(CORE::UnlimitedReal));
	this->dvalue = 0;
}

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

void ElementDouble::reset() {
	__setDValue__(0.0f);
}

void ElementDouble::__setDValue__(double newValue) throw () {
	if (this->dvalue != newValue) {
		TRANSACTION();
		this->dvalue = newValue;
	}
}

std::size_t ElementDouble::__len__() const throw () {
	return this->dvalue == 0.0f ? 0 : 1;
}

ElementDouble* ElementDouble::clone() const {
	/* NOT under transaction control. */
	ElementDouble* r = NULL;
	try {
		r = new ElementDouble(this->__type__());
	} catch (...) {
		return NULL;
	}
	r->dvalue = this->dvalue;
	return r;
}

void ElementDouble::swapWithAtom(ElementPrimitive* _a) throw () {
	ElementDouble* a = dynamic_cast<ElementDouble*>(_a);
	coral_assert(a);
	double old = this->dvalue;
	this->dvalue = a->dvalue;
	a->dvalue = old;
}


unicodestring ElementDouble::__str__() const {
     // Note that we can't use snprintf etc
     // because of locale
     std::ostringstream s;
     s.imbue(coral::clocale);
     s.precision(256); // arbitrary huge precision
     s << dvalue;
     return unicodestring(s.str(), true);
}

double ElementDouble::__float__() const throw () {
	return this->dvalue;
}

int ElementDouble::__int__() const throw () {
	return int(this->dvalue);
}

bool ElementDouble::operator==(const ElementPrimitive& other)
	const throw () {
	if (other.__type__() != this->__type__()) return false;
	const ElementDouble& o =
		static_cast<const ElementDouble&>(other);
	return this->dvalue == o.dvalue;
}

bool ElementDouble::operator<(const ElementPrimitive& other)
	const throw () {
	if (other.__type__() != this->__type__()) return false;
	const ElementDouble& o =
		static_cast<const ElementDouble&>(other);
	return this->dvalue < o.dvalue;
}

bool ElementDouble::operator<=(const ElementPrimitive& other)
	const throw () {
	if (other.__type__() != this->__type__()) return false;
	const ElementDouble& o =
		static_cast<const ElementDouble&>(other);
	return this->dvalue <= o.dvalue;
}


/*
 * ElementString
 */

ElementString::ElementString(DatatypeDefinition* e)
	: ElementPrimitive(e), svalue("", true) {
	coral_assert((e == CORE::String)
		     || (e == CORE::Name));
	;
}

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

void ElementString::reset() {
	__setString__(unicodestring("", true));
}

void ElementString::__setString__(const unicodestring& newValue) {
	if (newValue != this->svalue) {
		TRANSACTION();
		this->svalue = newValue;
	}
}

const unicodestring& ElementString::__getString__() const throw () {
	return this->svalue;
}

std::size_t ElementString::__len__() const throw () {
	return this->svalue.size();
}

ElementString* ElementString::clone() const {
	/* NOT under transaction control. */
	ElementString* r = NULL;
	try {
		r = new ElementString(this->__type__());
		r->svalue = this->svalue;
	} catch (...) {
		delete r;
		r = NULL;
	}
	return r;
}


void ElementString::swapWithAtom(ElementPrimitive* _a) throw () {
	ElementString* a = dynamic_cast<ElementString*>(_a);
	coral_assert(a);
	this->svalue.swap(a->svalue);
}

unicodestring ElementString::__str__() const {
	return this->svalue;
}

bool ElementString::operator==(const ElementPrimitive& other)
	const throw () {
	if (other.__type__() != this->__type__()) return false;
	const ElementString& o =
		static_cast<const ElementString&>(other);
	return this->svalue == o.svalue;
}

bool ElementString::operator<=(const ElementPrimitive& other)
	const throw () {
	if (other.__type__() != this->__type__()) return false;
	const ElementString& o =
		static_cast<const ElementString&>(other);
	return this->svalue <= o.svalue;
}

bool ElementString::operator<(const ElementPrimitive& other)
	const throw () {
	if (other.__type__() != this->__type__()) return false;
	const ElementString& o =
		static_cast<const ElementString&>(other);
	return this->svalue < o.svalue;
}

NAMESPACE_END();




