/*************************************************************************/ /* */ /* Centre for Speech Technology Research */ /* University of Edinburgh, UK */ /* Copyright (c) 1998 */ /* All Rights Reserved. */ /* */ /* Permission is hereby granted, free of charge, to use and distribute */ /* this software and its documentation without restriction, including */ /* without limitation the rights to use, copy, modify, merge, publish, */ /* distribute, sublicense, and/or sell copies of this work, and to */ /* permit persons to whom this work is furnished to do so, subject to */ /* the following conditions: */ /* 1. The code must retain the above copyright notice, this list of */ /* conditions and the following disclaimer. */ /* 2. Any modifications must be clearly marked as such. */ /* 3. Original authors' names are not deleted. */ /* 4. The authors' names are not used to endorse or promote products */ /* derived from this software without specific prior written */ /* permission. */ /* */ /* THE UNIVERSITY OF EDINBURGH AND THE CONTRIBUTORS TO THIS WORK */ /* DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING */ /* ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT */ /* SHALL THE UNIVERSITY OF EDINBURGH NOR THE CONTRIBUTORS BE LIABLE */ /* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES */ /* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN */ /* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, */ /* ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF */ /* THIS SOFTWARE. */ /* */ /*************************************************************************/ /* Author : Alan W Black */ /* Date : May 1998 */ /*-----------------------------------------------------------------------*/ /* Linguistic items (e.g. words, phones etc) held as part of Relations */ /* */ /* These objects may be held in relations within an utterance. They */ /* fact contain two sections, a structural part and a contents part */ /* (EST_Item_Content) though the user will usually only see the whole */ /* object. The content part may be shared between linguistic items in */ /* other relations, e.g. the word item may appear both in the word */ /* relation and the syntax relation. */ /* */ /* Each linguistic item is in a particular relation but it is easy */ /* to link to that item in another relation. Traversal of the relation */ /* for an item in it is trivial. */ /* */ /*=======================================================================*/ #include #include #include #include #include "ling_class/EST_Item.h" #include "ling_class/EST_Relation.h" #include "ling_class/EST_Utterance.h" #include "EST_TKVL.h" #include "EST_UList.h" #include "EST_string_aux.h" #include "ling_class_init.h" /* Class initialisation. This is where you should register * feature functions and so on. */ void EST_Item::class_init(void) { #ifdef EST_DEBUGGING cerr << "Calling EST_Item init\n"; #endif ling_class_init::use(); EST_register_feature_functions(standard); #ifdef EST_DEBUGGING cerr << "Finished EST_Item init\n"; #endif } EST_Item::EST_Item() { p_relation = 0; p_contents = 0; n=p=u=d=0; set_contents(0); } void EST_Item::copy(const EST_Item &s) { // You can't really do this in general as a node is doubly // linked to its neighbours and item. Copying all the fields would // mean it was no longer true (unless you copied everything). // So all you get for this is a *copy* of the contents (not a reference // to) p_relation = 0; p_contents = 0; n=p=u=d=0; set_contents(0); // get an empty contents structure *p_contents = *s.p_contents; } EST_Item::EST_Item(const EST_Item &i) { copy(i); } EST_Item::~EST_Item() { // Delete this item and its daughters (and the contents with no // other links) // Assumes a tree structure EST_Item *ds,*nds; // Tidy up pointers to this if (n != 0) { n->p = p; n->u = u; // when deleting first daughter. } if (p != 0) p->n = n; if (u != 0) u->d = n; if (p_relation) { if (p_relation->p_head == this) p_relation->p_head = n; if (p_relation->p_tail == this) p_relation->p_tail = p; } // A little cleverer with the daughters for (ds=d; ds != 0; ds=nds) { nds=ds->n; delete ds; } unref_contents(); } EST_Item::EST_Item(EST_Relation *rel) { p_relation = rel; p_contents = 0; n=p=u=d=0; } // all internal ids are found by getting the next id number from // the utterance and prefixing a "_" to show that this is internally // generated. static void assign_id(EST_Item *s) { if (s->f_present("id")) return; EST_Utterance *u = get_utt(s); if (u != 0) s->set("id", "_" + itoString(u->next_id())); } EST_Item::EST_Item(EST_Relation *rel, EST_Item *li) { p_relation = rel; p_contents = 0; n=p=u=d=0; set_contents(li->contents()); assign_id(this); } void EST_Item::evaluate_features() { evaluate(this,p_contents->f); } void EST_Item::unref_contents() { // Unref the related contents to this item, delete if no-one else is // referencing it if (p_contents != 0) { if (p_contents->unref_relation(relation_name())) delete p_contents; p_contents = 0; } } void EST_Item::unref_all() { // Unreference this item from all its relations, deleting its contents p_contents->unref_and_delete(); } const EST_String &EST_Item::relation_name() const { return ((this == 0) || (p_relation == 0)) ? EST_String::Empty : p_relation->name(); } void EST_Item::set_contents(EST_Item_Content *new_contents) { // This function is for internal use only, general use of this // is likely to be unsafe. EST_Item_Content *c; if (new_contents == 0) c = new EST_Item_Content; else c = new_contents; if (p_contents != c) { unref_contents(); p_contents = c; EST_Item *nn_item = p_contents->Relation(relation_name()); if (nn_item) // this is already linked to this relation { // can't recurse on set_contents nn_item->p_contents = new EST_Item_Content; nn_item->p_contents->relations.add_item(relation_name(), est_val(nn_item)); } p_contents->relations.add_item(relation_name(),est_val(this)); } } int EST_Item::length() const { int i=0; EST_Item *nn = (EST_Item *)(void *)this; for (; nn; nn=nn->n,i++); return i; } EST_Item *EST_Item::insert_after(EST_Item *si) { // Create a new item and add it after t, and return it. // Include the cross link from this new item's contents to si, and // from si's relations fields back to the new node EST_Item *new_node = new EST_Item(p_relation,si); new_node->p = this; new_node->n = this->n; if (new_node->n != 0) new_node->n->p = new_node; this->n = new_node; if (p_relation && (p_relation->p_tail == this)) p_relation->p_tail = new_node; return new_node; } EST_Item *EST_Item::insert_before(EST_Item *si) { // Create a new node and add it before this, and return it. EST_Item *new_node = new EST_Item(p_relation,si); new_node->n = this; new_node->p = this->p; if (new_node->p != 0) new_node->p->n = new_node; this->p = new_node; // This makes an assumption that we represent trees with only // the first daughter pointing to the parent if (this->u) { new_node->u = this->u; new_node->u->d = new_node; this->u = 0; } if (p_relation && (p_relation->p_head == this)) p_relation->p_head = new_node; return new_node; } EST_Item *EST_Item::insert_below(EST_Item *si) { // Create a new node and add it below this, and return it. EST_Item *new_node = new EST_Item(p_relation,si); new_node->u = this; new_node->d = this->d; if (new_node->d != 0) new_node->d->u = new_node; this->d = new_node; return new_node; } EST_Item *EST_Item::insert_above(EST_Item *si) { // Create a new node and add it above this, and return it. EST_Item *new_node = new EST_Item(p_relation,si); new_node->d = this; new_node->u = this->u; if (new_node->u != 0) new_node->u->d = new_node; this->u = new_node; if (p_relation && (p_relation->p_head == this)) p_relation->p_head = new_node; if (p_relation && (p_relation->p_tail == this)) p_relation->p_tail = new_node; return new_node; } EST_Item *EST_Item::insert_parent(EST_Item *si) { // Insert new parent here, by added a new below node and moving // the contents down to it. if (this == 0) return 0; insert_below(0); down()->set_contents(grab_contents()); if (si != 0) set_contents(si->grab_contents()); else set_contents(0); return this; } EST_Item *EST_Item::last() const { // To get round the const access to this EST_Item *node = (EST_Item *)(void *)this; if (this == 0) return 0; for (; node->n != 0; node=node->n); return node; } EST_Item *EST_Item::first() const { // To get round the const access to this EST_Item *node = (EST_Item *)(void *)this; if (this == 0) return 0; for (; node->p != 0; node=node->p); return node; } EST_Item *EST_Item::top() const { EST_Item *node = (EST_Item *)(void *)this; if (this == 0) return 0; for (; parent(node) != 0; node=parent(node)); return node; } EST_Item *EST_Item::next_leaf() const { if (this == 0) return 0; else if (next() != 0) return next()->first_leaf(); else return parent(this)->next_leaf(); } EST_Item *EST_Item::next_item() const { // For traversal through a relation, in pre-order (root then daughters) if (this == 0) return 0; else if (down() != 0) return down(); else if (next() != 0) return next(); else { // at the right most leaf so go up until you find a parent with a next for (EST_Item *pp = parent(this); pp != 0; pp = parent(pp)) if (pp->next()) return pp->next(); return 0; } } EST_Item *EST_Item::first_leaf() const { // Leafs are defined as those nodes with no daughters if (this == 0) return 0; if (down() == 0) return (EST_Item *)(void *)this; else return down()->first_leaf(); } EST_Item *EST_Item::last_leaf() const { // Leafs are defined as those nodes with no daughters if (this == 0) return 0; else if (next()) return next()->last_leaf(); else if (down()) return down()->last_leaf(); else return (EST_Item *)(void *)this; } EST_Item *first_leaf_in_tree(const EST_Item *root) { return root->first_leaf(); } EST_Item *last_leaf_in_tree(const EST_Item *root) { if (root == 0) return 0; else if (root->down() == 0) return (EST_Item *)(void *)root; else return root->down()->last_leaf(); } EST_Item *EST_Item::append_daughter(EST_Item *si) { if (this == 0) return 0; EST_Item *nnode; EST_Item *its_downs; // Because we don't distinguish forests properly we need // to do nasty things if this si is already associated to a // this relation and its "in the top list" EST_Item *c = si->as_relation(relation_name()); if (in_list(c,p_relation->head())) { // need to save its daughters to put on the new node its_downs = c->d; c->d = 0; // otherwise it could delete its sub tree if (its_downs) its_downs->u = 0; if (down() == 0) nnode = insert_below(si); else nnode = down()->last()->insert_after(si); // put daughters back on the new item if (its_downs) { its_downs->u = nnode; nnode->d = its_downs; } delete c; // delete its old form from the top level } else if (down() == 0) nnode = insert_below(si); else nnode = down()->last()->insert_after(si); return nnode; } EST_Item *EST_Item::prepend_daughter(EST_Item *si) { if (this == 0) return 0; EST_Item *nnode; EST_Item *its_downs; // Because we don't distinguish forests properly we need // to do nasty things if this si is already associated to a // this relation and its "in the top list" EST_Item *c = si->as_relation(relation_name()); if (in_list(c,p_relation->head())) { // need to save its daughters to put on the new node its_downs = c->d; c->d = 0; // otherwise it could delete its sub tree if (its_downs) its_downs->u = 0; if (down() == 0) nnode = insert_below(si); else nnode = down()->insert_before(si); // put daughters back on the new item if (its_downs) { its_downs->u = nnode; nnode->d = its_downs; } delete c; // delete its old form from the top level } else if (down() == 0) nnode = insert_below(si); else nnode = down()->insert_before(si); return nnode; } EST_Item *EST_Item::grab_daughters() { EST_Item *dd = down(); if (dd) { dd->u = 0; d = 0; } return dd; } EST_Item_Content *EST_Item::grab_contents(void) { // Unreference contents, but don't delete them if that's the // last reference. It is the caller's responsibility to deal // with these contents, typically they are just about to be set // as contents of someone else so are only orphaned for a short // time EST_Item_Content *c = contents(); c->unref_relation(relation_name()); p_contents = 0; set_contents(0); // can't sit without contents return c; } void copy_node_tree(EST_Item *from, EST_Item *to) { // Copy this node and all its siblings and daughters if (from->next() != 0) copy_node_tree(from->next(),to->insert_after(from->next())); if (from->down() != 0) copy_node_tree(from->down(),to->insert_below(from->down())); } void copy_node_tree_contents(EST_Item *from, EST_Item *to) { // Copy this node and all its siblings and daughters // also copy the item's contents if (from->next() != 0) { EST_Item i = *from->next(); // copies the contents copy_node_tree_contents(from->next(),to->insert_after(&i)); } if (from->down() != 0) { EST_Item i = *from->down(); copy_node_tree_contents(from->down(),to->insert_below(&i)); } } int EST_Item::verify() const { // Return FALSE if this node and its neighbours aren't // properly linked if (this == 0) return TRUE; if (((d == 0) || (d->u == this)) && ((n == 0) || (n->p == this)) && (d->verify()) && (n->verify())) return TRUE; else return FALSE; } EST_Item *append_daughter(EST_Item *n, EST_Item *p) { return n->append_daughter(p); } EST_Item *append_daughter(EST_Item *n,const char *relname, EST_Item *p) { return append_daughter(as(n,relname),p); } EST_Item *prepend_daughter(EST_Item *n, EST_Item *p) { return n->prepend_daughter(p); } EST_Item *prepend_daughter(EST_Item *n, const char *relname, EST_Item *p) { return prepend_daughter(as(n,relname),p); } void remove_item(EST_Item *l, const char *relname) { EST_Item *lr = l->as_relation(relname); EST_Relation *r = lr->relation(); if ((lr != 0) && (r != 0)) r->remove_item(lr); } EST_Item &EST_Item::operator=(const EST_Item &s) { copy(s); return *this; } ostream& operator << (ostream &s, const EST_Item &a) { a.features().save(s); return s; } void evaluate(EST_Item *a,EST_Features &f) { EST_Features::RwEntries p; for(p.begin(f); p; ++p) if (p->v.type() == val_type_featfunc) { if (featfunc(p->v) != NULL) p->v = (featfunc(p->v))(a); else { fprintf(stderr, "NULL %s function\n", (const char *) p->k ); p->v = EST_Features::feature_default_value; } } } VAL_REGISTER_CLASS_NODEL(item,EST_Item)