/*********************************************************************** * Copyright (C) 1995 Joe English * Freely redistributable *********************************************************************** * * relation.c,v 1.11 1999/07/18 06:40:06 joe Exp * * Author: Joe English * Created: 25 Oct 1995 * Description: Implementation of ILINKs and RELATIONS. * * 1999/07/18 06:40:06 * 1.11 */ #include #include #include "project.h" #include "strmgt.h" #include "esis.h" #include "strmap.h" #include "pile.h" #include "esisp.h" /* Organization: * * RELATION nodes appear in the attribute list of the document root. * name: relation name * children: ILINK nodes * next,prev: next/previous attribute/relation/entity/etc. * link: points to last ILINK node. * * ILINK node structure: * name: relation name * parent: parent relation * children: LINKEND nodes * next,prev: next/previous ilink in relation * reference: origin of link * * LINKEND node structure: * name: anchor name * parent: containing ILINK node * reference: anchor node * next,prev: next/previous anchor in link * * All the LINKEND nodes which reference a particular tree node * are stored in a circular linked list, chained by the 'link' * field. The 'link' field of the tree node points to the tail of * this list. */ /* * Constructors: */ static ESISNode find_relation(ESISNode node, ESISToken relname) { ESISNode nd = esis_docroot(node); for (nd = nd->attributes; nd; nd = nd->next) if (nd->type == EN_RELATION && nd->name == relname) return nd; return 0; } static ESISNode find_linkend(ESISNode ilink, ESISToken anchname) { ESISNode linkend = ilink->children; while (linkend) { if (linkend->name == anchname) break; linkend = linkend->next; } return linkend; } int esis_define_relation( ESISDocument doc, const char *relname, int nanchors, char **anchnames) { ESISNode rel; ESISToken name = ucintern(relname); if (find_relation(doc->rootnode, name)) return 0; rel = esis_create_node(EN_RELATION, name, doc->rootnode, NULL, 1); /* %%% do something about anchor names ... */ return 1; } ESISNode esis_create_ilink(ESISDocument doc, ESISToken relname, ESISNode origin) { ESISNode rel, ilink; rel = find_relation(doc->rootnode,relname); if (!rel) return 0; rel->link = ilink = esis_create_node( EN_ILINK, relname, rel, rel->link, 0); ilink->reference = origin; return ilink; } int esis_set_linkend(ESISNode ilink, ESISToken anchname, ESISNode anchor) { ESISNode linkend = find_linkend(ilink, anchname); if (!linkend) { linkend = esis_create_node( EN_LINKEND, anchname, ilink, NULL, 0); } if (linkend->reference) return 0; linkend->reference = anchor; /* Add to circular linked list: */ if (anchor->link) { linkend->link = anchor->link->link; anchor->link->link = linkend; anchor->link = linkend; } else { anchor->link = linkend->link = linkend; } return 1; } ESISNode esis_relation_first(ESISNode node, ESISToken relname) { ESISNode rel = find_relation(node, relname); return rel ? rel->children : 0; } ESISNode esis_relation_next(ESISNode ilink) { if (ilink->type != EN_ILINK) return 0; return ilink->next; } ESISNode esis_first_ilink( ESISNode origin, ESISToken relname, ESISToken anchname) { ESISNode tail = origin->link; ESISNode linkend; ASSERT(relname == ucintern(relname), "Uninterned relname"); ASSERT(anchname == ucintern(anchname), "Uninterned anchname"); if (!tail) return 0; linkend = tail->link; /* head of cll */ while (linkend) { ASSERT(linkend->type == EN_LINKEND && linkend->parent->type == EN_ILINK, "Botched ilink structure"); if (linkend->name == anchname && linkend->parent->name == relname) return linkend->parent; linkend = (linkend == tail) ? 0 : linkend->link; } return 0; } ESISNode esis_next_ilink( ESISNode origin, ESISToken relname, ESISToken anchname, ESISNode ilink) { ESISNode linkend; ASSERT(ilink->type == EN_ILINK, "Not an ILINK node"); linkend = find_linkend(ilink, anchname); while (linkend != origin->link) { linkend = linkend->link; ASSERT(linkend->type == EN_LINKEND && linkend->parent->type == EN_ILINK, "Botched ilink structure"); if (linkend->name == anchname && linkend->parent->name == relname) return linkend->parent; } return 0; } ESISNode esis_ilink_anchor(ESISNode ilink, ESISToken anchname) { ESISNode linkend; if (ilink->type != EN_ILINK) return 0; linkend = find_linkend(ilink, anchname); return linkend ? linkend->reference : 0; } ESISNode esis_ilink_origin(ESISNode ilink) { if (ilink->type == EN_ILINK) return ilink->reference; return NULL; }