/*______________________________________________________________________________ * * org.eidola.kernel.Element * * Part of the Eidola Kernel Reference Implementation * See http://eidola.org for oodles of relevant fun! * *______________________________________________________________________________ * * Copyright 1998, 2000-2001 Paul Cantrell * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License version 2, as published by the * Free Software Foundation. See the file LICENSE.html for more information. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY, including the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc. / 59 Temple * Place, Suite 330 / Boston, MA 02111-1307 / USA. *_______________________________________________________________________________ */ package org.eidola.kernel; import org.eidola.kernel.event.*; import org.eidola.kernel.event.EventListener; import org.eidola.kernel.error.*; import org.eidola.util.Util; import java.util.*; /** A piece of an Eidola program which is owned by another container. Elements can be as large as a Package, or as small as a numeric constant in an expression.

Elements sit in a strict hierarchy -- every Element has exactly one {@link #getOwner() owner}. The chain of owners always reaches up to a {@link Namespace}. All the elements within the namespace have unique {@link #getFullName() full name}s; see {@link NamedElement}.

Structure:

See note on synchronization and concurrent read safety in {@link Container}. @author Paul Cantrell @version [Development version] */ public abstract class Element extends Container { /** Part of an element. * @see org.eidola.kernel.event.StructureChanged */ static public final ContainerPart OWNER = new ContainerPart("owner"); /** Creates a new empty element. The new element won't be valid * until it has an owner. */ public Element() { ownerListener = new EventListener() { public void handleEvent(Event event) { handleOwnerEvent((ContainerEvent) event); } }; } //------------------------------------------------------ // Structural properties //------------------------------------------------------ /** Returns the namespace in which this element lives. */ public Namespace getNamespace() { return owner == null ? null : owner.getNamespace(); } /** Returns this element's owner. */ public Container getOwner() { return owner; } /** Sets this element's owner. This will broadcast a * {@link StructureChanged} notifying that the {@link #OWNER} * has changed. * @throws CyclicOwner If setting this owner would create a * cycle in the chain of owners. */ public void setOwner(Container newOwner) throws CyclicOwner { if(owner == newOwner) return; // Check for cycle in owners Set visitedElems = new HashSet(); visitedElems.add(this); for(Container contr = newOwner; contr instanceof Element; contr = ((Element) contr).getOwner()) { if(visitedElems.contains(contr)) throw new CyclicOwner(contr, this + " cannot have a cyclic chain of owners!"); visitedElems.add(contr); } // Change owner Container oldOwner = owner; Namespace oldNamespace = getNamespace(); owner = newOwner; Namespace newNamespace = getNamespace(); if(oldOwner != null) oldOwner.removeListener(ownerListener); if(newOwner != null) newOwner.addListener( ownerListener, Engine.getInstance().getPropagatorQ()); StructureChanged structEvent = new StructureChanged(this, OWNER); broadcastEvent(structEvent); if(oldNamespace != newNamespace) broadcastEvent(new NamespaceChanged(this, structEvent)); //if(oldScope != newScope) // broadcastEvent(new ScopeChangedEvent(this, structEvent)); } /** Returns the list including this container, its owner, its owner's owner, * etc, up to and including the container's namespace. * The list goes from the topmost container down. * Although this is really a derived structure, it is not a member * of Compilation and is not calculated lazily, because this would * often lead to the propogation of unreasonable numbers of events. */ public List/**/ getIndirectOwners() { LinkedList indirectOwners = new LinkedList(); Container contr = this; while(true) { indirectOwners.addFirst(contr); if(!(contr instanceof Element)) break; contr = ((Element) contr).getOwner(); } return indirectOwners; } /** Returns a list of the names of the chain of containers. * For unnamed elements in the chain, the element itself appears. * Although this is really a derived structure, it is not a member * of Compilation and is not calculated lazily, because this would * often lead to the propogation of unreasonable numbers of events. * @see NamedElement#getName * @see Element#toString */ public List/**/ getFullName() { LinkedList fullname = new LinkedList(); for(Container contr = this; contr instanceof Element; contr = ((Element) contr).getOwner()) { fullname.addFirst( (contr instanceof NamedElement) ? (Object) ((NamedElement) contr).getName() : (Object) contr); } return fullname; } //------------------------------------------------------ // Changes and events //------------------------------------------------------ /** Propagates events from the owner. * By default, this handler broadcasts a {@link CompileRequired} event * when it receives a {@link StructureChanged} event from the owner. */ protected void handleOwnerEvent(ContainerEvent event) { // If owner's structure has changed, we may no longer be a member, // which might create errors, so we need to recompile. if(event instanceof CompileCompleted) { ContainerEvent prop = event.getPropagatedFrom(); if(prop instanceof StructureChanged) synchronized(this) { broadcastEvent(new CompileRequired(this, (CompileRequired) prop)); } } } public abstract class Compilation extends Container.Compilation { /** Creates an empty compilation. */ public Compilation() { elementsUsed = Collections.EMPTY_SET; } /** Returns the set of elements which this element uses. This includes, * at the very least, the element itself, its owner, and its contents. */ public Set/**/ getElementsUsed() { return elementsUsed; } /** Checks that this element has an owner and is among its owner's contents. * This also performs the checks in {@link Container.Compilation#checkLazy()}. */ protected void checkLazy() { super.checkLazy(); // Check that our owner is not null and agrees that it contains us! if(owner == null) addError( new UninitializedPart(Element.this, Element.this + " needs an owner", OWNER)); else if(!owner.getCompilation().getContents().contains(Element.this)) addError( new IllegalOwner(Element.this, Element.this + " claims " + owner + " as its owner, but it's quite mistaken!")); } protected Set/**/ elementsUsed; } //------------------------------------------------------ // Debugging //------------------------------------------------------ /** Returns the full name of the element as a nicely formatted string. */ public String toString() { StringBuffer str = null; for(Container contr = this; contr instanceof Element; contr = ((Element) contr).getOwner()) { if(str == null) str = new StringBuffer(); else str.insert(0, '.'); if(contr instanceof NamedElement) { String name = ((NamedElement) contr).getName(); str.insert(0, name == null ? "" : name); } } return str.toString(); } //------------------------------------------------------ // Private //------------------------------------------------------ private Container owner; private EventListener ownerListener; }